refactor: rename cli-v2 → cli, archive legacy cli, plus broker-side grants + auto-migrate
- apps/cli/ is now the canonical CLI (was apps/cli-v2/). - apps/cli/ legacy v0 archived as branch 'legacy-cli-archive' and tag 'cli-v0-legacy-final' before deletion; git history preserves it too. - .github/workflows/release-cli.yml paths updated. - pnpm-lock.yaml regenerated. Broker-side peer-grant enforcement (spec: 2026-04-15-per-peer-capabilities): - 0020_peer-grants.sql adds peer_grants jsonb + GIN index on mesh.member. - handleSend in broker fetches recipient grant maps once per send, drops messages silently when sender lacks the required capability. - POST /cli/mesh/:slug/grants to update from CLI; broker_messages_dropped_by_grant_total metric. - CLI grant/revoke/block now mirror to broker via syncToBroker. Auto-migrate on broker startup: - apps/broker/src/migrate.ts runs drizzle migrate with pg_advisory_lock before the HTTP server binds. Exits non-zero on failure so Coolify healthcheck fails closed. - Dockerfile copies packages/db/migrations into /app/migrations. - postgres 3.4.5 added as direct broker dep. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
99
apps/cli/src/commands/upgrade.ts
Normal file
99
apps/cli/src/commands/upgrade.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* `claudemesh upgrade` — self-update the CLI to the latest alpha.
|
||||
*
|
||||
* Strategy:
|
||||
* 1. Query npm for the latest @alpha dist-tag.
|
||||
* 2. If we're behind, run `npm i -g claudemesh-cli@alpha` via the same
|
||||
* npm that installed us (detected from argv[1] path walk).
|
||||
* 3. Print before/after versions.
|
||||
*
|
||||
* For users who got the CLI via the `/install` shell flow (portable Node
|
||||
* in ~/.claudemesh), we call that npm directly so nothing else on the
|
||||
* system is touched.
|
||||
*/
|
||||
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { URLS, VERSION } from "~/constants/urls.js";
|
||||
import { render } from "~/ui/render.js";
|
||||
import { EXIT } from "~/constants/exit-codes.js";
|
||||
|
||||
async function latestAlpha(): Promise<string | null> {
|
||||
try {
|
||||
const res = await fetch(URLS.NPM_REGISTRY, { signal: AbortSignal.timeout(8000) });
|
||||
if (!res.ok) return null;
|
||||
const body = (await res.json()) as { "dist-tags"?: { alpha?: string; latest?: string } };
|
||||
return body["dist-tags"]?.alpha ?? body["dist-tags"]?.latest ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function findNpm(): { npm: string; prefix?: string } {
|
||||
// Portable install path (`/install.sh` puts npm in ~/.claudemesh/node/bin/npm)
|
||||
const portable = join(process.env.HOME ?? "", ".claudemesh", "node", "bin", "npm");
|
||||
if (existsSync(portable)) {
|
||||
return { npm: portable, prefix: join(process.env.HOME ?? "", ".claudemesh") };
|
||||
}
|
||||
// argv[1] → .../node_modules/claudemesh-cli/dist/entrypoints/cli.js
|
||||
// walk up to find a sibling npm binary.
|
||||
let cur = resolve(process.argv[1] ?? ".");
|
||||
for (let i = 0; i < 6; i++) {
|
||||
cur = dirname(cur);
|
||||
const candidate = join(cur, "bin", "npm");
|
||||
if (existsSync(candidate)) return { npm: candidate };
|
||||
}
|
||||
// Fallback to PATH.
|
||||
return { npm: "npm" };
|
||||
}
|
||||
|
||||
export async function runUpgrade(opts: { check?: boolean; yes?: boolean } = {}): Promise<number> {
|
||||
render.section("claudemesh upgrade");
|
||||
render.kv([
|
||||
["installed", VERSION],
|
||||
["checking", "npm registry…"],
|
||||
]);
|
||||
|
||||
const latest = await latestAlpha();
|
||||
if (!latest) {
|
||||
render.warn("Could not reach npm registry — skipped.");
|
||||
return EXIT.SUCCESS;
|
||||
}
|
||||
|
||||
render.kv([["latest", latest]]);
|
||||
|
||||
if (latest === VERSION) {
|
||||
render.blank();
|
||||
render.ok(`Already on latest (${latest}).`);
|
||||
return EXIT.SUCCESS;
|
||||
}
|
||||
|
||||
if (opts.check) {
|
||||
render.blank();
|
||||
render.warn(`Update available: ${VERSION} → ${latest}`);
|
||||
render.hint("Run: claudemesh upgrade");
|
||||
return EXIT.SUCCESS;
|
||||
}
|
||||
|
||||
const { npm, prefix } = findNpm();
|
||||
const args = ["install", "-g"];
|
||||
if (prefix) args.push("--prefix", prefix);
|
||||
args.push("claudemesh-cli@alpha");
|
||||
|
||||
render.blank();
|
||||
render.info(`Updating ${VERSION} → ${latest}…`);
|
||||
render.hint(`${npm} ${args.join(" ")}`);
|
||||
render.blank();
|
||||
|
||||
const res = spawnSync(npm, args, { stdio: "inherit" });
|
||||
if (res.status !== 0) {
|
||||
render.err(`npm exited with status ${res.status}`);
|
||||
render.hint("Try: npm i -g claudemesh-cli@alpha");
|
||||
return EXIT.INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
render.blank();
|
||||
render.ok(`Upgraded to ${latest}.`);
|
||||
return EXIT.SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user