@divergeai/cli 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@ model gateway, and call platform tools as yourself.
5
5
 
6
6
  ```bash
7
7
  npm install -g @divergeai/cli
8
- diverge login # opens the browser; choose your organization
8
+ diverge login # browser sign-in; the organization is chosen afterwards in the CLI
9
9
  diverge agents setup claude # Claude Code on the gateway
10
10
  diverge agents run dsh -- --profile web --port 3131 --no-open
11
11
  diverge tools list --organization-id <id>
@@ -16,8 +16,11 @@ Node 22 or later. Linux and macOS are supported; Windows is best-effort.
16
16
  ## Sign in
17
17
 
18
18
  `diverge login` runs the browser sign-in (authorization code with PKCE on a
19
- loopback listener). A login serves exactly one organization: without
20
- `--organization-id` the platform asks you to choose. The login is cached in
19
+ loopback listener). The browser only authenticates you; the organization is
20
+ chosen afterwards in the CLI, never through the browser: `--organization-id
21
+ <id|name>` if given, else the last login's, else your only membership, else a
22
+ menu. The login is bound to that organization on the platform, and `diverge
23
+ org use <id|name>` switches without a browser. The login is cached in
21
24
  `~/.diverge/token.json` (mode 0600), refreshes itself, and `diverge logout`
22
25
  revokes it on the platform before forgetting it.
23
26
 
package/dist/diverge.mjs CHANGED
@@ -7387,7 +7387,7 @@ import path10 from "node:path";
7387
7387
  import { parseArgs } from "node:util";
7388
7388
 
7389
7389
  // src/version.ts
7390
- var VERSION = true ? "0.1.2" : "dev";
7390
+ var VERSION = true ? "0.1.4" : "dev";
7391
7391
  var USER_AGENT = `diverge/${VERSION}`;
7392
7392
 
7393
7393
  // src/http.ts
@@ -7436,11 +7436,23 @@ async function fetchJson(url, init = {}) {
7436
7436
 
7437
7437
  // src/auth/browser.ts
7438
7438
  import { spawn } from "node:child_process";
7439
- function openBrowser(url, env = process.env, platform = process.platform) {
7439
+ function openerCommand(url, env = process.env, platform = process.platform) {
7440
7440
  const custom = env.BROWSER?.trim();
7441
- const [command, args] = custom ? [custom, [url]] : platform === "darwin" ? ["open", [url]] : platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
7441
+ if (custom) return { command: custom, args: [url] };
7442
+ if (platform === "darwin") return { command: "open", args: [url] };
7443
+ if (platform === "win32") {
7444
+ return { command: "cmd", args: ["/c", "start", '""', `"${url.replace(/&/g, "^&")}"`], windowsVerbatimArguments: true };
7445
+ }
7446
+ return { command: "xdg-open", args: [url] };
7447
+ }
7448
+ function openBrowser(url, env = process.env, platform = process.platform) {
7449
+ const opener = openerCommand(url, env, platform);
7442
7450
  try {
7443
- const child = spawn(command, args, { detached: true, stdio: "ignore" });
7451
+ const child = spawn(opener.command, opener.args, {
7452
+ detached: true,
7453
+ stdio: "ignore",
7454
+ ...opener.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
7455
+ });
7444
7456
  child.on("error", () => {
7445
7457
  });
7446
7458
  child.unref();
@@ -7686,15 +7698,25 @@ async function login(options, deps = defaultDeps) {
7686
7698
  await loopback.close();
7687
7699
  }
7688
7700
  }
7689
- async function refreshUserToken(baseUrl, clientId2, token, nowMs = Date.now()) {
7701
+ async function refreshUserToken(baseUrl, clientId2, token, nowMs = Date.now(), organizationId) {
7690
7702
  if (!token.refresh_token) throw new CliError("Cached login expired. Run: diverge login");
7691
7703
  const payload = await postToken(baseUrl, {
7692
7704
  grant_type: "refresh_token",
7693
7705
  refresh_token: token.refresh_token,
7694
- client_id: clientId2
7706
+ client_id: clientId2,
7707
+ ...organizationId ? { organization_id: organizationId } : {}
7695
7708
  });
7696
7709
  return cachedFrom(payload, token.base_url, token.refresh_token, nowMs);
7697
7710
  }
7711
+ async function bindOrganization(source, organizationId, nowMs = Date.now()) {
7712
+ const token = loadUserToken(source.tokenFile);
7713
+ if (!token || token.base_url !== source.baseUrl.replace(/\/+$/, "")) {
7714
+ throw new CliError("No cached login for this deployment. Run: diverge login");
7715
+ }
7716
+ const bound = await refreshUserToken(source.baseUrl, source.clientId, token, nowMs, organizationId);
7717
+ saveUserToken(source.tokenFile, bound);
7718
+ return bound;
7719
+ }
7698
7720
  async function getUserToken(source, freshForSeconds = 30, nowMs = Date.now()) {
7699
7721
  const token = loadUserToken(source.tokenFile);
7700
7722
  if (!token || token.base_url !== source.baseUrl.replace(/\/+$/, "")) {
@@ -8344,6 +8366,38 @@ ${agentsUsage}`);
8344
8366
  // src/commands/login.ts
