@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 +6 -3
- package/dist/diverge.mjs +172 -36
- package/package.json +1 -1
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 #
|
|
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).
|
|
20
|
-
|
|
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.
|
|
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
|
|
7439
|
+
function openerCommand(url, env = process.env, platform = process.platform) {
|
|
7440
7440
|
const custom = env.BROWSER?.trim();
|
|
7441
|
-
|
|
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, {
|
|
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
|
|
8365
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8482
|
+
info = await fetchUserInfo(apiUrl, token.access_token);
|
|
8420
8483
|
} catch (error) {
|
|
8421
|
-
|
|
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
|
-
|
|
8424
|
-
|
|
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
|
-
|
|
8528
|
-
io.stdout(`
|
|
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
|
|
8689
|
+
import { parseArgs as parseArgs4 } from "node:util";
|
|
8555
8690
|
|
|
8556
8691
|
// src/mcp/bridge.ts
|
|
8557
|
-
import
|
|
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 =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 } =
|
|
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 },
|