8345
8367
  import { parseArgs as parseArgs2 } from "node:util";
8346
8368
 
8369
+ // src/auth/organizations.ts
8370
+ function chooseOrganization(input) {
8371
+ const memberships = input.memberships;
8372
+ const flag = input.flag?.trim();
8373
+ if (flag) {
8374
+ const match = memberships.find((organization) => organization.id === flag || organization.name.toLowerCase() === flag.toLowerCase());
8375
+ if (match) return { kind: "chosen", organizationId: match.id, name: match.name, reason: "flag" };
8376
+ if (input.platformActor) return { kind: "chosen", organizationId: flag, reason: "flag" };
8377
+ throw new CliError(`Not one of your organizations: ${flag}. Yours: ${memberships.map((organization) => `${organization.name} (${organization.id})`).join(", ") || "none"}`);
8378
+ }
8379
+ const remembered = input.remembered?.trim();
8380
+ if (remembered) {
8381
+ const match = memberships.find((organization) => organization.id === remembered);
8382
+ if (match) return { kind: "chosen", organizationId: match.id, name: match.name, reason: "remembered" };
8383
+ if (input.platformActor) return { kind: "chosen", organizationId: remembered, reason: "remembered" };
8384
+ }
8385
+ if (memberships.length === 1) return { kind: "chosen", organizationId: memberships[0].id, name: memberships[0].name, reason: "only" };
8386
+ if (memberships.length === 0) return { kind: "none" };
8387
+ return { kind: "ask", options: memberships };
8388
+ }
8389
+ function organizationMenu(options) {
8390
+ return options.map((organization, index) => ` ${index + 1}. ${organization.name} (${organization.id})`);
8391
+ }
8392
+ function pickFromMenu(answer, options) {
8393
+ const trimmed = answer.trim();
8394
+ const index = Number(trimmed);
8395
+ if (Number.isInteger(index) && index >= 1 && index <= options.length) return options[index - 1];
8396
+ const match = options.find((organization) => organization.id === trimmed || organization.name.toLowerCase() === trimmed.toLowerCase());
8397
+ if (!match) throw new CliError(`Not one of the listed organizations: ${trimmed}`);
8398
+ return match;
8399
+ }
8400
+
8347
8401
  // src/auth/userinfo.ts
8348
8402
  async function fetchUserInfo(baseUrl, accessToken) {
8349
8403
  return fetchJson(userinfoEndpoint(baseUrl), { headers: { Authorization: `Bearer ${accessToken}` }, timeoutMs: 15e3 });
@@ -8357,12 +8411,25 @@ async function describeAccount(baseUrl, accessToken, fallbackSub, options = {})
8357
8411
  }
8358
8412
  }
8359
8413
 
8414
+ // src/prompt.ts
8415
+ import readline from "node:readline";
8416
+ function ask(question) {
8417
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
8418
+ return new Promise((resolve) => rl.question(question, (answer) => {
8419
+ rl.close();
8420
+ resolve(answer);
8421
+ }));
8422
+ }
8423
+
8360
8424
  // src/commands/login.ts
8361
8425
  var DEFAULT_SCOPES = "mcp:read mcp:execute inference:execute";
8362
- var loginUsage = `diverge login [--organization-id <id>] [--scopes "<scopes>"] [--as <email>] [--no-browser] [--api-url <url>]
8426
+ var loginUsage = `diverge login [--organization-id <id|name>] [--scopes "<scopes>"] [--as <email>] [--no-browser] [--api-url <url>]
8363
8427
 
8364
- Sign in through the browser. Without --organization-id the platform asks you to
8365
- choose one; a login serves exactly one organization. --api-url is remembered.
8428
+ Sign in through the browser, then choose where to act. The browser only
8429
+ authenticates the person; the organization is chosen here afterwards \u2014 the
8430
+ one named by --organization-id (id or name), else the last login's, else the
8431
+ only membership, else a menu \u2014 and the login is bound to it on the platform.
8432
+ Switch later with: diverge org use <id|name>.
8366
8433
  --as <email> names the account: the platform signs that account in instead of
8367
8434
  reusing whatever session the browser holds, and issues nothing to any other.
8368
8435
  Without --as, the account of the last login is used; switching is explicit.
@@ -8404,29 +8471,90 @@ async function runLogin(args, io) {
8404
8471
  baseUrl: apiUrl,
8405
8472
  clientId: clientId(io.env),
8406
8473
  scopes,
8407
- organizationId: values["organization-id"],
8408
- selectOrganization: !values["organization-id"],
8409
8474
  openBrowser: !values["no-browser"],
8410
8475
  tokenFile: tokenFile(io.env),
8411
8476
  loginHint,
8412
8477
  print: io.stdout
8413
8478
  });
8414
8479
  const claims = tokenClaims(token.access_token);
8415
- const remaining = Math.max(0, Math.round(token.expires_at - Date.now() / 1e3));
8416
- io.stdout(`Logged in to ${apiUrl} for organization ${claims.organizationId ?? "none"}; token cached (${remaining}s to expiry, auto-refreshes).`);
8417
- let account;
8480
+ let info;
8418
8481
  try {
8419
- account = (await fetchUserInfo(apiUrl, token.access_token)).email ?? void 0;
8482
+ info = await fetchUserInfo(apiUrl, token.access_token);
8420
8483
  } catch (error) {
8421
- io.stderr(`Signed in as: ${claims.sub ?? "unknown user"} (email unavailable: ${error instanceof Error ? error.message : String(error)})`);
8484
+ throw new CliError(`Signed in, but the platform could not say who or where (${error instanceof Error ? error.message : String(error)}); run: diverge login`);
8422
8485
  }
8423
- if (account) io.stdout(`Signed in as: ${account}`);
8424
- writeConfig({ ...readConfig(io.env), ...account ? { account } : {}, scopes }, io.env);
8486
+ io.stdout(`Signed in as: ${info.email}`);
8487
+ const bound = await bindChosenOrganization(io, apiUrl, values["organization-id"], config.organization, info);
8488
+ const remaining = Math.max(0, Math.round(bound.token.expires_at - Date.now() / 1e3));
8489
+ io.stdout(`Logged in to ${apiUrl}; token cached (${remaining}s to expiry, auto-refreshes).`);
8490
+ writeConfig({ ...readConfig(io.env), account: info.email, scopes, organization: bound.organizationId }, io.env);
8425
8491
  const granted = new Set(claims.scopes ?? []);
8426
8492
  const missing = scopes.split(" ").filter((scope) => scope && !granted.has(scope));
8427
8493
  if (missing.length) io.stderr(`Not granted at this organization (your roles lack them): ${missing.join(" ")}`);
8428
8494
  return 0;
8429
8495
  }
8496
+ async function bindChosenOrganization(io, apiUrl, flag, remembered, info) {
8497
+ const memberships = info.organizations ?? [];
8498
+ let choice = chooseOrganization({ flag, remembered, memberships, platformActor: info.platformActor });
8499
+ if (choice.kind === "none") {
8500
+ throw new CliError("You are not a member of any organization; ask an administrator to add you, then sign in again.");
8501
+ }
8502
+ if (choice.kind === "ask") {
8503
+ if (!io.interactive) {
8504
+ throw new CliError(`You belong to several organizations; pass --organization-id <id|name>:
8505
+ ${organizationMenu(choice.options).join("\n")}`);
8506
+ }
8507
+ io.stdout("Choose the organization to act in:");
8508
+ for (const line of organizationMenu(choice.options)) io.stdout(line);
8509
+ const picked = pickFromMenu(await ask("Organization (number, id or name): "), choice.options);
8510
+ choice = { kind: "chosen", organizationId: picked.id, name: picked.name, reason: "flag" };
8511
+ }
8512
+ const source = { tokenFile: tokenFile(io.env), baseUrl: apiUrl, clientId: clientId(io.env) };
8513
+ const token = await bindOrganization(source, choice.organizationId);
8514
+ const label = choice.name ? `${choice.name} (${choice.organizationId})` : choice.organizationId;
8515
+ const why = choice.reason === "remembered" ? " (the last login; pass --organization-id to switch)" : choice.reason === "only" ? " (your only organization)" : "";
8516
+ io.stdout(`Organization: ${label}${why}`);
8517
+ return { organizationId: choice.organizationId, token };
8518
+ }
8519
+
8520
+ // src/commands/org.ts
8521
+ import { parseArgs as parseArgs3 } from "node:util";
8522
+ var orgUsage = `diverge org [use <id|name>]
8523
+
8524
+ Without a verb: the organizations you may act in, the bound one marked.
8525
+ use <id|name>: bind the login to that organization on the platform, without a
8526
+ browser; every later command acts there. A platform actor may name an
8527
+ organization they are not a member of.`;
8528
+ async function runOrg(args, io) {
8529
+ const { positionals } = parseArgs3({ args, options: {}, strict: true, allowPositionals: true });
8530
+ const apiUrl = resolveApiUrl(void 0, io.env);
8531
+ const source = { tokenFile: tokenFile(io.env), baseUrl: apiUrl, clientId: clientId(io.env) };
8532
+ const token = await getUserToken(source);
8533
+ const info = await fetchUserInfo(apiUrl, token.access_token);
8534
+ const memberships = info.organizations ?? [];
8535
+ const current = tokenClaims(token.access_token).organizationId ?? void 0;
8536
+ const verb = positionals[0];
8537
+ if (!verb) {
8538
+ if (memberships.length === 0 && !current) {
8539
+ io.stdout("You are not a member of any organization.");
8540
+ return 0;
8541
+ }
8542
+ for (const organization of memberships) io.stdout(`${organization.id === current ? "*" : " "} ${organization.name} (${organization.id})`);
8543
+ if (current && !memberships.some((organization) => organization.id === current)) io.stdout(`* ${current} (not a membership; acting as the platform)`);
8544
+ return 0;
8545
+ }
8546
+ if (verb !== "use") throw new CliError(`Unknown verb: ${verb}
8547
+
8548
+ ${orgUsage}`);
8549
+ const target = positionals[1];
8550
+ if (!target) throw new CliError("org use needs an organization id or name");
8551
+ const choice = chooseOrganization({ flag: target, memberships, platformActor: info.platformActor });
8552
+ if (choice.kind !== "chosen") throw new CliError("org use needs an organization id or name");
8553
+ const bound = await bindOrganization(source, choice.organizationId);
8554
+ writeConfig({ ...readConfig(io.env), organization: choice.organizationId }, io.env);
8555
+ io.stdout(`Organization: ${choice.name ? `${choice.name} (${choice.organizationId})` : choice.organizationId}; scopes ${(tokenClaims(bound.access_token).scopes ?? []).join(" ") || "none"}`);
8556
+ return 0;
8557
+ }
8430
8558
 
8431
8559
  // src/commands/logout.ts
8432
8560
  var logoutUsage = "diverge logout\n\nRevoke the login's session on the platform and forget it locally.";
@@ -8524,8 +8652,10 @@ async function runStatus(_args, io) {
8524
8652
  const remaining = Math.round(token.expires_at - Date.now() / 1e3);
8525
8653
  const mismatch = token.base_url !== apiUrl ? ` (for ${token.base_url}, not the configured deployment)` : "";
8526
8654
  io.stdout(`Login: ${isFresh(token, 0) ? `valid for ${remaining}s` : "expired (auto-refreshes)"}; refresh token ${token.refresh_token ? "present" : "absent"}${mismatch}`);
8527
- io.stdout(`Signed in as: ${await signedInAs(token, io)}`);
8528
- io.stdout(`Organization: ${claims.organizationId ?? "none (sign in again to choose one)"}`);
8655
+ const account = await signedInAs(token, io);
8656
+ io.stdout(`Signed in as: ${account.line}`);
8657
+ const organization = account.organizations?.find((candidate) => candidate.id === claims.organizationId);
8658
+ io.stdout(`Organization: ${claims.organizationId ? `${organization ? `${organization.name} (${claims.organizationId})` : claims.organizationId}` : "none (run: diverge org use <id|name>)"}`);
8529
8659
  io.stdout(`Scopes: ${(claims.scopes ?? []).join(" ") || "none"}`);
8530
8660
  io.stdout(describeToolExecution(io.env));
8531
8661
  return 0;
@@ -8536,9 +8666,14 @@ async function signedInAs(cached, io) {
8536
8666
  try {
8537
8667
  accessToken = (await getUserToken({ tokenFile: tokenFile(io.env), baseUrl: cached.base_url, clientId: clientId(io.env) })).access_token;
8538
8668
  } catch (error) {
8539
- return `${sub ?? "unknown user"} (email unavailable: ${error instanceof Error ? error.message : String(error)})`;
8669
+ return { line: `${sub ?? "unknown user"} (email unavailable: ${error instanceof Error ? error.message : String(error)})` };
8670
+ }
8671
+ try {
8672
+ const info = await fetchUserInfo(cached.base_url, accessToken);
8673
+ return { line: `${info.email} (${info.sub})`, organizations: info.organizations };
8674
+ } catch (error) {
8675
+ return { line: `${sub ?? "unknown user"} (email unavailable: ${error instanceof Error ? error.message : String(error)})` };
8540
8676
  }
8541
- return describeAccount(cached.base_url, accessToken, sub, { withSub: true });
8542
8677
  }
8543
8678
 
8544
8679
  // src/commands/token.ts
@@ -8551,10 +8686,10 @@ async function runToken(_args, io) {
8551
8686
  }
8552
8687
 
8553
8688
  // src/commands/tools.ts
8554
- import { parseArgs as parseArgs3 } from "node:util";
8689
+ import { parseArgs as parseArgs4 } from "node:util";
8555
8690
 
8556
8691
  // src/mcp/bridge.ts
8557
- import readline from "node:readline";
8692
+ import readline2 from "node:readline";
8558
8693
 
8559
8694
  // src/tools/client.ts
8560
8695
  var PENDING_INVOCATION_STATUSES = ["pending", "approved", "executing"];
@@ -8831,7 +8966,7 @@ var BridgeServer = class {
8831
8966
  }
8832
8967
  };
8833
8968
  async function serveBridge(server, input, output) {
8834
- const lines = readline.createInterface({ input, crlfDelay: Infinity });
8969
+ const lines = readline2.createInterface({ input, crlfDelay: Infinity });
8835
8970
  for await (const raw of lines) {
8836
8971
  const line = raw.trim();
8837
8972
  if (!line) continue;
@@ -8998,7 +9133,7 @@ async function runTools(args, io) {
8998
9133
  }
8999
9134
  switch (verb) {
9000
9135
  case "list": {
9001
- const { values } = parseArgs3({ args: rest, options: { ...SCOPE_OPTIONS, filter: { type: "string" }, capability: { type: "string" }, json: { type: "boolean", default: false } }, strict: true });
9136
+ const { values } = parseArgs4({ args: rest, options: { ...SCOPE_OPTIONS, filter: { type: "string" }, capability: { type: "string" }, json: { type: "boolean", default: false } }, strict: true });
9002
9137
  const { client: tools } = await client(values, io);
9003
9138
  let list = await tools.listTools();
9004
9139
  if (values.filter) list = list.filter((tool) => tool.name.includes(values.filter));
@@ -9008,7 +9143,7 @@ async function runTools(args, io) {
9008
9143
  return ExitCode.Ok;
9009
9144
  }
9010
9145
  case "capabilities": {
9011
- const { values } = parseArgs3({ args: rest, options: { ...SCOPE_OPTIONS, json: { type: "boolean", default: false } }, strict: true });
9146
+ const { values } = parseArgs4({ args: rest, options: { ...SCOPE_OPTIONS, json: { type: "boolean", default: false } }, strict: true });
9012
9147
  const { client: tools } = await client(values, io);
9013
9148
  const list = await tools.listTools();
9014
9149
  if (values.json) io.stdout(JSON.stringify(capabilityClusters(list), null, 2));
@@ -9016,7 +9151,7 @@ async function runTools(args, io) {
9016
9151
  return ExitCode.Ok;
9017
9152
  }
9018
9153
  case "describe": {
9019
- const { values, positionals } = parseArgs3({ args: rest, options: { ...SCOPE_OPTIONS, json: { type: "boolean", default: false } }, strict: true, allowPositionals: true });
9154
+ const { values, positionals } = parseArgs4({ args: rest, options: { ...SCOPE_OPTIONS, json: { type: "boolean", default: false } }, strict: true, allowPositionals: true });
9020
9155
  const name = positionals[0];
9021
9156
  if (!name) throw new CliError("describe needs a tool name");
9022
9157
  const { settings, client: tools } = await client(values, io);
@@ -9031,7 +9166,7 @@ async function runTools(args, io) {
9031
9166
  return ExitCode.Ok;
9032
9167
  }
9033
9168
  case "call": {
9034
- const { values, positionals } = parseArgs3({ args: rest, options: { ...SCOPE_OPTIONS, "args-json": { type: "string", default: "{}" }, wait: { type: "boolean", default: false }, timeout: { type: "string", default: "600" } }, strict: true, allowPositionals: true });
9169
+ const { values, positionals } = parseArgs4({ args: rest, options: { ...SCOPE_OPTIONS, "args-json": { type: "string", default: "{}" }, wait: { type: "boolean", default: false }, timeout: { type: "string", default: "600" } }, strict: true, allowPositionals: true });
9035
9170
  const name = positionals[0];
9036
9171
  if (!name) throw new CliError("call needs a tool name");
9037
9172
  let parsedArgs;
@@ -9060,7 +9195,7 @@ async function runTools(args, io) {
9060
9195
  return emit(io, settledOutcome(invocation));
9061
9196
  }
9062
9197
  case "invocation": {
9063
- const { values, positionals } = parseArgs3({ args: rest, options: { ...SCOPE_OPTIONS, wait: { type: "boolean", default: false }, timeout: { type: "string", default: "600" }, json: { type: "boolean", default: false } }, strict: true, allowPositionals: true });
9198
+ const { values, positionals } = parseArgs4({ args: rest, options: { ...SCOPE_OPTIONS, wait: { type: "boolean", default: false }, timeout: { type: "string", default: "600" }, json: { type: "boolean", default: false } }, strict: true, allowPositionals: true });
9064
9199
  const id = positionals[0];
9065
9200
  if (!id) throw new CliError("invocation needs an id");
9066
9201
  const { client: tools } = await client(values, io);
@@ -9078,7 +9213,7 @@ async function runTools(args, io) {
9078
9213
  return emit(io, settledOutcome(invocation));
9079
9214
  }
9080
9215
  case "bridge": {
9081
- const { values } = parseArgs3({ args: rest, options: { ...SCOPE_OPTIONS, tools: { type: "string" } }, strict: true });
9216
+ const { values } = parseArgs4({ args: rest, options: { ...SCOPE_OPTIONS, tools: { type: "string" } }, strict: true });
9082
9217
  const { client: tools } = await client(values, io);
9083
9218
  const allow = (values.tools ?? "").split(",").map((name) => name.trim()).filter(Boolean);
9084
9219
  return serveBridge(new BridgeServer(tools, { tools: allow }), process.stdin, process.stdout);
@@ -9092,7 +9227,7 @@ ${toolsUsage}`);
9092
9227
 
9093
9228
  // src/commands/plugins.ts
9094
9229
  import fs7 from "node:fs";
9095
- import { parseArgs as parseArgs4 } from "node:util";
9230
+ import { parseArgs as parseArgs5 } from "node:util";
9096
9231
  var pluginsUsage = `diverge plugins <validate|publish|list|retire> [--organization-id <id>] ...
9097
9232
 
9098
9233
  validate <manifest.json> (dry run: the platform's full validation, nothing stored)
@@ -9155,7 +9290,7 @@ async function runPlugins(args, io) {
9155
9290
  }
9156
9291
  switch (verb) {
9157
9292
  case "validate": {
9158
- const { values, positionals } = parseArgs4({ args: rest, options: { ...SCOPE_OPTIONS2, json: { type: "boolean", default: false } }, allowPositionals: true, strict: true });
9293
+ const { values, positionals } = parseArgs5({ args: rest, options: { ...SCOPE_OPTIONS2, json: { type: "boolean", default: false } }, allowPositionals: true, strict: true });
9159
9294
  const manifest = readManifest(positionals[0]);
9160
9295
  const tools = await client2(values, io);
9161
9296
  const outcome = await tools.publishPlugin(manifest, { dryRun: true });
@@ -9172,7 +9307,7 @@ async function runPlugins(args, io) {
9172
9307
  return ExitCode.Ok;
9173
9308
  }
9174
9309
  case "publish": {
9175
- const { values, positionals } = parseArgs4({ args: rest, options: { ...SCOPE_OPTIONS2, json: { type: "boolean", default: false } }, allowPositionals: true, strict: true });
9310
+ const { values, positionals } = parseArgs5({ args: rest, options: { ...SCOPE_OPTIONS2, json: { type: "boolean", default: false } }, allowPositionals: true, strict: true });
9176
9311
  const manifest = readManifest(positionals[0]);
9177
9312
  const tools = await client2(values, io);
9178
9313
  const outcome = await tools.publishPlugin(manifest, { dryRun: false });
@@ -9190,7 +9325,7 @@ async function runPlugins(args, io) {
9190
9325
  return ExitCode.Ok;
9191
9326
  }
9192
9327
  case "list": {
9193
- const { values } = parseArgs4({ args: rest, options: { ...SCOPE_OPTIONS2, json: { type: "boolean", default: false } }, strict: true });
9328
+ const { values } = parseArgs5({ args: rest, options: { ...SCOPE_OPTIONS2, json: { type: "boolean", default: false } }, strict: true });
9194
9329
  const tools = await client2(values, io);
9195
9330
  const plugins = await tools.listPlugins();
9196
9331
  if (values.json) io.stdout(JSON.stringify(plugins, null, 2));
@@ -9199,7 +9334,7 @@ async function runPlugins(args, io) {
9199
9334
  return ExitCode.Ok;
9200
9335
  }
9201
9336
  case "retire": {
9202
- const { values, positionals } = parseArgs4({ args: rest, options: SCOPE_OPTIONS2, allowPositionals: true, strict: true });
9337
+ const { values, positionals } = parseArgs5({ args: rest, options: SCOPE_OPTIONS2, allowPositionals: true, strict: true });
9203
9338
  const pluginId = positionals[0];
9204
9339
  if (!pluginId) throw new CliError(`A plugin id is required.
9205
9340
 
@@ -9269,6 +9404,7 @@ var COMMANDS = [
9269
9404
  { name: "login", summary: "Sign in through the browser", usage: loginUsage, run: runLogin },
9270
9405
  { name: "logout", summary: "Revoke the login and forget it", usage: logoutUsage, run: runLogout },
9271
9406
  { name: "status", summary: "Show the deployment and the cached login", usage: statusUsage, run: runStatus },
9407
+ { name: "org", summary: "Show or switch the organization the login acts in", usage: orgUsage, run: runOrg },
9272
9408
  { name: "token", summary: "Print a fresh access token", usage: tokenUsage, run: runToken },
9273
9409
  { name: "agents", summary: "Point a coding agent at the model gateway (setup, run, status)", usage: agentsUsage, run: runAgents },
9274
9410
  { name: "tools", summary: "Discover and execute platform tools; raise and read approvals", usage: toolsUsage, run: runTools },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divergeai/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Divergent platform CLI: sign in, wire coding agents to the inference gateway, call platform tools",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",