@openwop/cli 1.3.0 → 1.4.1

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/dist/cli.js CHANGED
@@ -578,7 +578,7 @@ function errText(err) {
578
578
  // package.json
579
579
  var package_default = {
580
580
  name: "@openwop/cli",
581
- version: "1.3.0",
581
+ version: "1.4.1",
582
582
  description: "OpenWOP CLI for operating the workflow-engine demo app and OpenWOP-compatible hosts.",
583
583
  license: "Apache-2.0",
584
584
  type: "module",
@@ -1078,7 +1078,7 @@ import { createInterface as createInterface2 } from "node:readline";
1078
1078
 
1079
1079
  // src/ids.ts
1080
1080
  function projectTenantBoundId(id) {
1081
- if (id.includes("~")) return id;
1081
+ if (id.includes("~") && !id.includes("/")) return id;
1082
1082
  let out = "";
1083
1083
  for (const byte of new TextEncoder().encode(id)) {
1084
1084
  const ch = String.fromCharCode(byte);
@@ -2632,7 +2632,19 @@ async function runInterrupts(ctx, argv) {
2632
2632
  write(ctx.io.stdout, "Usage: openwop interrupts list <runId> [--json]\n");
2633
2633
  return 2;
2634
2634
  }
2635
- const res = await requestJson(ctx, `/v1/host/openwop-app/runs/${encodeURIComponent(rest[0])}/interrupts`);
2635
+ let res;
2636
+ try {
2637
+ res = await requestJson(ctx, `/v1/host/openwop-app/runs/${encodeURIComponent(rest[0])}/interrupts`);
2638
+ } catch (err) {
2639
+ const env = err instanceof HttpError ? errorEnvelope(err.body) : {};
2640
+ if (err instanceof HttpError && err.status === 404 && /^no operation at /i.test(env.message ?? "")) {
2641
+ throw new CliError(
2642
+ "interrupts: `list` reads the openwop-app host extension (GET /v1/host/openwop-app/runs/{runId}/interrupts), which this host does not serve. On any host an open interrupt appears as interrupt.requested in the run's events; answer it with `openwop interrupts respond <runId> <nodeId>`.",
2643
+ 1
2644
+ );
2645
+ }
2646
+ throw err;
2647
+ }
2636
2648
  if (ctx.json) {
2637
2649
  writeJson(ctx.io.stdout, res.body);
2638
2650
  return 0;
@@ -3599,6 +3611,24 @@ async function readDiscovery(ctx) {
3599
3611
  ctx.discovery = { doc: res.body, servedVersion };
3600
3612
  return { doc: res.body, servedVersion, major };
3601
3613
  }
3614
+ async function advertisedRecord(ctx, pick) {
3615
+ const doc = (d) => d && typeof d === "object" ? d : {};
3616
+ let readAny = false;
3617
+ const v1 = await safeRequest(ctx, "/.well-known/openwop", { auth: false });
3618
+ if (v1.ok) {
3619
+ readAny = true;
3620
+ const r = pick(doc(v1.body));
3621
+ if (r) return r;
3622
+ }
3623
+ try {
3624
+ const { doc: negotiated } = await readDiscovery(ctx);
3625
+ readAny = true;
3626
+ const r = pick(doc(negotiated));
3627
+ if (r) return r;
3628
+ } catch {
3629
+ }
3630
+ return readAny ? null : void 0;
3631
+ }
3602
3632
  function isV2Discovery(caps, servedVersion) {
3603
3633
  if (typeof servedVersion === "string" && servedVersion.startsWith("2.")) return true;
3604
3634
  if (!caps || typeof caps !== "object" || "capabilities" in caps || "supportedTransports" in caps) return false;
@@ -4496,7 +4526,7 @@ async function runCatalogPacks(ctx, argv = []) {
4496
4526
  write(ctx.io.stdout, CATALOG_HELP);
4497
4527
  return 0;
4498
4528
  }
4499
- const res = await requestJson(ctx, "/v1/packs", { auth: false });
4529
+ const res = await catalogRead(ctx, "", { auth: false });
4500
4530
  if (ctx.json) {
4501
4531
  writeJson(ctx.io.stdout, res.body);
4502
4532
  return 0;
@@ -4505,6 +4535,14 @@ async function runCatalogPacks(ctx, argv = []) {
4505
4535
  writeLine(ctx.io.stdout, formatTable(rows, ["name", "nodes"]));
4506
4536
  return 0;
4507
4537
  }
4538
+ async function catalogRead(ctx, rest, options = {}) {
4539
+ try {
4540
+ return await requestJson(ctx, `/v1/host/openwop-app/packs${rest}`, options);
4541
+ } catch (err) {
4542
+ if (err instanceof HttpError && err.status === 404) return requestJson(ctx, `/v1/packs${rest}`, options);
4543
+ throw err;
4544
+ }
4545
+ }
4508
4546
  async function runCatalogPacksVerb(ctx, verb, argv) {
4509
4547
  const { options, positionals } = parseOptions(argv, { bool: ["--help"], value: ["--out"] });
4510
4548
  if (options.help) {
@@ -4513,7 +4551,7 @@ async function runCatalogPacksVerb(ctx, verb, argv) {
4513
4551
  }
4514
4552
  if (verb === "search") {
4515
4553
  const q = positionals[0] ?? "";
4516
- const res2 = await requestJson(ctx, `/v1/packs/-/search${q ? `?q=${encodeURIComponent(q)}` : ""}`);
4554
+ const res2 = await catalogRead(ctx, `/-/search${q ? `?q=${encodeURIComponent(q)}` : ""}`);
4517
4555
  if (ctx.json) {
4518
4556
  writeJson(ctx.io.stdout, res2.body);
4519
4557
  return 0;
@@ -4529,7 +4567,7 @@ async function runCatalogPacksVerb(ctx, verb, argv) {
4529
4567
  }
4530
4568
  if (verb === "get") {
4531
4569
  if (positionals.length !== 1) throw new CliError("Usage: openwop catalog packs get <packName> [--json]", 2);
4532
- const res2 = await requestJson(ctx, `/v1/packs/${encodeURIComponent(positionals[0])}`);
4570
+ const res2 = await catalogRead(ctx, `/${encodeURIComponent(positionals[0])}`);
4533
4571
  if (ctx.json) {
4534
4572
  writeJson(ctx.io.stdout, res2.body);
4535
4573
  return 0;
@@ -4540,7 +4578,7 @@ async function runCatalogPacksVerb(ctx, verb, argv) {
4540
4578
  for (const n of nodes) writeLine(ctx.io.stdout, ` ${n}`);
4541
4579
  return 0;
4542
4580
  }
4543
- const res = await requestJson(ctx, "/v1/packs/export");
4581
+ const res = await catalogRead(ctx, "/export");
4544
4582
  if (options.out) {
4545
4583
  writeFileSync4(String(options.out), `${JSON.stringify(res.body, null, 2)}
4546
4584
  `);
@@ -14860,6 +14898,153 @@ async function runOnboard(ctx, argv) {
14860
14898
  return 0;
14861
14899
  }
14862
14900
 
14901
+ // src/cli/login.ts
14902
+ import { hostname } from "node:os";
14903
+ var BASE10 = "/v1/host/openwop-app/cli-login";
14904
+ var LOGIN_HELP = `Usage:
14905
+ openwop login [--label <name>] [--timeout <seconds>] [--profile <name>] [--json]
14906
+ openwop logout [--profile <name>] [--json]
14907
+
14908
+ Sign this terminal in to an openwop-app host without copying an API key
14909
+ (host extension under ${BASE10}).
14910
+
14911
+ login Shows a short code. Open the host's Access \u2192 API keys page, type the
14912
+ code under "Sign in the OpenWOP CLI", and approve. The host then issues
14913
+ this CLI a key (30 days by default), saved to your config file.
14914
+ logout Revokes that key on the host and removes it from your config file.
14915
+
14916
+ The key is never printed. It is listed in the app as "CLI: <label>", where you can
14917
+ revoke it. Sign in again to renew.
14918
+
14919
+ --label <name> What to call this terminal in the app (default: this machine's hostname).
14920
+ --timeout <seconds> Give up waiting for approval after this long (default: the host's own limit).
14921
+ --profile <name> Use the named config profile.
14922
+
14923
+ Approve a code ONLY if you just ran \`openwop login\` yourself.
14924
+
14925
+ Exit codes: 0 signed in / signed out \xB7 1 denied, expired or not available on this host \xB7 2 usage.
14926
+
14927
+ Examples:
14928
+ export OPENWOP_BASE_URL=https://app.openwop.dev/api
14929
+ openwop login
14930
+ openwop login --label "work laptop"
14931
+ openwop logout
14932
+ `;
14933
+ var sleep2 = (ms2) => new Promise((r) => setTimeout(r, ms2));
14934
+ function approvalUrl(baseUrl, path) {
14935
+ const p = typeof path === "string" && path.startsWith("/") ? path : "/access?tab=api-keys";
14936
+ try {
14937
+ return `${new URL(baseUrl).origin}${p}`;
14938
+ } catch {
14939
+ return p;
14940
+ }
14941
+ }
14942
+ function notAvailable(ctx) {
14943
+ return new CliError(
14944
+ `${ctx.baseUrl} does not serve the CLI sign-in (${BASE10}/start is not there). Create an API key on the host and set OPENWOP_API_KEY instead.`,
14945
+ 1
14946
+ );
14947
+ }
14948
+ async function runLogin(ctx, argv) {
14949
+ const { options } = parseOptions(argv, { bool: ["--help"], value: ["--label", "--timeout", "--profile"] });
14950
+ if (options.help) {
14951
+ write(ctx.io.stdout, LOGIN_HELP);
14952
+ return 0;
14953
+ }
14954
+ const timeoutS = options.timeout === void 0 ? void 0 : Number(options.timeout);
14955
+ if (timeoutS !== void 0 && !(Number.isFinite(timeoutS) && timeoutS > 0)) {
14956
+ writeLine(ctx.io.stderr, "--timeout must be a positive number of seconds.");
14957
+ return 2;
14958
+ }
14959
+ const label = String(options.label ?? hostname() ?? "this machine").slice(0, 60);
14960
+ const anon = { ...ctx, apiKey: void 0 };
14961
+ let started;
14962
+ try {
14963
+ started = (await requestJson(anon, `${BASE10}/start`, { method: "POST", body: { label } })).body;
14964
+ } catch (err) {
14965
+ if (err instanceof HttpError && err.status === 404) throw notAvailable(ctx);
14966
+ throw err;
14967
+ }
14968
+ const deviceCode = started?.deviceCode;
14969
+ const userCode = started?.userCode;
14970
+ if (typeof deviceCode !== "string" || typeof userCode !== "string") throw notAvailable(ctx);
14971
+ writeLine(ctx.io.stderr, "");
14972
+ writeLine(ctx.io.stderr, ` 1. Open ${approvalUrl(ctx.baseUrl, started.verificationPath)}`);
14973
+ writeLine(ctx.io.stderr, ` 2. Enter ${userCode} under "Sign in the OpenWOP CLI", and approve.`);
14974
+ writeLine(ctx.io.stderr, "");
14975
+ writeLine(ctx.io.stderr, "Waiting for approval\u2026");
14976
+ const hostLimitS = Number.isFinite(Number(started.expiresIn)) ? Number(started.expiresIn) : 600;
14977
+ const deadline = Date.now() + Math.min(timeoutS ?? hostLimitS, hostLimitS) * 1e3;
14978
+ let intervalS = Number.isFinite(Number(started.interval)) ? Number(started.interval) : 3;
14979
+ for (; ; ) {
14980
+ if (Date.now() >= deadline) throw new CliError("Timed out waiting for approval. Run `openwop login` again for a new code.", 1);
14981
+ await sleep2(Math.max(0, intervalS) * 1e3);
14982
+ let res;
14983
+ try {
14984
+ res = (await requestJson(anon, `${BASE10}/poll`, { method: "POST", body: { deviceCode } })).body;
14985
+ } catch (err) {
14986
+ if (err instanceof HttpError && err.status === 404) {
14987
+ throw new CliError("That sign-in expired or was already used. Run `openwop login` again for a new code.", 1);
14988
+ }
14989
+ throw err;
14990
+ }
14991
+ if (res?.status === "pending") {
14992
+ if (Number.isFinite(Number(res.interval))) intervalS = Number(res.interval);
14993
+ continue;
14994
+ }
14995
+ if (res?.status === "denied") throw new CliError("The sign-in was denied in the app.", 1);
14996
+ if (res?.status === "failed") throw new CliError(`The host could not issue a key: ${String(res.message ?? "unknown reason")}`, 1);
14997
+ if (res?.status === "approved" && typeof res.token === "string") {
14998
+ const configPath = configPathFor(options.profile, ctx.env);
14999
+ saveConfig(configPath, mergeConfig(readConfigSafe(configPath), { host: { baseUrl: ctx.baseUrl, apiKey: res.token } }));
15000
+ const key = res.key ?? {};
15001
+ if (ctx.json) {
15002
+ writeJson(ctx.io.stdout, { status: "signed-in", keyId: key.keyId ?? null, name: key.name ?? null, expiresAt: key.expiresAt ?? null, configPath });
15003
+ return 0;
15004
+ }
15005
+ writeLine(ctx.io.stdout, `Signed in to ${ctx.baseUrl} as \u201C${key.name ?? `CLI: ${label}`}\u201D.`);
15006
+ if (key.expiresAt) writeLine(ctx.io.stdout, `The key expires ${String(key.expiresAt).slice(0, 10)}; run \`openwop login\` again to renew.`);
15007
+ writeLine(ctx.io.stdout, `Saved to ${configPath}. Revoke it any time with \`openwop logout\` or in the app.`);
15008
+ return 0;
15009
+ }
15010
+ throw new CliError("The host answered the sign-in in a shape this CLI does not understand.", 1);
15011
+ }
15012
+ }
15013
+ async function runLogout(ctx, argv) {
15014
+ const { options } = parseOptions(argv, { bool: ["--help"], value: ["--profile"] });
15015
+ if (options.help) {
15016
+ write(ctx.io.stdout, LOGIN_HELP);
15017
+ return 0;
15018
+ }
15019
+ const configPath = configPathFor(options.profile, ctx.env);
15020
+ const config = readConfigSafe(configPath);
15021
+ const saved = config?.host?.apiKey;
15022
+ if (typeof saved !== "string" || saved.length === 0) {
15023
+ if (ctx.json) writeJson(ctx.io.stdout, { status: "not-signed-in" });
15024
+ else writeLine(ctx.io.stdout, "Not signed in: no key is saved in your config.");
15025
+ return 0;
15026
+ }
15027
+ let revoked = false;
15028
+ let note = "";
15029
+ try {
15030
+ await requestJson({ ...ctx, apiKey: saved }, `${BASE10}/logout`, { method: "POST", body: {} });
15031
+ revoked = true;
15032
+ } catch (err) {
15033
+ if (err instanceof HttpError && err.status === 401) note = "The host no longer recognised the key (already revoked or expired).";
15034
+ else if (err instanceof HttpError && err.status === 404) note = "This host has no sign-out route; the key was NOT revoked there \u2014 revoke it in the app.";
15035
+ else if (err instanceof HttpError && err.status === 400) note = "The saved key is not one a sign-in issued; it was NOT revoked on the host \u2014 revoke it in the app.";
15036
+ else throw err;
15037
+ }
15038
+ unsetByPath(config, "host.apiKey");
15039
+ saveConfig(configPath, config);
15040
+ if (ctx.json) {
15041
+ writeJson(ctx.io.stdout, { status: "signed-out", revokedOnHost: revoked, ...note ? { note } : {} });
15042
+ return 0;
15043
+ }
15044
+ writeLine(ctx.io.stdout, revoked ? "Signed out. The key was revoked on the host and removed from your config." : `Removed the key from your config. ${note}`);
15045
+ return 0;
15046
+ }
15047
+
14863
15048
  // src/cli/providers.ts
14864
15049
  var PROVIDERS_HELP = `Usage:
14865
15050
  openwop providers list [--json]
@@ -18086,7 +18271,7 @@ Examples:
18086
18271
  openwop profiles pin r_123
18087
18272
  openwop profiles activity --limit 10 --status failed
18088
18273
  `;
18089
- var BASE10 = "/v1/host/openwop-app/profiles";
18274
+ var BASE11 = "/v1/host/openwop-app/profiles";
18090
18275
  async function profilesRequest(ctx, path, options, notFoundMsg) {
18091
18276
  try {
18092
18277
  return await requestJson(ctx, path, options);
@@ -18183,7 +18368,7 @@ async function runList2(ctx, argv) {
18183
18368
  write(ctx.io.stdout, "Usage: openwop profiles list [--json]\n");
18184
18369
  return 0;
18185
18370
  }
18186
- const res = await profilesRequest(ctx, BASE10);
18371
+ const res = await profilesRequest(ctx, BASE11);
18187
18372
  if (ctx.json) {
18188
18373
  writeJson(ctx.io.stdout, res.body);
18189
18374
  return 0;
@@ -18209,7 +18394,7 @@ async function runGet3(ctx, argv) {
18209
18394
  write(ctx.io.stdout, "Usage: openwop profiles get [<userId>] [--json]\n");
18210
18395
  return options.help ? 0 : 2;
18211
18396
  }
18212
- const path = positionals.length === 1 ? `${BASE10}/${encodeURIComponent(positionals[0])}` : `${BASE10}/me`;
18397
+ const path = positionals.length === 1 ? `${BASE11}/${encodeURIComponent(positionals[0])}` : `${BASE11}/me`;
18213
18398
  const res = await profilesRequest(ctx, path, void 0, `Profile not found: ${positionals[0] ?? "me"}`);
18214
18399
  if (ctx.json) {
18215
18400
  writeJson(ctx.io.stdout, res.body);
@@ -18228,7 +18413,7 @@ async function runActivity(ctx, argv) {
18228
18413
  if (options.limit !== void 0) qs2.set("limit", String(options.limit));
18229
18414
  if (options.status !== void 0) qs2.set("status", String(options.status));
18230
18415
  const q = qs2.toString();
18231
- const res = await profilesRequest(ctx, `${BASE10}/me/activity${q ? `?${q}` : ""}`);
18416
+ const res = await profilesRequest(ctx, `${BASE11}/me/activity${q ? `?${q}` : ""}`);
18232
18417
  if (ctx.json) {
18233
18418
  writeJson(ctx.io.stdout, res.body);
18234
18419
  return 0;
@@ -18267,7 +18452,7 @@ async function runEdit(ctx, argv) {
18267
18452
  if (Array.isArray(options.interests)) body.interests = options.interests;
18268
18453
  if (Array.isArray(options.growthInterests)) body.growthInterests = options.growthInterests;
18269
18454
  if (options.location !== void 0 || Array.isArray(options.link)) {
18270
- const current = (await profilesRequest(ctx, `${BASE10}/me`)).body?.contact ?? {};
18455
+ const current = (await profilesRequest(ctx, `${BASE11}/me`)).body?.contact ?? {};
18271
18456
  const contact = { ...current };
18272
18457
  if (options.location !== void 0) contact.location = options.location;
18273
18458
  if (Array.isArray(options.link)) {
@@ -18294,7 +18479,7 @@ async function runEdit(ctx, argv) {
18294
18479
  }
18295
18480
  if (Object.keys(avail).length) body.availability = avail;
18296
18481
  if (Object.keys(body).length === 0) throw new CliError("Nothing to edit \u2014 pass at least one field.", 2);
18297
- const res = await profilesRequest(ctx, `${BASE10}/me`, { method: "PATCH", body });
18482
+ const res = await profilesRequest(ctx, `${BASE11}/me`, { method: "PATCH", body });
18298
18483
  if (ctx.json) {
18299
18484
  writeJson(ctx.io.stdout, res.body);
18300
18485
  return 0;
@@ -18320,7 +18505,7 @@ async function runSkillsSet(ctx, argv) {
18320
18505
  if (!Number.isFinite(prof2) || prof2 < 1 || prof2 > 5) throw new CliError(`--skill proficiency must be 1..5 (got '${e}')`, 2);
18321
18506
  return { name, proficiency: prof2 };
18322
18507
  });
18323
- const res = await profilesRequest(ctx, `${BASE10}/me/skills`, { method: "PUT", body: { skills } });
18508
+ const res = await profilesRequest(ctx, `${BASE11}/me/skills`, { method: "PUT", body: { skills } });
18324
18509
  if (ctx.json) {
18325
18510
  writeJson(ctx.io.stdout, res.body);
18326
18511
  return 0;
@@ -18335,7 +18520,7 @@ async function runPortfolioAdd(ctx, argv) {
18335
18520
  write(ctx.io.stdout, "Usage: openwop profiles portfolio add --token <mediaToken> [--json]\n");
18336
18521
  return options.help ? 0 : 2;
18337
18522
  }
18338
- const res = await profilesRequest(ctx, `${BASE10}/me/portfolio`, { method: "POST", body: { token: options.token } });
18523
+ const res = await profilesRequest(ctx, `${BASE11}/me/portfolio`, { method: "POST", body: { token: options.token } });
18339
18524
  if (ctx.json) {
18340
18525
  writeJson(ctx.io.stdout, res.body);
18341
18526
  return 0;
@@ -18349,7 +18534,7 @@ async function runPortfolioRemove(ctx, argv) {
18349
18534
  write(ctx.io.stdout, "Usage: openwop profiles portfolio remove <mediaToken> [--json]\n");
18350
18535
  return options.help ? 0 : 2;
18351
18536
  }
18352
- const res = await profilesRequest(ctx, `${BASE10}/me/portfolio/${encodeURIComponent(positionals[0])}`, { method: "DELETE" }, `Portfolio asset not found: ${positionals[0]}`);
18537
+ const res = await profilesRequest(ctx, `${BASE11}/me/portfolio/${encodeURIComponent(positionals[0])}`, { method: "DELETE" }, `Portfolio asset not found: ${positionals[0]}`);
18353
18538
  if (ctx.json) {
18354
18539
  writeJson(ctx.io.stdout, res.body);
18355
18540
  return 0;
@@ -18364,7 +18549,7 @@ async function runPin(ctx, argv, pinned) {
18364
18549
  `);
18365
18550
  return options.help ? 0 : 2;
18366
18551
  }
18367
- const path = `${BASE10}/me/pinned-agents/${encodeURIComponent(positionals[0])}`;
18552
+ const path = `${BASE11}/me/pinned-agents/${encodeURIComponent(positionals[0])}`;
18368
18553
  const res = await profilesRequest(ctx, path, { method: pinned ? "PUT" : "DELETE" }, `Agent not found: ${positionals[0]}`);
18369
18554
  if (ctx.json) {
18370
18555
  writeJson(ctx.io.stdout, res.body);
@@ -18381,7 +18566,7 @@ async function runEndorse(ctx, argv, add) {
18381
18566
  return options.help ? 0 : 2;
18382
18567
  }
18383
18568
  const [userId, skill] = positionals;
18384
- const path = `${BASE10}/${encodeURIComponent(userId)}/skills/${encodeURIComponent(skill)}/endorse`;
18569
+ const path = `${BASE11}/${encodeURIComponent(userId)}/skills/${encodeURIComponent(skill)}/endorse`;
18385
18570
  const res = await profilesRequest(ctx, path, { method: add ? "POST" : "DELETE" }, `Profile or skill not found: ${userId} / ${skill}`);
18386
18571
  if (ctx.json) {
18387
18572
  writeJson(ctx.io.stdout, res.body);
@@ -19476,7 +19661,7 @@ Examples:
19476
19661
  openwop auth saml validate --idp-url http://localhost:9100/idp --variant valid
19477
19662
  openwop auth scim provision --op create-user --user-name jo@acme.com --email jo@acme.com
19478
19663
  `;
19479
- var BASE11 = "/v1/host/openwop-app/auth";
19664
+ var BASE12 = "/v1/host/openwop-app/auth";
19480
19665
  var SECRET_KEY2 = /secret|token|password|private[-_]?key|client[-_]?secret|api[-_]?key|credential|bearer|assertion|certificate|cert|pem/i;
19481
19666
  function redact2(value) {
19482
19667
  return redactSecrets(value, { secret: SECRET_KEY2 });
@@ -19527,7 +19712,7 @@ async function runStatus(ctx, argv) {
19527
19712
  const profiles = disc.ok && Array.isArray(disc.body?.capabilities?.auth?.profiles) ? disc.body.capabilities.auth.profiles : [];
19528
19713
  const samlAdvertised = profiles.includes("openwop-auth-saml");
19529
19714
  const scimAdvertised = profiles.includes("openwop-auth-scim");
19530
- const metaReachable = samlAdvertised ? (await probeEndpoint(ctx, `${BASE11}/saml/sso/metadata`)).ok : false;
19715
+ const metaReachable = samlAdvertised ? (await probeEndpoint(ctx, `${BASE12}/saml/sso/metadata`)).ok : false;
19531
19716
  if (ctx.json) {
19532
19717
  writeJson(ctx.io.stdout, {
19533
19718
  authProfilesAdvertised: profiles,
@@ -19552,7 +19737,7 @@ async function runSamlMetadata(ctx, argv) {
19552
19737
  write(ctx.io.stdout, "Usage: openwop auth saml metadata [--json]\n");
19553
19738
  return 0;
19554
19739
  }
19555
- const res = await authRequest(ctx, `${BASE11}/saml/sso/metadata`, { auth: false }, "SAML SSO is not configured on this host (set OPENWOP_SAML_*).");
19740
+ const res = await authRequest(ctx, `${BASE12}/saml/sso/metadata`, { auth: false }, "SAML SSO is not configured on this host (set OPENWOP_SAML_*).");
19556
19741
  const xml = typeof res.body?.raw === "string" ? res.body.raw : typeof res.body === "string" ? res.body : JSON.stringify(res.body);
19557
19742
  if (ctx.json) {
19558
19743
  writeJson(ctx.io.stdout, { metadata: xml });
@@ -19570,7 +19755,7 @@ async function runSamlLoginUrl(ctx, argv) {
19570
19755
  }
19571
19756
  const returnTo = options.returnTo ?? "/";
19572
19757
  const base16 = ctx.baseUrl.endsWith("/") ? ctx.baseUrl : `${ctx.baseUrl}/`;
19573
- const url = new URL(`${BASE11}/saml/sso/login?returnTo=${encodeURIComponent(returnTo)}`.replace(/^\//, ""), base16);
19758
+ const url = new URL(`${BASE12}/saml/sso/login?returnTo=${encodeURIComponent(returnTo)}`.replace(/^\//, ""), base16);
19574
19759
  const res = await ctx.fetchImpl(url, { method: "GET", redirect: "manual" });
19575
19760
  if (res.status === 404) {
19576
19761
  throw new CliError("SAML SSO is not configured on this host (set OPENWOP_SAML_*).", 1);
@@ -19595,7 +19780,7 @@ async function runSamlValidate(ctx, argv) {
19595
19780
  const body = { idpUrl: options.idpUrl, variant: options.variant };
19596
19781
  let res;
19597
19782
  try {
19598
- res = await requestJson(ctx, `${BASE11}/saml/validate`, { method: "POST", body });
19783
+ res = await requestJson(ctx, `${BASE12}/saml/validate`, { method: "POST", body });
19599
19784
  } catch (err) {
19600
19785
  if (err instanceof HttpError && err.status === 404) {
19601
19786
  throw new CliError("SAML validation seam not configured on this host (set OPENWOP_TEST_SAML_IDP_URL).", 1);
@@ -19638,7 +19823,7 @@ async function runScimProvision(ctx, argv) {
19638
19823
  if (options.displayName !== void 0) user.displayName = options.displayName;
19639
19824
  const body = { op: options.op, user };
19640
19825
  if (options.group !== void 0) body.group = options.group;
19641
- const res = await authRequest(ctx, `${BASE11}/scim/provision`, { method: "POST", body }, "SCIM provisioning seam not configured on this host (set OPENWOP_TEST_SCIM_URL).");
19826
+ const res = await authRequest(ctx, `${BASE12}/scim/provision`, { method: "POST", body }, "SCIM provisioning seam not configured on this host (set OPENWOP_TEST_SCIM_URL).");
19642
19827
  const safe = redact2(res.body ?? {});
19643
19828
  if (ctx.json) {
19644
19829
  writeJson(ctx.io.stdout, safe);
@@ -19662,7 +19847,7 @@ async function runBreakGlass(ctx, argv) {
19662
19847
  const token = await readSecretInput(ctx, options.tokenFile !== void 0 ? { valueFile: options.tokenFile } : {}, "Break-glass token");
19663
19848
  let res;
19664
19849
  try {
19665
- res = await requestJson(ctx, `${BASE11}/break-glass`, { method: "POST", body: { token } });
19850
+ res = await requestJson(ctx, `${BASE12}/break-glass`, { method: "POST", body: { token } });
19666
19851
  } catch (err) {
19667
19852
  if (err instanceof HttpError && err.status === 404) throw new CliError("Break-glass login is not enabled on this host (OPENWOP_BREAKGLASS_ENABLED is not true).", 1);
19668
19853
  if (err instanceof HttpError && err.status === 401) throw new CliError("Break-glass login refused (wrong, short, or already-used token, or the host is misconfigured). The attempt was audited.", 4);
@@ -19991,10 +20176,9 @@ function exitForState(state) {
19991
20176
  return 1;
19992
20177
  }
19993
20178
  async function ensureAdvertised3(ctx) {
19994
- const wk = await safeRequest(ctx, "/.well-known/openwop", { auth: false });
19995
- if (!wk.ok) return;
19996
- const body = wk.body && typeof wk.body === "object" ? wk.body : {};
19997
- const advertised = !!(body.capabilities?.agents?.proposals ?? body.agents?.proposals);
20179
+ const record = await advertisedRecord(ctx, (b) => b.capabilities?.agents?.proposals ?? b.agents?.proposals);
20180
+ if (record === void 0) return;
20181
+ const advertised = !!record;
19998
20182
  if (!advertised) {
19999
20183
  throw new CliError(
20000
20184
  "proposals: this host does not advertise the reviewable-learning proposal capability (capabilities.agents.proposals is absent from /.well-known/openwop). The host is the authority \u2014 refusing to guess.",
@@ -20323,10 +20507,9 @@ function exitForState2(state) {
20323
20507
  return 1;
20324
20508
  }
20325
20509
  async function ensureAdvertised4(ctx) {
20326
- const wk = await safeRequest(ctx, "/.well-known/openwop", { auth: false });
20327
- if (!wk.ok) return;
20328
- const body = wk.body && typeof wk.body === "object" ? wk.body : {};
20329
- const advertised = !!(body.capabilities?.agents?.goals ?? body.agents?.goals);
20510
+ const record = await advertisedRecord(ctx, (b) => b.capabilities?.agents?.goals ?? b.agents?.goals);
20511
+ if (record === void 0) return;
20512
+ const advertised = !!record;
20330
20513
  if (!advertised) {
20331
20514
  throw new CliError(
20332
20515
  "goals: this host does not advertise the standing-goals capability (capabilities.agents.goals is absent from /.well-known/openwop). The host is the authority \u2014 refusing to guess.",
@@ -20594,10 +20777,9 @@ function redact3(value) {
20594
20777
  return redactSecrets(value, { secret: SECRET_KEY3, keep: REF_ONLY_KEY });
20595
20778
  }
20596
20779
  async function ensureAdvertised5(ctx) {
20597
- const wk = await safeRequest(ctx, "/.well-known/openwop", { auth: false });
20598
- if (!wk.ok) return;
20599
- const body = wk.body && typeof wk.body === "object" ? wk.body : {};
20600
- const advertised = !!(body.capabilities?.portability ?? body.portability);
20780
+ const record = await advertisedRecord(ctx, (b) => b.capabilities?.portability ?? b.portability);
20781
+ if (record === void 0) return;
20782
+ const advertised = !!record;
20601
20783
  if (!advertised) {
20602
20784
  throw new CliError(
20603
20785
  "portability: this host does not advertise the export/import capability (capabilities.portability is absent from /.well-known/openwop). The host is the authority \u2014 refusing to guess.",
@@ -20844,10 +21026,7 @@ function exitForState3(state) {
20844
21026
  return 1;
20845
21027
  }
20846
21028
  async function triggerBridgeCaps(ctx) {
20847
- const wk = await safeRequest(ctx, "/.well-known/openwop", { auth: false });
20848
- if (!wk.ok) return void 0;
20849
- const body = wk.body && typeof wk.body === "object" ? wk.body : {};
20850
- return body.capabilities?.triggerBridge ?? body.triggerBridge ?? null;
21029
+ return advertisedRecord(ctx, (b) => b.capabilities?.triggerBridge ?? b.triggerBridge);
20851
21030
  }
20852
21031
  async function ensureAdvertised6(ctx) {
20853
21032
  const tb = await triggerBridgeCaps(ctx);
@@ -21107,10 +21286,7 @@ function exitForState4(state) {
21107
21286
  return 1;
21108
21287
  }
21109
21288
  async function a2aCaps(ctx) {
21110
- const wk = await safeRequest(ctx, "/.well-known/openwop", { auth: false });
21111
- if (!wk.ok) return void 0;
21112
- const body = wk.body && typeof wk.body === "object" ? wk.body : {};
21113
- return body.capabilities?.a2a ?? body.a2a ?? null;
21289
+ return advertisedRecord(ctx, (b) => b.capabilities?.a2a ?? b.a2a);
21114
21290
  }
21115
21291
  async function runStatus2(ctx, argv) {
21116
21292
  const { options } = parseOptions(argv, { bool: ["--help"] });
@@ -21127,7 +21303,7 @@ async function runStatus2(ctx, argv) {
21127
21303
  writeLine(ctx.io.stdout, "This host does not advertise A2A (capabilities.a2a absent).");
21128
21304
  return 1;
21129
21305
  }
21130
- writeLine(ctx.io.stdout, `a2a.supported: ${a2a.supported ? "yes" : "no"}`);
21306
+ writeLine(ctx.io.stdout, `a2a.supported: ${a2a.supported ?? true ? "yes" : "no"}`);
21131
21307
  if (a2a.agentCardUrl) writeLine(ctx.io.stdout, `agentCardUrl: ${a2a.agentCardUrl}`);
21132
21308
  writeLine(ctx.io.stdout, `streaming: ${a2a.streaming ? "yes" : "no"}`);
21133
21309
  writeLine(ctx.io.stdout, `pushNotifications: ${a2a.pushNotifications ? "yes" : "no"}`);
@@ -21316,7 +21492,7 @@ Run \`openwop docs --help\` for usage.`);
21316
21492
  }
21317
21493
 
21318
21494
  // src/cli/knowledgeSync.ts
21319
- var BASE12 = "/v1/host/openwop-app/knowledge-sync";
21495
+ var BASE13 = "/v1/host/openwop-app/knowledge-sync";
21320
21496
  var KNOWLEDGE_SYNC_HELP = `Usage:
21321
21497
  openwop knowledge-sync list --org <orgId> [--json]
21322
21498
  openwop knowledge-sync get <sourceId> [--json]
@@ -21376,12 +21552,12 @@ async function runKnowledgeSync(ctx, argv) {
21376
21552
  switch (sub) {
21377
21553
  case "list": {
21378
21554
  if (!options.org) return usage4(ctx, "list --org <orgId>");
21379
- const res = await requestJson(ctx, withQuery(BASE12, { orgId: String(options.org) }));
21555
+ const res = await requestJson(ctx, withQuery(BASE13, { orgId: String(options.org) }));
21380
21556
  return renderList(ctx, res.body, pickArray(res.body, "sources"), ["id", "provider", "collectionId", "cadence", "status", "lastSyncedAt"], "No sync sources.");
21381
21557
  }
21382
21558
  case "get": {
21383
21559
  if (!id) return usage4(ctx, "get <sourceId>");
21384
- writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE12}/${enc2(id)}`)).body);
21560
+ writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE13}/${enc2(id)}`)).body);
21385
21561
  return 0;
21386
21562
  }
21387
21563
  case "create": {
@@ -21397,12 +21573,12 @@ async function runKnowledgeSync(ctx, argv) {
21397
21573
  if (options.cadence) body.cadence = String(options.cadence);
21398
21574
  if (options.provider) body.provider = String(options.provider);
21399
21575
  if (options.noMedia) body.includeMedia = false;
21400
- const res = await requestJson(ctx, BASE12, { method: "POST", body });
21576
+ const res = await requestJson(ctx, BASE13, { method: "POST", body });
21401
21577
  return renderDone(ctx, res.body, `Created sync source ${res.body?.source?.id ?? ""}.`);
21402
21578
  }
21403
21579
  case "browse": {
21404
21580
  if (!options.org || !options.connection) return usage4(ctx, "browse --org <orgId> --connection <connectionId> [--folder id]");
21405
- const res = await requestJson(ctx, withQuery(`${BASE12}/browse`, {
21581
+ const res = await requestJson(ctx, withQuery(`${BASE13}/browse`, {
21406
21582
  orgId: String(options.org),
21407
21583
  connectionId: String(options.connection),
21408
21584
  folderId: options.folder
@@ -21411,18 +21587,18 @@ async function runKnowledgeSync(ctx, argv) {
21411
21587
  }
21412
21588
  case "update": {
21413
21589
  if (!id || !options.includeMedia && !options.noMedia) return usage4(ctx, "update <sourceId> (--include-media | --no-media)");
21414
- const res = await requestJson(ctx, `${BASE12}/${enc2(id)}`, { method: "PATCH", body: { includeMedia: !options.noMedia } });
21590
+ const res = await requestJson(ctx, `${BASE13}/${enc2(id)}`, { method: "PATCH", body: { includeMedia: !options.noMedia } });
21415
21591
  return renderDone(ctx, res.body, `Updated sync source ${id} (media ${options.noMedia ? "excluded" : "included"}).`);
21416
21592
  }
21417
21593
  case "pause":
21418
21594
  case "resume": {
21419
21595
  if (!id) return usage4(ctx, `${sub} <sourceId>`);
21420
- const res = await requestJson(ctx, `${BASE12}/${enc2(id)}/${sub}`, { method: "POST", body: {} });
21596
+ const res = await requestJson(ctx, `${BASE13}/${enc2(id)}/${sub}`, { method: "POST", body: {} });
21421
21597
  return renderDone(ctx, res.body, `${sub === "pause" ? "Paused" : "Resumed"} sync source ${id}.`);
21422
21598
  }
21423
21599
  case "sync": {
21424
21600
  if (!id) return usage4(ctx, "sync <sourceId>");
21425
- const res = await requestJson(ctx, `${BASE12}/${enc2(id)}/sync`, { method: "POST", body: {} });
21601
+ const res = await requestJson(ctx, `${BASE13}/${enc2(id)}/sync`, { method: "POST", body: {} });
21426
21602
  const r = res.body?.result ?? {};
21427
21603
  const counts = Object.entries(r).filter(([, v]) => typeof v === "number").map(([k, v]) => `${k}=${v}`).join(" ");
21428
21604
  return renderDone(ctx, res.body, `Synced source ${id}${counts ? `: ${counts}` : "."}`);
@@ -21430,7 +21606,7 @@ async function runKnowledgeSync(ctx, argv) {
21430
21606
  case "delete": {
21431
21607
  if (!id) return usage4(ctx, "delete <sourceId> [--yes]");
21432
21608
  if (!options.yes) throw new CliError(`Refusing to delete sync source ${id} without --yes.`, 2);
21433
- const res = await requestJson(ctx, `${BASE12}/${enc2(id)}`, { method: "DELETE" });
21609
+ const res = await requestJson(ctx, `${BASE13}/${enc2(id)}`, { method: "DELETE" });
21434
21610
  return renderDone(ctx, res.body, `Deleted sync source ${id}.`);
21435
21611
  }
21436
21612
  default:
@@ -21442,7 +21618,7 @@ Run \`openwop knowledge-sync --help\` for usage.`);
21442
21618
  // src/cli/entities.ts
21443
21619
  import { readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "node:fs";
21444
21620
  import { resolve as resolvePath9 } from "node:path";
21445
- var BASE13 = "/v1/host/openwop-app/entities";
21621
+ var BASE14 = "/v1/host/openwop-app/entities";
21446
21622
  var PUBLIC_BASE2 = "/v1/host/openwop-app/public-entities";
21447
21623
  var ENTITIES_HELP = `Usage:
21448
21624
  openwop entities locale-context [--json]
@@ -21564,7 +21740,7 @@ async function runEntities(ctx, argv) {
21564
21740
  const q = (path, extra = {}) => withQuery(path, { projectId: project4, ...extra });
21565
21741
  switch (family) {
21566
21742
  case "locale-context": {
21567
- const res = await requestJson(ctx, q(`${BASE13}/locale-context`));
21743
+ const res = await requestJson(ctx, q(`${BASE14}/locale-context`));
21568
21744
  if (ctx.json) {
21569
21745
  writeJson(ctx.io.stdout, res.body);
21570
21746
  return 0;
@@ -21596,10 +21772,10 @@ Run \`openwop entities --help\` for usage.`);
21596
21772
  }
21597
21773
  async function types(ctx, sub, pos, o, project4, q) {
21598
21774
  const name = pos[0];
21599
- const typePath = (n) => `${BASE13}/types/${enc2(n)}`;
21775
+ const typePath = (n) => `${BASE14}/types/${enc2(n)}`;
21600
21776
  switch (sub) {
21601
21777
  case "list": {
21602
- const res = await requestJson(ctx, q(`${BASE13}/types`));
21778
+ const res = await requestJson(ctx, q(`${BASE14}/types`));
21603
21779
  return renderList(
21604
21780
  ctx,
21605
21781
  res.body,
@@ -21623,7 +21799,7 @@ async function types(ctx, sub, pos, o, project4, q) {
21623
21799
  projectId: project4
21624
21800
  });
21625
21801
  if (!body.name) return usage5(ctx, "types create --name <n> [--fields <json>]");
21626
- const res = await requestJson(ctx, `${BASE13}/types`, { method: "POST", body });
21802
+ const res = await requestJson(ctx, `${BASE14}/types`, { method: "POST", body });
21627
21803
  return renderDone(ctx, res.body, `Created entity type ${res.body?.name ?? body.name}.`);
21628
21804
  }
21629
21805
  case "update": {
@@ -21698,7 +21874,7 @@ async function types(ctx, sub, pos, o, project4, q) {
21698
21874
  async function records(ctx, sub, pos, o, _project, q) {
21699
21875
  const [type, id] = pos;
21700
21876
  if (!type) return usage5(ctx, `${sub} <type>${sub === "list" || sub === "create" ? "" : " <entityId>"}`);
21701
- const coll = `${BASE13}/types/${enc2(type)}/entities`;
21877
+ const coll = `${BASE14}/types/${enc2(type)}/entities`;
21702
21878
  const recordBody = () => mergeBody(ctx, o, {
21703
21879
  values: o.values !== void 0 ? parseJsonFlag("--values", o.values) : void 0,
21704
21880
  termIds: csv(o.termIds),
@@ -21743,7 +21919,7 @@ async function records(ctx, sub, pos, o, _project, q) {
21743
21919
  async function taxonomies(ctx, sub, pos, o, project4, q) {
21744
21920
  switch (sub) {
21745
21921
  case "list": {
21746
- const res = await requestJson(ctx, q(`${BASE13}/taxonomies`));
21922
+ const res = await requestJson(ctx, q(`${BASE14}/taxonomies`));
21747
21923
  return renderList(ctx, res.body, pickArray(res.body, "taxonomies"), ["name", "displayName"], "No taxonomies.");
21748
21924
  }
21749
21925
  case "create": {
@@ -21753,14 +21929,14 @@ async function taxonomies(ctx, sub, pos, o, project4, q) {
21753
21929
  projectId: project4
21754
21930
  });
21755
21931
  if (!body.name) return usage5(ctx, "taxonomies create --name <n> [--display-name d]");
21756
- const res = await requestJson(ctx, `${BASE13}/taxonomies`, { method: "POST", body });
21932
+ const res = await requestJson(ctx, `${BASE14}/taxonomies`, { method: "POST", body });
21757
21933
  return renderDone(ctx, res.body, `Created taxonomy ${res.body?.name ?? body.name}.`);
21758
21934
  }
21759
21935
  case "delete": {
21760
21936
  const name = pos[0];
21761
21937
  if (!name) return usage5(ctx, "taxonomies delete <taxonomy> --yes");
21762
21938
  confirm(o, `taxonomy ${name}`);
21763
- await requestJson(ctx, q(`${BASE13}/taxonomies/${enc2(name)}`), { method: "DELETE" });
21939
+ await requestJson(ctx, q(`${BASE14}/taxonomies/${enc2(name)}`), { method: "DELETE" });
21764
21940
  writeLine(ctx.io.stdout, `Deleted taxonomy ${name}.`);
21765
21941
  return 0;
21766
21942
  }
@@ -21771,7 +21947,7 @@ async function taxonomies(ctx, sub, pos, o, project4, q) {
21771
21947
  async function terms(ctx, sub, pos, o, project4, q) {
21772
21948
  const [tax, slug] = pos;
21773
21949
  if (!tax) return usage5(ctx, `terms ${sub} <taxonomy>`);
21774
- const coll = `${BASE13}/taxonomies/${enc2(tax)}/terms`;
21950
+ const coll = `${BASE14}/taxonomies/${enc2(tax)}/terms`;
21775
21951
  switch (sub) {
21776
21952
  case "list": {
21777
21953
  const res = await requestJson(ctx, q(coll));
@@ -21823,7 +21999,7 @@ async function terms(ctx, sub, pos, o, project4, q) {
21823
21999
  async function relationships(ctx, sub, pos, o, project4, q) {
21824
22000
  switch (sub) {
21825
22001
  case "list": {
21826
- const res = await requestJson(ctx, q(`${BASE13}/relationships`));
22002
+ const res = await requestJson(ctx, q(`${BASE14}/relationships`));
21827
22003
  return renderList(ctx, res.body, pickArray(res.body, "relationships"), ["fromTypeName", "toTypeName", "cardinality", "onDelete"], "No relationships.");
21828
22004
  }
21829
22005
  case "create": {
@@ -21835,14 +22011,14 @@ async function relationships(ctx, sub, pos, o, project4, q) {
21835
22011
  projectId: project4
21836
22012
  });
21837
22013
  if (!body.fromTypeName || !body.toTypeName) return usage5(ctx, "relationships create --from <type> --to <type>");
21838
- const res = await requestJson(ctx, `${BASE13}/relationships`, { method: "POST", body });
22014
+ const res = await requestJson(ctx, `${BASE14}/relationships`, { method: "POST", body });
21839
22015
  return renderDone(ctx, res.body, `Created relationship ${body.fromTypeName} \u2192 ${body.toTypeName}.`);
21840
22016
  }
21841
22017
  case "delete": {
21842
22018
  const [from, to] = pos;
21843
22019
  if (!from || !to) return usage5(ctx, "relationships delete <fromType> <toType> --yes");
21844
22020
  confirm(o, `relationship ${from} \u2192 ${to}`);
21845
- await requestJson(ctx, q(`${BASE13}/relationships/${enc2(from)}/${enc2(to)}`), { method: "DELETE" });
22021
+ await requestJson(ctx, q(`${BASE14}/relationships/${enc2(from)}/${enc2(to)}`), { method: "DELETE" });
21846
22022
  writeLine(ctx.io.stdout, `Deleted relationship ${from} \u2192 ${to}.`);
21847
22023
  return 0;
21848
22024
  }
@@ -22430,7 +22606,7 @@ Run \`openwop production --help\` for usage.`);
22430
22606
  }
22431
22607
 
22432
22608
  // src/cli/tutorials.ts
22433
- var BASE14 = "/v1/host/openwop-app/tutorials";
22609
+ var BASE15 = "/v1/host/openwop-app/tutorials";
22434
22610
  var TUTORIALS_HELP = `Usage:
22435
22611
  openwop tutorials list [--json]
22436
22612
  openwop tutorials get <slug> [--json]
@@ -22438,9 +22614,9 @@ var TUTORIALS_HELP = `Usage:
22438
22614
  openwop tutorials progress set <tutorialId> --steps <id,id,...> [--json]
22439
22615
  openwop tutorials progress clear <tutorialId> [--json]
22440
22616
 
22441
- The tutorial library (GET ${BASE14}) and one tutorial in full (GET ${BASE14}/<slug>).
22442
- \`progress\` reads YOUR saved steps (GET ${BASE14}/progress); \`progress set\` replaces a
22443
- tutorial's completed steps and \`progress clear\` resets them (POST ${BASE14}/progress,
22617
+ The tutorial library (GET ${BASE15}) and one tutorial in full (GET ${BASE15}/<slug>).
22618
+ \`progress\` reads YOUR saved steps (GET ${BASE15}/progress); \`progress set\` replaces a
22619
+ tutorial's completed steps and \`progress clear\` resets them (POST ${BASE15}/progress,
22444
22620
  body { tutorialId, completedStepIds, mode? }). The server stamps the user \u2014 you can
22445
22621
  only write your own progress; anonymous sessions cannot save.
22446
22622
 
@@ -22464,7 +22640,7 @@ async function runTutorials(ctx, argv) {
22464
22640
  }
22465
22641
  switch (sub) {
22466
22642
  case "list": {
22467
- const res = await requestJson(ctx, BASE14);
22643
+ const res = await requestJson(ctx, BASE15);
22468
22644
  const items = Array.isArray(res.body?.tutorials) ? res.body.tutorials : [];
22469
22645
  const code = renderList(ctx, res.body, items, ["id", "category", "title", "source"], "No tutorials.");
22470
22646
  if (!ctx.json && res.body?.degraded) writeLine(ctx.io.stdout, "(shipped copy \u2014 the tenant library was unreachable)");
@@ -22476,13 +22652,13 @@ async function runTutorials(ctx, argv) {
22476
22652
  write(ctx.io.stderr, "Usage: openwop tutorials get <slug>\n");
22477
22653
  return 2;
22478
22654
  }
22479
- writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE14}/${enc2(slug)}`)).body);
22655
+ writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE15}/${enc2(slug)}`)).body);
22480
22656
  return 0;
22481
22657
  }
22482
22658
  case "progress": {
22483
22659
  const action = positionals[0];
22484
22660
  if (!action) {
22485
- const res2 = await requestJson(ctx, `${BASE14}/progress`);
22661
+ const res2 = await requestJson(ctx, `${BASE15}/progress`);
22486
22662
  const items = Array.isArray(res2.body?.progress) ? res2.body.progress : [];
22487
22663
  const code = renderList(
22488
22664
  ctx,
@@ -22505,7 +22681,7 @@ async function runTutorials(ctx, argv) {
22505
22681
  return 2;
22506
22682
  }
22507
22683
  const body = action === "set" ? { tutorialId, completedStepIds: csv(options.steps) ?? [] } : { tutorialId, completedStepIds: [], mode: "clear" };
22508
- const res = await requestJson(ctx, `${BASE14}/progress`, { method: "POST", body });
22684
+ const res = await requestJson(ctx, `${BASE15}/progress`, { method: "POST", body });
22509
22685
  return renderDone(ctx, res.body, action === "set" ? `Saved ${Array.isArray(res.body?.completedStepIds) ? res.body.completedStepIds.length : body.completedStepIds.length} completed step(s) for ${tutorialId}.` : `Cleared progress for ${tutorialId}.`);
22510
22686
  }
22511
22687
  default:
@@ -22515,16 +22691,16 @@ Run \`openwop tutorials --help\` for usage.`);
22515
22691
  }
22516
22692
 
22517
22693
  // src/cli/walkthroughs.ts
22518
- var BASE15 = "/v1/host/openwop-app/walkthroughs";
22694
+ var BASE16 = "/v1/host/openwop-app/walkthroughs";
22519
22695
  var WALKTHROUGHS_HELP = `Usage:
22520
22696
  openwop walkthroughs progress [--json]
22521
22697
  openwop walkthroughs progress set <walkthroughId> --status started|completed --run <runId> [--json]
22522
22698
  openwop walkthroughs funnel <walkthroughId> [--json]
22523
22699
 
22524
- \`progress\` reads your walkthrough progress (GET ${BASE15}/progress); \`progress set\`
22525
- records one (POST ${BASE15}/progress, body { walkthroughId, status, runId }).
22700
+ \`progress\` reads your walkthrough progress (GET ${BASE16}/progress); \`progress set\`
22701
+ records one (POST ${BASE16}/progress, body { walkthroughId, status, runId }).
22526
22702
  \`funnel\` shows run-status counts + where runs stalled for one walkthrough
22527
- (GET ${BASE15}/funnel?walkthroughId=\u2026) \u2014 a recent-window view over the newest runs.
22703
+ (GET ${BASE16}/funnel?walkthroughId=\u2026) \u2014 a recent-window view over the newest runs.
22528
22704
 
22529
22705
  Exit codes: 0 ok \xB7 2 usage / validation / feature off \xB7 4 forbidden.
22530
22706
 
@@ -22552,10 +22728,10 @@ async function runWalkthroughs(ctx, argv) {
22552
22728
  write(ctx.io.stderr, "Usage: openwop walkthroughs progress set <walkthroughId> --status started|completed --run <runId>\n");
22553
22729
  return 2;
22554
22730
  }
22555
- const res2 = await requestJson(ctx, `${BASE15}/progress`, { method: "POST", body: { walkthroughId, status: String(options.status), runId: String(options.run) } });
22731
+ const res2 = await requestJson(ctx, `${BASE16}/progress`, { method: "POST", body: { walkthroughId, status: String(options.status), runId: String(options.run) } });
22556
22732
  return renderDone(ctx, res2.body, `Recorded ${walkthroughId} as ${String(options.status)}.`);
22557
22733
  }
22558
- const res = await requestJson(ctx, `${BASE15}/progress`);
22734
+ const res = await requestJson(ctx, `${BASE16}/progress`);
22559
22735
  const items = Array.isArray(res.body?.progress) ? res.body.progress : [];
22560
22736
  return renderList(ctx, res.body, items, ["walkthroughId", "status", "runId", "updatedAt"], "No walkthrough progress.");
22561
22737
  }
@@ -22565,7 +22741,7 @@ async function runWalkthroughs(ctx, argv) {
22565
22741
  write(ctx.io.stderr, "Usage: openwop walkthroughs funnel <walkthroughId>\n");
22566
22742
  return 2;
22567
22743
  }
22568
- const res = await requestJson(ctx, withQuery(`${BASE15}/funnel`, { walkthroughId }));
22744
+ const res = await requestJson(ctx, withQuery(`${BASE16}/funnel`, { walkthroughId }));
22569
22745
  if (ctx.json) {
22570
22746
  writeJson(ctx.io.stdout, res.body);
22571
22747
  return 0;
@@ -22586,7 +22762,7 @@ Run \`openwop walkthroughs --help\` for usage.`);
22586
22762
  }
22587
22763
 
22588
22764
  // src/cli/widgets.ts
22589
- var BASE16 = "/v1/host/openwop-app/widgets";
22765
+ var BASE17 = "/v1/host/openwop-app/widgets";
22590
22766
  var WIDGETS_HELP = `Usage:
22591
22767
  openwop widgets list [--json]
22592
22768
  openwop widgets summary [--json]
@@ -22594,8 +22770,8 @@ var WIDGETS_HELP = `Usage:
22594
22770
  openwop widgets archive <widgetId> [--json]
22595
22771
  openwop widgets seed [--json]
22596
22772
 
22597
- The reference example domain (GET/POST ${BASE16}, GET ${BASE16}/summary,
22598
- POST ${BASE16}/<id>/archive, POST ${BASE16}/seed). Mounted only when the host sets
22773
+ The reference example domain (GET/POST ${BASE17}, GET ${BASE17}/summary,
22774
+ POST ${BASE17}/<id>/archive, POST ${BASE17}/seed). Mounted only when the host sets
22599
22775
  OPENWOP_EXAMPLE_WIDGETS_ENABLED=true. \`seed\` is idempotent (\`seeded: false\` on a
22600
22776
  re-run). Archiving an already-archived widget is a conflict (exit 2), never a
22601
22777
  silent success.
@@ -22621,12 +22797,12 @@ async function runWidgets(ctx, argv) {
22621
22797
  try {
22622
22798
  switch (sub) {
22623
22799
  case "list": {
22624
- const res = await requestJson(ctx, BASE16);
22800
+ const res = await requestJson(ctx, BASE17);
22625
22801
  const items = Array.isArray(res.body?.widgets) ? res.body.widgets : [];
22626
22802
  return renderList(ctx, res.body, items, ["id", "name", "status"], "No widgets.");
22627
22803
  }
22628
22804
  case "summary": {
22629
- const res = await requestJson(ctx, `${BASE16}/summary`);
22805
+ const res = await requestJson(ctx, `${BASE17}/summary`);
22630
22806
  if (ctx.json) {
22631
22807
  writeJson(ctx.io.stdout, res.body);
22632
22808
  return 0;
@@ -22640,7 +22816,7 @@ async function runWidgets(ctx, argv) {
22640
22816
  write(ctx.io.stderr, "widgets create needs --name.\n");
22641
22817
  return 2;
22642
22818
  }
22643
- const res = await requestJson(ctx, BASE16, { method: "POST", body: { name: String(options.name) } });
22819
+ const res = await requestJson(ctx, BASE17, { method: "POST", body: { name: String(options.name) } });
22644
22820
  return renderDone(ctx, res.body, `Created widget ${res.body?.id ?? ""} (${String(options.name)}).`);
22645
22821
  }
22646
22822
  case "archive": {
@@ -22649,11 +22825,11 @@ async function runWidgets(ctx, argv) {
22649
22825
  write(ctx.io.stderr, "Usage: openwop widgets archive <widgetId>\n");
22650
22826
  return 2;
22651
22827
  }
22652
- const res = await requestJson(ctx, `${BASE16}/${enc2(id)}/archive`, { method: "POST", body: {} });
22828
+ const res = await requestJson(ctx, `${BASE17}/${enc2(id)}/archive`, { method: "POST", body: {} });
22653
22829
  return renderDone(ctx, res.body, `Archived widget ${id}.`);
22654
22830
  }
22655
22831
  case "seed": {
22656
- const res = await requestJson(ctx, `${BASE16}/seed`, { method: "POST", body: {} });
22832
+ const res = await requestJson(ctx, `${BASE17}/seed`, { method: "POST", body: {} });
22657
22833
  return renderDone(ctx, res.body, res.body?.seeded === false ? "Example widgets already seeded (no-op)." : "Seeded example widgets.");
22658
22834
  }
22659
22835
  default:
@@ -22673,17 +22849,17 @@ Run \`openwop widgets --help\` for usage.`);
22673
22849
  }
22674
22850
 
22675
22851
  // src/cli/uiState.ts
22676
- var BASE17 = "/v1/host/openwop-app/ui-state";
22852
+ var BASE18 = "/v1/host/openwop-app/ui-state";
22677
22853
  var UI_STATE_HELP = `Usage:
22678
22854
  openwop ui-state list [--resource-type <t>] [--resource-id <id>] [--json]
22679
22855
  openwop ui-state get <resourceType> <resourceId> <key> [--json]
22680
22856
  openwop ui-state set <resourceType> <resourceId> <key> --value <json> [--json]
22681
22857
  openwop ui-state delete <resourceType> <resourceId> <key>
22682
22858
 
22683
- \`list\` reads your rows (GET ${BASE17}?resourceType=&resourceId=); \`get\` filters one
22684
- key from that list. \`set\` writes one key (PUT ${BASE17}, body { resourceType,
22859
+ \`list\` reads your rows (GET ${BASE18}?resourceType=&resourceId=); \`get\` filters one
22860
+ key from that list. \`set\` writes one key (PUT ${BASE18}, body { resourceType,
22685
22861
  resourceId, key, value }); --value is parsed as JSON, falling back to a string.
22686
- \`delete\` removes one key (DELETE ${BASE17}/<type>/<id>/<key>).
22862
+ \`delete\` removes one key (DELETE ${BASE18}/<type>/<id>/<key>).
22687
22863
 
22688
22864
  Exit codes: 0 ok \xB7 2 usage / not found \xB7 4 forbidden.
22689
22865
 
@@ -22706,7 +22882,7 @@ async function runUiState(ctx, argv) {
22706
22882
  const [resourceType, resourceId, key] = positionals;
22707
22883
  switch (sub) {
22708
22884
  case "list": {
22709
- const res = await requestJson(ctx, withQuery(BASE17, { resourceType: options.resourceType, resourceId: options.resourceId }));
22885
+ const res = await requestJson(ctx, withQuery(BASE18, { resourceType: options.resourceType, resourceId: options.resourceId }));
22710
22886
  const items = Array.isArray(res.body?.items) ? res.body.items : [];
22711
22887
  return renderList(ctx, res.body, items, ["resourceType", "resourceId", "key", "value"], "No saved UI state.");
22712
22888
  }
@@ -22715,7 +22891,7 @@ async function runUiState(ctx, argv) {
22715
22891
  write(ctx.io.stderr, "Usage: openwop ui-state get <resourceType> <resourceId> <key>\n");
22716
22892
  return 2;
22717
22893
  }
22718
- const res = await requestJson(ctx, withQuery(BASE17, { resourceType, resourceId }));
22894
+ const res = await requestJson(ctx, withQuery(BASE18, { resourceType, resourceId }));
22719
22895
  const items = Array.isArray(res.body?.items) ? res.body.items : [];
22720
22896
  const hit = items.find((i) => i?.key === key);
22721
22897
  if (!hit) throw new CliError(`No ui-state for ${resourceType}/${resourceId}/${key}.`, 2);
@@ -22734,7 +22910,7 @@ async function runUiState(ctx, argv) {
22734
22910
  } catch {
22735
22911
  value = String(options.value);
22736
22912
  }
22737
- const res = await requestJson(ctx, BASE17, { method: "PUT", body: { resourceType, resourceId, key, value } });
22913
+ const res = await requestJson(ctx, BASE18, { method: "PUT", body: { resourceType, resourceId, key, value } });
22738
22914
  return renderDone(ctx, res.body, `Saved ${resourceType}/${resourceId}/${key}.`);
22739
22915
  }
22740
22916
  case "delete": {
@@ -22742,7 +22918,7 @@ async function runUiState(ctx, argv) {
22742
22918
  write(ctx.io.stderr, "Usage: openwop ui-state delete <resourceType> <resourceId> <key>\n");
22743
22919
  return 2;
22744
22920
  }
22745
- await requestJson(ctx, `${BASE17}/${enc2(resourceType)}/${enc2(resourceId)}/${enc2(key)}`, { method: "DELETE" });
22921
+ await requestJson(ctx, `${BASE18}/${enc2(resourceType)}/${enc2(resourceId)}/${enc2(key)}`, { method: "DELETE" });
22746
22922
  writeLine(ctx.io.stdout, `Deleted ${resourceType}/${resourceId}/${key}.`);
22747
22923
  return 0;
22748
22924
  }
@@ -22755,8 +22931,8 @@ Run \`openwop ui-state --help\` for usage.`);
22755
22931
  // src/cli/uiPlugin.ts
22756
22932
  import { writeFileSync as writeFileSync16 } from "node:fs";
22757
22933
  import { resolve as resolvePath11 } from "node:path";
22758
- var BASE18 = "/v1/host/openwop-app/ui-plugin";
22759
- var RPC_PRODUCT = `${BASE18}/rpc`;
22934
+ var BASE19 = "/v1/host/openwop-app/ui-plugin";
22935
+ var RPC_PRODUCT = `${BASE19}/rpc`;
22760
22936
  var RPC_CONFORMANCE = "/v1/host/sample/ui-plugin/rpc";
22761
22937
  var UI_PLUGIN_HELP = `Usage:
22762
22938
  openwop ui-plugin packs [--json]
@@ -22767,11 +22943,11 @@ var UI_PLUGIN_HELP = `Usage:
22767
22943
  openwop ui-plugin rpc --body <json> | --body-file <path>
22768
22944
 
22769
22945
  \`packs\` lists served plugins + the host's isolation mechanism + each plugin's tier
22770
- (GET ${BASE18}/packs). \`entry\` fetches a plugin's sandboxed entry bundle
22771
- (GET ${BASE18}/packs/<name>/plugins/<id>/entry); \`trusted-entry\` fetches a signed
22772
- pack's main-frame ES module (GET ${BASE18}/trusted/<name>/plugins/<id>/entry.mjs \u2014
22946
+ (GET ${BASE19}/packs). \`entry\` fetches a plugin's sandboxed entry bundle
22947
+ (GET ${BASE19}/packs/<name>/plugins/<id>/entry); \`trusted-entry\` fetches a signed
22948
+ pack's main-frame ES module (GET ${BASE19}/trusted/<name>/plugins/<id>/entry.mjs \u2014
22773
22949
  404 unless the trusted-plugins toggle is on and the signature verifies).
22774
- \`demo-artifact\` ensures the idempotent demo artifact (POST ${BASE18}/demo-artifact).
22950
+ \`demo-artifact\` ensures the idempotent demo artifact (POST ${BASE19}/demo-artifact).
22775
22951
  \`rpc\` sends one ui-plugin/1 request (POST ${RPC_PRODUCT}, body { message }); the
22776
22952
  message is { openwop: "ui-plugin/1", type: "request", id (integer,
22777
22953
  default 1), method, params } unless you pass a
@@ -22812,7 +22988,7 @@ async function runUiPlugin(ctx, argv) {
22812
22988
  }
22813
22989
  switch (sub) {
22814
22990
  case "packs": {
22815
- const res = await requestJson(ctx, `${BASE18}/packs`);
22991
+ const res = await requestJson(ctx, `${BASE19}/packs`);
22816
22992
  const items = Array.isArray(res.body?.plugins) ? res.body.plugins : [];
22817
22993
  const code = renderList(ctx, res.body, items, ["packName", "pluginId", "tier"], "No UI plugins served.");
22818
22994
  if (!ctx.json && res.body?.isolation !== void 0) writeLine(ctx.io.stdout, `isolation: ${typeof res.body.isolation === "string" ? res.body.isolation : JSON.stringify(res.body.isolation)}`);
@@ -22826,7 +23002,7 @@ async function runUiPlugin(ctx, argv) {
22826
23002
  `);
22827
23003
  return 2;
22828
23004
  }
22829
- const path = sub === "entry" ? `${BASE18}/packs/${enc2(name)}/plugins/${enc2(pluginId)}/entry` : `${BASE18}/trusted/${enc2(name)}/plugins/${enc2(pluginId)}/entry.mjs`;
23005
+ const path = sub === "entry" ? `${BASE19}/packs/${enc2(name)}/plugins/${enc2(pluginId)}/entry` : `${BASE19}/trusted/${enc2(name)}/plugins/${enc2(pluginId)}/entry.mjs`;
22830
23006
  const res = await fetchRawText(ctx, path);
22831
23007
  if (options.output) {
22832
23008
  writeFileSync16(resolvePath11(ctx.cwd, String(options.output)), res.text);
@@ -22838,7 +23014,7 @@ async function runUiPlugin(ctx, argv) {
22838
23014
  return 0;
22839
23015
  }
22840
23016
  case "demo-artifact": {
22841
- const res = await requestJson(ctx, `${BASE18}/demo-artifact`, { method: "POST", body: {} });
23017
+ const res = await requestJson(ctx, `${BASE19}/demo-artifact`, { method: "POST", body: {} });
22842
23018
  return renderDone(ctx, res.body, `Demo artifact ${res.body?.artifactId ?? ""} (version ${res.body?.version ?? ""}).`);
22843
23019
  }
22844
23020
  case "rpc": {
@@ -22868,18 +23044,18 @@ Run \`openwop ui-plugin --help\` for usage.`);
22868
23044
  }
22869
23045
 
22870
23046
  // src/cli/canvasCollab.ts
22871
- var BASE19 = "/v1/host/openwop-app/canvas-collab";
23047
+ var BASE20 = "/v1/host/openwop-app/canvas-collab";
22872
23048
  var CANVAS_COLLAB_HELP = `Usage:
22873
23049
  openwop canvas-collab ticket <canvasId> [--json]
22874
23050
  openwop canvas-collab claim-seed <canvasId> [--json]
22875
23051
  openwop canvas-collab debug [--json]
22876
23052
 
22877
23053
  \`ticket\` mints a short-lived collaboration ticket for one canvas
22878
- (POST ${BASE19}/<canvasId>/ticket) \u2014 a credential for the collaboration socket's
23054
+ (POST ${BASE20}/<canvasId>/ticket) \u2014 a credential for the collaboration socket's
22879
23055
  ?ticket= parameter; it is printed once and never stored. \`claim-seed\` runs the
22880
- seeder election (POST ${BASE19}/<canvasId>/claim-seed) \u2192 { seed: true } for the one
23056
+ seeder election (POST ${BASE20}/<canvasId>/claim-seed) \u2192 { seed: true } for the one
22881
23057
  caller that should seed a fresh room. \`debug\` lists this server instance's live
22882
- rooms (GET ${BASE19}/_debug; super-admin only; lifecycle metadata, never content).
23058
+ rooms (GET ${BASE20}/_debug; super-admin only; lifecycle metadata, never content).
22883
23059
 
22884
23060
  Exit codes: 0 ok \xB7 2 usage / not found (uniform) \xB7 4 forbidden (debug without super-admin).
22885
23061
 
@@ -22913,7 +23089,7 @@ async function runCanvasCollab(ctx, argv) {
22913
23089
  write(ctx.io.stderr, "Usage: openwop canvas-collab ticket <canvasId>\n");
22914
23090
  return 2;
22915
23091
  }
22916
- const res = await requestJson(ctx, `${BASE19}/${enc2(id)}/ticket`, { method: "POST", body: {} });
23092
+ const res = await requestJson(ctx, `${BASE20}/${enc2(id)}/ticket`, { method: "POST", body: {} });
22917
23093
  return printTicketOnce(ctx, res.body, `canvas ${id}`);
22918
23094
  }
22919
23095
  case "claim-seed": {
@@ -22921,13 +23097,13 @@ async function runCanvasCollab(ctx, argv) {
22921
23097
  write(ctx.io.stderr, "Usage: openwop canvas-collab claim-seed <canvasId>\n");
22922
23098
  return 2;
22923
23099
  }
22924
- const res = await requestJson(ctx, `${BASE19}/${enc2(id)}/claim-seed`, { method: "POST", body: {} });
23100
+ const res = await requestJson(ctx, `${BASE20}/${enc2(id)}/claim-seed`, { method: "POST", body: {} });
22925
23101
  if (ctx.json) writeJson(ctx.io.stdout, res.body);
22926
23102
  else writeLine(ctx.io.stdout, res.body?.seed ? `You won the seed election for ${id} \u2014 seed the room.` : `Another client seeds ${id} (seed: false).`);
22927
23103
  return 0;
22928
23104
  }
22929
23105
  case "debug": {
22930
- const res = await requestJson(ctx, `${BASE19}/_debug`);
23106
+ const res = await requestJson(ctx, `${BASE20}/_debug`);
22931
23107
  const items = Array.isArray(res.body?.instanceRooms) ? res.body.instanceRooms : [];
22932
23108
  return renderList(ctx, res.body, items, ["roomId", "tenantId", "clients", "openedAt"], "No live collaboration rooms on this instance.");
22933
23109
  }
@@ -22938,14 +23114,14 @@ Run \`openwop canvas-collab --help\` for usage.`);
22938
23114
  }
22939
23115
 
22940
23116
  // src/cli/workflowCollab.ts
22941
- var BASE20 = "/v1/host/openwop-app/workflow-collab";
23117
+ var BASE21 = "/v1/host/openwop-app/workflow-collab";
22942
23118
  var WORKFLOW_COLLAB_HELP = `Usage:
22943
23119
  openwop workflow-collab ticket <workflowId> [--json]
22944
23120
  openwop workflow-collab claim-seed <workflowId> [--json]
22945
23121
 
22946
23122
  \`ticket\` mints a short-lived collaboration ticket for one workflow draft
22947
- (POST ${BASE20}/<workflowId>/ticket) \u2014 shown once, never stored. \`claim-seed\` runs
22948
- the seeder election (POST ${BASE20}/<workflowId>/claim-seed) \u2192 { seed }.
23123
+ (POST ${BASE21}/<workflowId>/ticket) \u2014 shown once, never stored. \`claim-seed\` runs
23124
+ the seeder election (POST ${BASE21}/<workflowId>/claim-seed) \u2192 { seed }.
22949
23125
 
22950
23126
  Exit codes: 0 ok \xB7 2 usage / not found / feature off \xB7 4 forbidden.
22951
23127
 
@@ -22972,11 +23148,11 @@ async function runWorkflowCollab(ctx, argv) {
22972
23148
  }
22973
23149
  switch (sub) {
22974
23150
  case "ticket": {
22975
- const res = await requestJson(ctx, `${BASE20}/${enc2(id)}/ticket`, { method: "POST", body: {} });
23151
+ const res = await requestJson(ctx, `${BASE21}/${enc2(id)}/ticket`, { method: "POST", body: {} });
22976
23152
  return printTicketOnce(ctx, res.body, `workflow ${id}`);
22977
23153
  }
22978
23154
  case "claim-seed": {
22979
- const res = await requestJson(ctx, `${BASE20}/${enc2(id)}/claim-seed`, { method: "POST", body: {} });
23155
+ const res = await requestJson(ctx, `${BASE21}/${enc2(id)}/claim-seed`, { method: "POST", body: {} });
22980
23156
  if (ctx.json) writeJson(ctx.io.stdout, res.body);
22981
23157
  else writeLine(ctx.io.stdout, res.body?.seed ? `You won the seed election for ${id} \u2014 seed the room.` : `Another client seeds ${id} (seed: false).`);
22982
23158
  return 0;
@@ -23020,7 +23196,7 @@ Run \`openwop canvas-packs --help\` for usage.`);
23020
23196
  }
23021
23197
 
23022
23198
  // src/cli/present.ts
23023
- var BASE21 = "/v1/host/openwop-app/present";
23199
+ var BASE22 = "/v1/host/openwop-app/present";
23024
23200
  var ACTIONS = ["next", "prev", "goto", "blank"];
23025
23201
  var PRESENT_HELP = `Usage:
23026
23202
  openwop present outline <token> [--json]
@@ -23030,11 +23206,11 @@ var PRESENT_HELP = `Usage:
23030
23206
  openwop present events <token> [--max <n>] [--timeout <seconds>] [--json]
23031
23207
 
23032
23208
  Drive a live presentation with its remote token (no API key \u2014 the token is the
23033
- credential). \`outline\` reads { title, frames, current? } (GET ${BASE21}/<token>/outline).
23034
- \`command\` sends a navigation command (POST ${BASE21}/<token>/command, body
23209
+ credential). \`outline\` reads { title, frames, current? } (GET ${BASE22}/<token>/outline).
23210
+ \`command\` sends a navigation command (POST ${BASE22}/<token>/command, body
23035
23211
  { action, index? }). \`state\` reports the presenter's current frame
23036
- (POST ${BASE21}/<token>/state, body { current }). \`events\` follows the navigation
23037
- feed (GET ${BASE21}/<token>/events, server-sent events) \u2014 one line per event, or one
23212
+ (POST ${BASE22}/<token>/state, body { current }). \`events\` follows the navigation
23213
+ feed (GET ${BASE22}/<token>/events, server-sent events) \u2014 one line per event, or one
23038
23214
  JSON object per line with --json; stops after --max events or --timeout seconds.
23039
23215
 
23040
23216
  Exit codes: 0 ok \xB7 2 usage / unknown or expired session.
@@ -23061,7 +23237,7 @@ async function runPresent(ctx, argv) {
23061
23237
  `);
23062
23238
  return 2;
23063
23239
  }
23064
- const base16 = `${BASE21}/${enc2(token)}`;
23240
+ const base16 = `${BASE22}/${enc2(token)}`;
23065
23241
  switch (sub) {
23066
23242
  case "outline": {
23067
23243
  const res = await requestJson(ctx, `${base16}/outline`, { auth: false });
@@ -24069,8 +24245,8 @@ async function runCampaignBrief(ctx, argv) {
24069
24245
  }
24070
24246
 
24071
24247
  // src/cli/campaignConnectors.ts
24072
- var BASE22 = `${APP}/campaign-connectors`;
24073
- var orgBase2 = (org2) => `${BASE22}/orgs/${enc2(org2)}`;
24248
+ var BASE23 = `${APP}/campaign-connectors`;
24249
+ var orgBase2 = (org2) => `${BASE23}/orgs/${enc2(org2)}`;
24074
24250
  var CAMPAIGN_CONNECTORS_HELP = `Usage:
24075
24251
  openwop campaign-connectors platforms [--json]
24076
24252
  openwop campaign-connectors sync --org <orgId> --platform meta|google [--json]
@@ -24119,7 +24295,7 @@ var TABLE2 = {
24119
24295
  usage: "platforms [--json]",
24120
24296
  args: 0,
24121
24297
  run: async (ctx) => {
24122
- const body = (await requestJson(ctx, `${BASE22}/platforms`)).body;
24298
+ const body = (await requestJson(ctx, `${BASE23}/platforms`)).body;
24123
24299
  if (ctx.json) {
24124
24300
  writeJson(ctx.io.stdout, body);
24125
24301
  return 0;
@@ -24136,7 +24312,7 @@ var TABLE2 = {
24136
24312
  requires: ["platform"],
24137
24313
  run: async (ctx, a) => {
24138
24314
  const req2 = assign({ ...a.body }, { orgId: orgOf(a), platform: a.options.platform });
24139
- const body = (await requestJson(ctx, `${BASE22}/sync`, { method: "POST", body: req2 })).body;
24315
+ const body = (await requestJson(ctx, `${BASE23}/sync`, { method: "POST", body: req2 })).body;
24140
24316
  return done(ctx, body, `Sync ${body?.outcome ?? "done"}${body?.imported !== void 0 ? ` (${body.imported} imported)` : ""}.`);
24141
24317
  }
24142
24318
  },
@@ -24149,7 +24325,7 @@ var TABLE2 = {
24149
24325
  run: async (ctx, a) => {
24150
24326
  const o = a.options;
24151
24327
  const req2 = assign({ ...a.body }, { orgId: orgOf(a), segmentId: o.segment, adAccountId: o.adAccount, platform: o.platform, audienceName: o.audienceName });
24152
- const body = (await requestJson(ctx, `${BASE22}/audience-sync`, { method: "POST", body: req2 })).body;
24328
+ const body = (await requestJson(ctx, `${BASE23}/audience-sync`, { method: "POST", body: req2 })).body;
24153
24329
  return done(ctx, body, `Audience sync: ${body?.outcome ?? "done"}.`);
24154
24330
  }
24155
24331
  },
@@ -24172,7 +24348,7 @@ var TABLE2 = {
24172
24348
  writeLine(ctx.io.stderr, "openwop: missing --csv-file");
24173
24349
  return 2;
24174
24350
  }
24175
- const body = (await requestJson(ctx, `${BASE22}/import`, { method: "POST", body: req2 })).body;
24351
+ const body = (await requestJson(ctx, `${BASE23}/import`, { method: "POST", body: req2 })).body;
24176
24352
  return done(ctx, body, `Imported ${body?.imported ?? 0} rows (${body?.deduped ?? 0} deduped, ${Array.isArray(body?.invalid) ? body.invalid.length : body?.invalid ?? 0} invalid).`);
24177
24353
  }
24178
24354
  },
@@ -24182,7 +24358,7 @@ var TABLE2 = {
24182
24358
  value: ["--org", "--campaign"],
24183
24359
  run: async (ctx, a) => listOut(
24184
24360
  ctx,
24185
- (await requestJson(ctx, `${BASE22}/records${qs({ orgId: orgOf(a), campaignId: a.options.campaign })}`)).body,
24361
+ (await requestJson(ctx, `${BASE23}/records${qs({ orgId: orgOf(a), campaignId: a.options.campaign })}`)).body,
24186
24362
  "records",
24187
24363
  ["date", "platform", "campaignId", "impressions", "clicks", "spend", "conversions"],
24188
24364
  "No performance records."
@@ -24192,7 +24368,7 @@ var TABLE2 = {
24192
24368
  usage: "kpi --org <orgId> [--campaign <id>] [--json]",
24193
24369
  args: 0,
24194
24370
  value: ["--org", "--campaign"],
24195
- run: async (ctx, a) => detail(ctx, (await requestJson(ctx, `${BASE22}/kpi${qs({ orgId: orgOf(a), campaignId: a.options.campaign })}`)).body)
24371
+ run: async (ctx, a) => detail(ctx, (await requestJson(ctx, `${BASE23}/kpi${qs({ orgId: orgOf(a), campaignId: a.options.campaign })}`)).body)
24196
24372
  },
24197
24373
  "sync-status": {
24198
24374
  usage: "sync-status --org <orgId> [--json]",
@@ -24314,7 +24490,7 @@ async function runCampaignConnectors(ctx, argv) {
24314
24490
  }
24315
24491
 
24316
24492
  // src/cli/campaignIntel.ts
24317
- var BASE23 = `${APP}/campaign-intel`;
24493
+ var BASE24 = `${APP}/campaign-intel`;
24318
24494
  var CAMPAIGN_INTEL_HELP = `Usage:
24319
24495
  openwop campaign-intel budget --org <orgId> [--campaign <id>] [--json]
24320
24496
  openwop campaign-intel forecast --org <orgId> [--campaign <id>] [--json]
@@ -24353,7 +24529,7 @@ var read = (name, withCampaign) => ({
24353
24529
  args: 0,
24354
24530
  value: ["--org", ...withCampaign ? ["--campaign"] : []],
24355
24531
  run: async (ctx, a) => {
24356
- const body = (await requestJson(ctx, `${BASE23}/${name}${qs({ orgId: orgOf2(a), campaignId: withCampaign ? a.options.campaign : void 0 })}`)).body;
24532
+ const body = (await requestJson(ctx, `${BASE24}/${name}${qs({ orgId: orgOf2(a), campaignId: withCampaign ? a.options.campaign : void 0 })}`)).body;
24357
24533
  if (name === "anomalies") return listOut(ctx, body, "anomalies", ["date", "platform", "campaignId", "metric", "severity", "message"], "No anomalies.");
24358
24534
  if (name === "forecast") return listOut(ctx, body, "forecasts", ["campaignId", "platform", "horizonDays", "projectedSpend", "projectedConversions"], "No forecasts (no performance records).");
24359
24535
  return detail(ctx, body);
@@ -24388,7 +24564,7 @@ var TABLE3 = {
24388
24564
  return 2;
24389
24565
  }
24390
24566
  }
24391
- return detail(ctx, (await requestJson(ctx, `${BASE23}/plan-budget`, { method: "POST", body: req2 })).body);
24567
+ return detail(ctx, (await requestJson(ctx, `${BASE24}/plan-budget`, { method: "POST", body: req2 })).body);
24392
24568
  }
24393
24569
  },
24394
24570
  apply: {
@@ -24413,7 +24589,7 @@ var TABLE3 = {
24413
24589
  dailyBudgetMinor: o.dailyBudgetMinor !== void 0 ? num(o.dailyBudgetMinor, "--daily-budget-minor") : void 0,
24414
24590
  dryRun: dryRun ? true : void 0
24415
24591
  });
24416
- const body = (await requestJson(ctx, `${BASE23}/recommendations/apply`, { method: "POST", body: req2 })).body;
24592
+ const body = (await requestJson(ctx, `${BASE24}/recommendations/apply`, { method: "POST", body: req2 })).body;
24417
24593
  return done(ctx, body, `Budget ${dryRun ? "preview" : "update"}: ${body?.outcome ?? "done"}.`);
24418
24594
  }
24419
24595
  }
@@ -24473,7 +24649,7 @@ async function runCampaignJourneys(ctx, argv) {
24473
24649
  }
24474
24650
 
24475
24651
  // src/cli/cdp.ts
24476
- var BASE24 = `${APP}/cdp`;
24652
+ var BASE25 = `${APP}/cdp`;
24477
24653
  var CDP_HELP = `Usage:
24478
24654
  openwop cdp resolve --type <identifierType> --value <v> [--json]
24479
24655
  openwop cdp schemas list [--json]
@@ -24520,7 +24696,7 @@ var limited = (path, key, columns, empty) => ({
24520
24696
  usage: `${path === "governance-decisions" ? "governance" : path === "collected-events" ? "events" : path} [--limit <n>] [--json]`,
24521
24697
  args: 0,
24522
24698
  value: ["--limit"],
24523
- run: async (ctx, a) => listOut(ctx, (await requestJson(ctx, `${BASE24}/${path}${qs({ limit: a.options.limit })}`)).body, key, columns, empty)
24699
+ run: async (ctx, a) => listOut(ctx, (await requestJson(ctx, `${BASE25}/${path}${qs({ limit: a.options.limit })}`)).body, key, columns, empty)
24524
24700
  });
24525
24701
  var TABLE5 = {
24526
24702
  resolve: {
@@ -24528,7 +24704,7 @@ var TABLE5 = {
24528
24704
  args: 0,
24529
24705
  value: ["--type", "--value"],
24530
24706
  requires: ["type", "value"],
24531
- run: async (ctx, a) => detail(ctx, (await requestJson(ctx, `${BASE24}/identity/resolve${qs({ type: a.options.type, value: a.options.value })}`)).body)
24707
+ run: async (ctx, a) => detail(ctx, (await requestJson(ctx, `${BASE25}/identity/resolve${qs({ type: a.options.type, value: a.options.value })}`)).body)
24532
24708
  },
24533
24709
  collect: {
24534
24710
  usage: "collect --event-type <t> [--payload-json '{...}'] [--dedupe-key <k>] [--json]",
@@ -24543,7 +24719,7 @@ var TABLE5 = {
24543
24719
  payload: o.payloadJson !== void 0 ? parseJsonFlag2(o.payloadJson, "--payload-json") : void 0,
24544
24720
  dedupeKey: o.dedupeKey
24545
24721
  });
24546
- const body = (await requestJson(ctx, `${BASE24}/collect`, { method: "POST", body: req2 })).body;
24722
+ const body = (await requestJson(ctx, `${BASE25}/collect`, { method: "POST", body: req2 })).body;
24547
24723
  return done(ctx, body, `Collected ${req2.eventType} event ${body?.eventId ?? body?.event?.eventId ?? ""}${body?.deduped ? " (deduplicated)" : ""}.`);
24548
24724
  }
24549
24725
  },
@@ -24558,7 +24734,7 @@ var TABLE5 = {
24558
24734
  writeLine(ctx.io.stderr, "openwop: missing --events-json or --events-file");
24559
24735
  return 2;
24560
24736
  }
24561
- return detail(ctx, (await requestJson(ctx, `${BASE24}/collect/batch`, { method: "POST", body: req2 })).body);
24737
+ return detail(ctx, (await requestJson(ctx, `${BASE25}/collect/batch`, { method: "POST", body: req2 })).body);
24562
24738
  }
24563
24739
  },
24564
24740
  import: {
@@ -24578,7 +24754,7 @@ var TABLE5 = {
24578
24754
  writeLine(ctx.io.stderr, "openwop: missing --csv-file");
24579
24755
  return 2;
24580
24756
  }
24581
- return detail(ctx, (await requestJson(ctx, `${BASE24}/collect/import`, { method: "POST", body: req2 })).body);
24757
+ return detail(ctx, (await requestJson(ctx, `${BASE25}/collect/import`, { method: "POST", body: req2 })).body);
24582
24758
  }
24583
24759
  },
24584
24760
  events: limited("collected-events", "events", ["eventId", "eventType", ["at", (e) => e.at ?? e.receivedAt ?? e.createdAt], "dedupeKey"], "No collected events."),
@@ -24588,7 +24764,7 @@ var TABLE5 = {
24588
24764
  usage: "audit-verify [--json]",
24589
24765
  args: 0,
24590
24766
  run: async (ctx) => {
24591
- const body = (await requestJson(ctx, `${BASE24}/audit-chain/verify`)).body;
24767
+ const body = (await requestJson(ctx, `${BASE25}/audit-chain/verify`)).body;
24592
24768
  if (ctx.json) {
24593
24769
  writeJson(ctx.io.stdout, body);
24594
24770
  return body?.valid === false || body?.ok === false ? 1 : 0;
@@ -24606,7 +24782,7 @@ var SCHEMAS = {
24606
24782
  args: 0,
24607
24783
  run: async (ctx) => listOut(
24608
24784
  ctx,
24609
- (await requestJson(ctx, `${BASE24}/event-schemas`)).body,
24785
+ (await requestJson(ctx, `${BASE25}/event-schemas`)).body,
24610
24786
  "schemas",
24611
24787
  ["eventType", "version", ["updatedAt", (s) => s.updatedAt ?? s.registeredAt ?? s.createdAt]],
24612
24788
  "No event schemas registered."
@@ -24627,7 +24803,7 @@ var SCHEMAS = {
24627
24803
  writeLine(ctx.io.stderr, "openwop: missing --schema-json or --schema-file");
24628
24804
  return 2;
24629
24805
  }
24630
- const body = (await requestJson(ctx, `${BASE24}/event-schemas`, { method: "POST", body: req2 })).body;
24806
+ const body = (await requestJson(ctx, `${BASE25}/event-schemas`, { method: "POST", body: req2 })).body;
24631
24807
  return done(ctx, body, `Registered the ${req2.eventType} event schema${body?.version !== void 0 ? ` (version ${body.version})` : ""}.`);
24632
24808
  }
24633
24809
  },
@@ -24640,7 +24816,7 @@ var SCHEMAS = {
24640
24816
  const req2 = assign({ ...a.body }, { payload: jsonFrom(ctx, a.options.payloadJson, a.options.payloadFile, "--payload-json", "--payload-file") });
24641
24817
  let body;
24642
24818
  try {
24643
- body = (await requestJson(ctx, `${BASE24}/event-schemas/${enc2(a.positionals[0])}/validate`, { method: "POST", body: req2 })).body;
24819
+ body = (await requestJson(ctx, `${BASE25}/event-schemas/${enc2(a.positionals[0])}/validate`, { method: "POST", body: req2 })).body;
24644
24820
  } catch (err) {
24645
24821
  if (!(err instanceof HttpError) || err.status !== 422) throw err;
24646
24822
  body = err.body;
@@ -24664,7 +24840,7 @@ async function runCdp(ctx, argv) {
24664
24840
  }
24665
24841
 
24666
24842
  // src/cli/destinationSync.ts
24667
- var BASE25 = `${APP}/destination-sync/syncs`;
24843
+ var BASE26 = `${APP}/destination-sync/syncs`;
24668
24844
  var DESTINATION_SYNC_HELP = `Usage:
24669
24845
  openwop destination-sync list [--json]
24670
24846
  openwop destination-sync get <syncId> [--json]
@@ -24698,14 +24874,14 @@ function jsonFrom2(ctx, inline, file, inlineFlag, fileFlag) {
24698
24874
  if (file !== void 0) return parseJsonFlag2(readText(ctx, String(file), fileFlag), fileFlag);
24699
24875
  return void 0;
24700
24876
  }
24701
- var item = (id) => `${BASE25}/${enc2(id)}`;
24877
+ var item = (id) => `${BASE26}/${enc2(id)}`;
24702
24878
  var TABLE6 = {
24703
24879
  list: {
24704
24880
  usage: "list [--json]",
24705
24881
  args: 0,
24706
24882
  run: async (ctx) => listOut(
24707
24883
  ctx,
24708
- (await requestJson(ctx, BASE25)).body,
24884
+ (await requestJson(ctx, BASE26)).body,
24709
24885
  "syncs",
24710
24886
  [["id", (s) => s.id ?? s.syncId], "name", "destinationKind", "sourceObject", "syncMode", "cursor"],
24711
24887
  "No destination syncs."
@@ -24733,7 +24909,7 @@ var TABLE6 = {
24733
24909
  cursorField: o.cursorField,
24734
24910
  connectionId: o.connection
24735
24911
  });
24736
- const body = (await requestJson(ctx, BASE25, { method: "POST", body: req2 })).body;
24912
+ const body = (await requestJson(ctx, BASE26, { method: "POST", body: req2 })).body;
24737
24913
  return done(ctx, body, `Created destination sync ${body?.id ?? body?.syncId ?? ""} (${req2.name}).`);
24738
24914
  }
24739
24915
  },
@@ -25563,7 +25739,7 @@ async function runPublic(ctx, argv) {
25563
25739
  }
25564
25740
 
25565
25741
  // src/cli/vault.ts
25566
- var BASE26 = "/v1/host/openwop-app/vault";
25742
+ var BASE27 = "/v1/host/openwop-app/vault";
25567
25743
  var SURFACE2 = "The secrets vault";
25568
25744
  var VAULT_HELP = `Usage:
25569
25745
  openwop vault list [--json]
@@ -25575,10 +25751,10 @@ The super-admin secrets vault (host extension). Every route is SUPER-ADMIN gated
25575
25751
  without a super-admin principal the command fails closed with exit 4 and says how to
25576
25752
  get one (OPENWOP_SUPERADMIN_TENANTS on the server).
25577
25753
 
25578
- list GET ${BASE26} inventory of refs (never values)
25579
- set POST ${BASE26}/secrets store a new secret under a ref
25580
- rotate POST ${BASE26}/secrets/:credentialRef/rotate overwrite an EXISTING secret
25581
- delete DELETE ${BASE26}/secrets/:credentialRef remove (refused while the ref has live consumers unless --force)
25754
+ list GET ${BASE27} inventory of refs (never values)
25755
+ set POST ${BASE27}/secrets store a new secret under a ref
25756
+ rotate POST ${BASE27}/secrets/:credentialRef/rotate overwrite an EXISTING secret
25757
+ delete DELETE ${BASE27}/secrets/:credentialRef remove (refused while the ref has live consumers unless --force)
25582
25758
 
25583
25759
  SECRETS ARE REFS, NEVER VALUES. The value for set/rotate is read from --value-file or a
25584
25760
  no-echo prompt (a piped line also works) \u2014 never from a command-line flag. The host's
@@ -25634,7 +25810,7 @@ async function vaultList(ctx, argv) {
25634
25810
  write(ctx.io.stdout, VAULT_HELP);
25635
25811
  return 0;
25636
25812
  }
25637
- const res = await gatedRequest(ctx, BASE26, void 0, SURFACE2, "superadmin");
25813
+ const res = await gatedRequest(ctx, BASE27, void 0, SURFACE2, "superadmin");
25638
25814
  if (ctx.json) {
25639
25815
  writeJson(ctx.io.stdout, res.body);
25640
25816
  return 0;
@@ -25665,7 +25841,7 @@ async function vaultWrite(ctx, argv, verb) {
25665
25841
  const scope = scopeOf(options);
25666
25842
  const value = await readSecretInput(ctx, options, `Secret value for "${ref}"`);
25667
25843
  const body = { value, ...scope === "host" ? { scope: "host" } : {} };
25668
- const path = verb === "set" ? `${BASE26}/secrets` : `${BASE26}/secrets/${encodeURIComponent(ref)}/rotate`;
25844
+ const path = verb === "set" ? `${BASE27}/secrets` : `${BASE27}/secrets/${encodeURIComponent(ref)}/rotate`;
25669
25845
  if (verb === "set") body.credentialRef = ref;
25670
25846
  const res = await gatedRequest(ctx, path, { method: "POST", body }, SURFACE2, "superadmin");
25671
25847
  if (ctx.json) {
@@ -25689,7 +25865,7 @@ async function vaultDelete(ctx, argv) {
25689
25865
  if (scopeOf(options) === "host") qs2.set("scope", "host");
25690
25866
  if (options.force) qs2.set("force", "true");
25691
25867
  const q = qs2.toString() ? `?${qs2}` : "";
25692
- const res = await gatedRequest(ctx, `${BASE26}/secrets/${encodeURIComponent(positionals[0])}${q}`, { method: "DELETE" }, SURFACE2, "superadmin");
25868
+ const res = await gatedRequest(ctx, `${BASE27}/secrets/${encodeURIComponent(positionals[0])}${q}`, { method: "DELETE" }, SURFACE2, "superadmin");
25693
25869
  if (ctx.json) {
25694
25870
  writeJson(ctx.io.stdout, res.body);
25695
25871
  return 0;
@@ -25699,27 +25875,40 @@ async function vaultDelete(ctx, argv) {
25699
25875
  }
25700
25876
 
25701
25877
  // src/cli/developerKeys.ts
25702
- var BASE27 = "/v1/host/openwop-app/developer-keys";
25878
+ var BASE28 = "/v1/host/openwop-app/developer-keys";
25703
25879
  var SURFACE3 = "Managing developer API keys";
25704
25880
  var DEVELOPER_KEYS_HELP = `Usage:
25705
25881
  openwop developer-keys list [--json]
25706
25882
  openwop developer-keys create --name <n> [--scope <s>]... [--expires-at <iso>] [--json]
25707
25883
  openwop developer-keys revoke <keyId> --yes [--json]
25708
25884
 
25709
- Developer API keys for this workspace (host extension under ${BASE27}). Requires an
25885
+ Developer API keys for this workspace (host extension under ${BASE28}). Requires an
25710
25886
  authenticated principal (not an anonymous session); an admin or owner sees and can
25711
25887
  revoke every key in the workspace, anyone else only their own.
25712
25888
 
25713
- list GET ${BASE27} keys (public fields only \u2014 never the token or its hash)
25714
- create POST ${BASE27} issue a key; the token is shown ONCE
25715
- revoke DELETE ${BASE27}/:keyId revoke (404 when the key is not yours / not in this workspace)
25889
+ list GET ${BASE28} keys (public fields only \u2014 never the token or its hash)
25890
+ create POST ${BASE28} issue a key; the token is shown ONCE
25891
+ revoke DELETE ${BASE28}/:keyId revoke (404 when the key is not yours / not in this workspace)
25716
25892
 
25717
25893
  ONE-TIME SECRET: \`create\` prints the new token exactly once, on stdout, with a warning.
25718
25894
  Store it immediately \u2014 the host keeps only a hash and cannot show it again. The CLI does
25719
25895
  not save it anywhere.
25720
25896
 
25897
+ A KEY CANNOT MANAGE KEYS: openwop-app refuses \`list\`, \`create\` and \`revoke\` (403)
25898
+ when the CLI is itself signed in with a developer key (owk_\u2026). Create and revoke keys
25899
+ in the app (Access \u2192 API keys), or sign in with an operator key.
25900
+
25901
+ SCOPES ARE REQUIRED AND NARROW: openwop-app refuses a key with no --scope, and a key is
25902
+ limited to exactly the scopes it declares. To start a run and read it back a key needs
25903
+ runs:create AND runs:read; reaching the rest of the workspace (agents, memory, BYOK)
25904
+ needs workspace:read / workspace:write. \`--scope '*'\` is a key with your full authority.
25905
+ A scope the host does not know is refused by name.
25906
+
25907
+ EXPIRY: openwop-app gives a key 90 days unless --expires-at says otherwise, and refuses
25908
+ an expiry in the past or more than its maximum (366 days by default) away.
25909
+
25721
25910
  --name <n> (create) Display name. Required.
25722
- --scope <s> (create) Scope granted to the key (repeatable).
25911
+ --scope <s> (create) Scope granted to the key (repeatable; at least one).
25723
25912
  --expires-at <iso> (create) Expiry timestamp.
25724
25913
  --yes (revoke) Required confirmation.
25725
25914
 
@@ -25727,7 +25916,7 @@ Exit codes: 0 ok \xB7 2 usage / not found \xB7 4 not signed in.
25727
25916
 
25728
25917
  Examples:
25729
25918
  openwop developer-keys list
25730
- openwop developer-keys create --name "CI deploy" --scope runs:write
25919
+ openwop developer-keys create --name "CI deploy" --scope runs:create --scope runs:read
25731
25920
  openwop developer-keys revoke dk:0123abcd --yes
25732
25921
  `;
25733
25922
  async function runDeveloperKeys(ctx, argv) {
@@ -25756,7 +25945,7 @@ async function keysList(ctx, argv) {
25756
25945
  write(ctx.io.stdout, DEVELOPER_KEYS_HELP);
25757
25946
  return 0;
25758
25947
  }
25759
- const res = await gatedRequest(ctx, BASE27, void 0, SURFACE3, "signed-in");
25948
+ const res = await gatedRequest(ctx, BASE28, void 0, SURFACE3, "signed-in");
25760
25949
  if (ctx.json) {
25761
25950
  writeJson(ctx.io.stdout, res.body);
25762
25951
  return 0;
@@ -25785,7 +25974,7 @@ async function keysCreate(ctx, argv) {
25785
25974
  const body = { name: options.name };
25786
25975
  if (Array.isArray(options.scope) && options.scope.length) body.scopes = options.scope;
25787
25976
  if (options.expiresAt) body.expiresAt = options.expiresAt;
25788
- const res = await gatedRequest(ctx, BASE27, { method: "POST", body }, SURFACE3, "signed-in");
25977
+ const res = await gatedRequest(ctx, BASE28, { method: "POST", body }, SURFACE3, "signed-in");
25789
25978
  writeLine(ctx.io.stderr, "WARNING: this token is shown ONCE. Store it now \u2014 it cannot be retrieved again.");
25790
25979
  if (ctx.json) {
25791
25980
  writeJson(ctx.io.stdout, res.body);
@@ -25806,7 +25995,7 @@ async function keysRevoke(ctx, argv) {
25806
25995
  writeLine(ctx.io.stderr, `Refusing to revoke ${positionals[0]} without --yes.`);
25807
25996
  return 2;
25808
25997
  }
25809
- await gatedRequest(ctx, `${BASE27}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" }, SURFACE3, "signed-in");
25998
+ await gatedRequest(ctx, `${BASE28}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" }, SURFACE3, "signed-in");
25810
25999
  if (ctx.json) {
25811
26000
  writeJson(ctx.io.stdout, { keyId: positionals[0], revoked: true });
25812
26001
  return 0;
@@ -25875,44 +26064,44 @@ Run \`openwop custom-domains --help\` for usage.`);
25875
26064
  })), ["hostname", "status", "verifiedAt", "lastError"]));
25876
26065
  return 0;
25877
26066
  }
25878
- const hostname = positionals[0];
25879
- if (!hostname) throw new CliError(`Usage: openwop custom-domains ${sub} <hostname> --org <orgId>`, 2);
26067
+ const hostname2 = positionals[0];
26068
+ if (!hostname2) throw new CliError(`Usage: openwop custom-domains ${sub} <hostname> --org <orgId>`, 2);
25880
26069
  if (sub === "add") {
25881
- const res2 = await requestJson(ctx, base14(orgId), { method: "POST", body: { hostname } });
26070
+ const res2 = await requestJson(ctx, base14(orgId), { method: "POST", body: { hostname: hostname2 } });
25882
26071
  if (ctx.json) {
25883
26072
  writeJson(ctx.io.stdout, res2.body);
25884
26073
  return 0;
25885
26074
  }
25886
26075
  const d = res2.body?.domain ?? {};
25887
- writeLine(ctx.io.stdout, `Added ${d.hostname ?? hostname} (status ${d.status ?? "pending"}).`);
25888
- writeLine(ctx.io.stdout, `Publish TXT _openwop-verify.${d.hostname ?? hostname} = ${d.verificationToken ?? "(see --json)"} then run \`openwop custom-domains verify ${d.hostname ?? hostname} --org ${orgId}\`.`);
26076
+ writeLine(ctx.io.stdout, `Added ${d.hostname ?? hostname2} (status ${d.status ?? "pending"}).`);
26077
+ writeLine(ctx.io.stdout, `Publish TXT _openwop-verify.${d.hostname ?? hostname2} = ${d.verificationToken ?? "(see --json)"} then run \`openwop custom-domains verify ${d.hostname ?? hostname2} --org ${orgId}\`.`);
25889
26078
  return 0;
25890
26079
  }
25891
26080
  if (sub === "verify") {
25892
- const res2 = await requestJson(ctx, `${base14(orgId)}/${encodeURIComponent(hostname)}/verify`, { method: "POST", body: {} });
26081
+ const res2 = await requestJson(ctx, `${base14(orgId)}/${encodeURIComponent(hostname2)}/verify`, { method: "POST", body: {} });
25893
26082
  if (ctx.json) {
25894
26083
  writeJson(ctx.io.stdout, res2.body);
25895
26084
  return 0;
25896
26085
  }
25897
26086
  const d = res2.body?.domain ?? {};
25898
- writeLine(ctx.io.stdout, `${d.hostname ?? hostname}: ${d.status ?? "?"}${d.lastError ? ` \u2014 ${d.lastError}` : ""}`);
26087
+ writeLine(ctx.io.stdout, `${d.hostname ?? hostname2}: ${d.status ?? "?"}${d.lastError ? ` \u2014 ${d.lastError}` : ""}`);
25899
26088
  return d.status === "live" ? 0 : 3;
25900
26089
  }
25901
26090
  if (!options.yes) {
25902
- writeLine(ctx.io.stderr, `Refusing to remove ${hostname} without --yes.`);
26091
+ writeLine(ctx.io.stderr, `Refusing to remove ${hostname2} without --yes.`);
25903
26092
  return 2;
25904
26093
  }
25905
- const res = await requestJson(ctx, `${base14(orgId)}/${encodeURIComponent(hostname)}`, { method: "DELETE" });
26094
+ const res = await requestJson(ctx, `${base14(orgId)}/${encodeURIComponent(hostname2)}`, { method: "DELETE" });
25906
26095
  if (ctx.json) {
25907
26096
  writeJson(ctx.io.stdout, res.body);
25908
26097
  return 0;
25909
26098
  }
25910
- writeLine(ctx.io.stdout, `Removed ${hostname}.`);
26099
+ writeLine(ctx.io.stdout, `Removed ${hostname2}.`);
25911
26100
  return 0;
25912
26101
  }
25913
26102
 
25914
26103
  // src/cli/environments.ts
25915
- var BASE28 = "/v1/host/openwop-app/environments";
26104
+ var BASE29 = "/v1/host/openwop-app/environments";
25916
26105
  var PROTECTION = ["open", "protected", "locked"];
25917
26106
  var ENVIRONMENTS_HELP = `Usage:
25918
26107
  openwop environments list [--drift] [--json]
@@ -25929,21 +26118,21 @@ var ENVIRONMENTS_HELP = `Usage:
25929
26118
  openwop environments apply --snapshot <hash> [--json]
25930
26119
  openwop environments promotions [--json]
25931
26120
 
25932
- Config environments + promotion (host extension under ${BASE28}; toggle 'environments').
26121
+ Config environments + promotion (host extension under ${BASE29}; toggle 'environments').
25933
26122
  Reads need workspace:read; every write needs host:members:manage in the workspace.
25934
26123
 
25935
- list GET ${BASE28}[?drift=1] environments, config domains, app version
25936
- create POST ${BASE28} add an environment to the chain
25937
- ensure-chain POST ${BASE28}/ensure-chain create the default dev \u2192 staging \u2192 prod chain
25938
- protect PATCH ${BASE28}/:name/protection set open | protected | locked
25939
- settings GET/PATCH ${BASE28}/settings the promotion-approval gate
25940
- snapshots GET ${BASE28}/snapshots stored config snapshots
25941
- snapshot POST ${BASE28}/snapshots snapshot the current live config
25942
- preview POST ${BASE28}/preview diff a snapshot against an environment (no change)
25943
- promote POST ${BASE28}/promote promote an env's snapshot to the next (or --to) env
25944
- rollback POST ${BASE28}/rollback point an env back at a historical snapshot
25945
- apply POST ${BASE28}/apply apply a snapshot to the live config
25946
- promotions GET ${BASE28}/promotions the promotion ledger
26124
+ list GET ${BASE29}[?drift=1] environments, config domains, app version
26125
+ create POST ${BASE29} add an environment to the chain
26126
+ ensure-chain POST ${BASE29}/ensure-chain create the default dev \u2192 staging \u2192 prod chain
26127
+ protect PATCH ${BASE29}/:name/protection set open | protected | locked
26128
+ settings GET/PATCH ${BASE29}/settings the promotion-approval gate
26129
+ snapshots GET ${BASE29}/snapshots stored config snapshots
26130
+ snapshot POST ${BASE29}/snapshots snapshot the current live config
26131
+ preview POST ${BASE29}/preview diff a snapshot against an environment (no change)
26132
+ promote POST ${BASE29}/promote promote an env's snapshot to the next (or --to) env
26133
+ rollback POST ${BASE29}/rollback point an env back at a historical snapshot
26134
+ apply POST ${BASE29}/apply apply a snapshot to the live config
26135
+ promotions GET ${BASE29}/promotions the promotion ledger
25947
26136
 
25948
26137
  Exit codes: 0 ok \xB7 3 queued for approval (202 pending_approval) \xB7 2 usage / not found /
25949
26138
  feature off \xB7 4 permission denied.
@@ -25976,7 +26165,7 @@ async function runEnvironments(ctx, argv) {
25976
26165
  };
25977
26166
  switch (sub) {
25978
26167
  case "list": {
25979
- const res = await requestJson(ctx, `${BASE28}${options.drift ? "?drift=1" : ""}`);
26168
+ const res = await requestJson(ctx, `${BASE29}${options.drift ? "?drift=1" : ""}`);
25980
26169
  emit2(res.body, () => {
25981
26170
  const envs = arrayOf(res.body, "environments");
25982
26171
  if (envs.length === 0) {
@@ -26003,35 +26192,35 @@ async function runEnvironments(ctx, argv) {
26003
26192
  body.order = n;
26004
26193
  }
26005
26194
  if (options.protection !== void 0) body.protection = protectionOf(options.protection);
26006
- const res = await requestJson(ctx, BASE28, { method: "POST", body });
26195
+ const res = await requestJson(ctx, BASE29, { method: "POST", body });
26007
26196
  emit2(res.body, () => writeLine(ctx.io.stdout, `Created environment ${res.body?.name ?? name} (order ${res.body?.order ?? "?"}, ${res.body?.protection ?? "?"}).`));
26008
26197
  return 0;
26009
26198
  }
26010
26199
  case "ensure-chain": {
26011
- const res = await requestJson(ctx, `${BASE28}/ensure-chain`, { method: "POST", body: {} });
26200
+ const res = await requestJson(ctx, `${BASE29}/ensure-chain`, { method: "POST", body: {} });
26012
26201
  emit2(res.body, () => writeLine(ctx.io.stdout, `Chain: ${arrayOf(res.body, "environments").map((e) => e.name).join(" \u2192 ")}`));
26013
26202
  return 0;
26014
26203
  }
26015
26204
  case "protect": {
26016
26205
  const name = positionals[0];
26017
26206
  if (!name || options.protection === void 0) throw new CliError("Usage: openwop environments protect <name> --protection open|protected|locked", 2);
26018
- const res = await requestJson(ctx, `${BASE28}/${encodeURIComponent(name)}/protection`, { method: "PATCH", body: { protection: protectionOf(options.protection) } });
26207
+ const res = await requestJson(ctx, `${BASE29}/${encodeURIComponent(name)}/protection`, { method: "PATCH", body: { protection: protectionOf(options.protection) } });
26019
26208
  emit2(res.body, () => writeLine(ctx.io.stdout, `${name} is now ${res.body?.protection ?? options.protection}.`));
26020
26209
  return 0;
26021
26210
  }
26022
26211
  case "settings": {
26023
26212
  if (positionals[0] === "set") {
26024
26213
  if (options.requireApproval === void 0) throw new CliError("Usage: openwop environments settings set --require-approval true|false", 2);
26025
- const res2 = await requestJson(ctx, `${BASE28}/settings`, { method: "PATCH", body: { requireApprovalForPromotion: parseBool("--require-approval", options.requireApproval) } });
26214
+ const res2 = await requestJson(ctx, `${BASE29}/settings`, { method: "PATCH", body: { requireApprovalForPromotion: parseBool("--require-approval", options.requireApproval) } });
26026
26215
  emit2(res2.body, () => writeLine(ctx.io.stdout, `requireApprovalForPromotion: ${res2.body?.requireApprovalForPromotion}`));
26027
26216
  return 0;
26028
26217
  }
26029
- const res = await requestJson(ctx, `${BASE28}/settings`);
26218
+ const res = await requestJson(ctx, `${BASE29}/settings`);
26030
26219
  emit2(res.body, () => writeLine(ctx.io.stdout, `requireApprovalForPromotion: ${res.body?.requireApprovalForPromotion ?? false}`));
26031
26220
  return 0;
26032
26221
  }
26033
26222
  case "snapshots": {
26034
- const res = await requestJson(ctx, `${BASE28}/snapshots`);
26223
+ const res = await requestJson(ctx, `${BASE29}/snapshots`);
26035
26224
  emit2(res.body, () => {
26036
26225
  const snaps = arrayOf(res.body, "snapshots");
26037
26226
  if (snaps.length === 0) {
@@ -26051,13 +26240,13 @@ async function runEnvironments(ctx, argv) {
26051
26240
  case "snapshot": {
26052
26241
  const body = {};
26053
26242
  if (options.sourceEnv) body.sourceEnv = options.sourceEnv;
26054
- const res = await requestJson(ctx, `${BASE28}/snapshots`, { method: "POST", body });
26243
+ const res = await requestJson(ctx, `${BASE29}/snapshots`, { method: "POST", body });
26055
26244
  emit2(res.body, () => writeLine(ctx.io.stdout, `Snapshot ${res.body?.hash ?? "?"}${res.body?.sourceEnv ? ` (from ${res.body.sourceEnv})` : ""}.`));
26056
26245
  return 0;
26057
26246
  }
26058
26247
  case "preview": {
26059
26248
  if (!options.to || !options.snapshot) throw new CliError("Usage: openwop environments preview --to <env> --snapshot <hash>", 2);
26060
- const res = await requestJson(ctx, `${BASE28}/preview`, { method: "POST", body: { toEnv: options.to, snapshotHash: options.snapshot } });
26249
+ const res = await requestJson(ctx, `${BASE29}/preview`, { method: "POST", body: { toEnv: options.to, snapshotHash: options.snapshot } });
26061
26250
  writeJson(ctx.io.stdout, res.body);
26062
26251
  return 0;
26063
26252
  }
@@ -26065,18 +26254,18 @@ async function runEnvironments(ctx, argv) {
26065
26254
  if (!options.from) throw new CliError("Usage: openwop environments promote --from <env> [--to <env>]", 2);
26066
26255
  const body = { fromEnv: options.from };
26067
26256
  if (options.to) body.toEnv = options.to;
26068
- return movement(ctx, await requestJson(ctx, `${BASE28}/promote`, { method: "POST", body }), "Promoted");
26257
+ return movement(ctx, await requestJson(ctx, `${BASE29}/promote`, { method: "POST", body }), "Promoted");
26069
26258
  }
26070
26259
  case "rollback": {
26071
26260
  if (!options.env || !options.snapshot) throw new CliError("Usage: openwop environments rollback --env <env> --snapshot <hash>", 2);
26072
- return movement(ctx, await requestJson(ctx, `${BASE28}/rollback`, { method: "POST", body: { env: options.env, snapshotHash: options.snapshot } }), "Rolled back");
26261
+ return movement(ctx, await requestJson(ctx, `${BASE29}/rollback`, { method: "POST", body: { env: options.env, snapshotHash: options.snapshot } }), "Rolled back");
26073
26262
  }
26074
26263
  case "apply": {
26075
26264
  if (!options.snapshot) throw new CliError("Usage: openwop environments apply --snapshot <hash>", 2);
26076
- return movement(ctx, await requestJson(ctx, `${BASE28}/apply`, { method: "POST", body: { snapshotHash: options.snapshot } }), "Applied");
26265
+ return movement(ctx, await requestJson(ctx, `${BASE29}/apply`, { method: "POST", body: { snapshotHash: options.snapshot } }), "Applied");
26077
26266
  }
26078
26267
  case "promotions": {
26079
- const res = await requestJson(ctx, `${BASE28}/promotions`);
26268
+ const res = await requestJson(ctx, `${BASE29}/promotions`);
26080
26269
  emit2(res.body, () => {
26081
26270
  const rows = arrayOf(res.body, "promotions");
26082
26271
  if (rows.length === 0) {
@@ -26121,7 +26310,7 @@ function movement(ctx, res, verb) {
26121
26310
  }
26122
26311
 
26123
26312
  // src/cli/billing.ts
26124
- var BASE29 = "/v1/host/openwop-app/billing";
26313
+ var BASE30 = "/v1/host/openwop-app/billing";
26125
26314
  var BILLING_HELP = `Usage:
26126
26315
  openwop billing subscription [--json]
26127
26316
  openwop billing balance [--json]
@@ -26139,7 +26328,7 @@ var BILLING_HELP = `Usage:
26139
26328
  openwop billing sync-seats [--json]
26140
26329
  openwop billing import (--body <json> | --body-file <path>) [--json]
26141
26330
 
26142
- Workspace billing (host extension under ${BASE29}; toggle 'billing').
26331
+ Workspace billing (host extension under ${BASE30}; toggle 'billing').
26143
26332
  subscription GET /subscription plan tier, status, seats, period
26144
26333
  balance GET /balance purchased token balance
26145
26334
  entitlements GET /entitlements the plan's allowed features + limits
@@ -26186,7 +26375,7 @@ async function runBilling(ctx, argv) {
26186
26375
  const superadmin = (path, body, surface) => gatedRequest(ctx, path, { method: "POST", body }, surface, "superadmin");
26187
26376
  switch (sub) {
26188
26377
  case "subscription": {
26189
- const res = await requestJson(ctx, `${BASE29}/subscription`);
26378
+ const res = await requestJson(ctx, `${BASE30}/subscription`);
26190
26379
  emit2(res.body, () => {
26191
26380
  const s = res.body ?? {};
26192
26381
  writeLine(ctx.io.stdout, `plan: ${s.planTier ?? "?"} (${s.status ?? "?"})`);
@@ -26197,12 +26386,12 @@ async function runBilling(ctx, argv) {
26197
26386
  return 0;
26198
26387
  }
26199
26388
  case "balance": {
26200
- const res = await requestJson(ctx, `${BASE29}/balance`);
26389
+ const res = await requestJson(ctx, `${BASE30}/balance`);
26201
26390
  emit2(res.body, () => writeLine(ctx.io.stdout, `tokens available: ${res.body?.totalAvailable ?? 0} (purchased total ${res.body?.purchasedTokensTotal ?? 0})`));
26202
26391
  return 0;
26203
26392
  }
26204
26393
  case "entitlements": {
26205
- const res = await requestJson(ctx, `${BASE29}/entitlements`);
26394
+ const res = await requestJson(ctx, `${BASE30}/entitlements`);
26206
26395
  emit2(res.body, () => {
26207
26396
  const e = res.body ?? {};
26208
26397
  writeLine(ctx.io.stdout, `plan: ${e.plan ?? "?"}`);
@@ -26213,7 +26402,7 @@ async function runBilling(ctx, argv) {
26213
26402
  return 0;
26214
26403
  }
26215
26404
  case "bundles": {
26216
- const res = await requestJson(ctx, `${BASE29}/bundles`);
26405
+ const res = await requestJson(ctx, `${BASE30}/bundles`);
26217
26406
  emit2(res.body, () => {
26218
26407
  const rows = arrayOf(res.body, "bundles");
26219
26408
  if (rows.length === 0) {
@@ -26233,19 +26422,19 @@ async function runBilling(ctx, argv) {
26233
26422
  const verb = positionals[0] ?? "list";
26234
26423
  if (verb === "get") {
26235
26424
  if (!positionals[1]) throw new CliError("Usage: openwop billing invoices get <invoiceId>", 2);
26236
- const res2 = await requestJson(ctx, `${BASE29}/invoices/${encodeURIComponent(positionals[1])}`);
26425
+ const res2 = await requestJson(ctx, `${BASE30}/invoices/${encodeURIComponent(positionals[1])}`);
26237
26426
  emit2(res2.body, () => writeLine(ctx.io.stdout, res2.body?.markdown ?? JSON.stringify(res2.body, null, 2)));
26238
26427
  return 0;
26239
26428
  }
26240
26429
  if (verb === "create") {
26241
26430
  const amount = Number(options.amount);
26242
26431
  if (options.amount === void 0 || !Number.isFinite(amount)) throw new CliError("Usage: openwop billing invoices create --amount <n> [--currency <c>]", 2);
26243
- const res2 = await superadmin(`${BASE29}/invoices`, { amount, ...options.currency ? { currency: options.currency } : {} }, "Generating an invoice");
26432
+ const res2 = await superadmin(`${BASE30}/invoices`, { amount, ...options.currency ? { currency: options.currency } : {} }, "Generating an invoice");
26244
26433
  emit2(res2.body, () => writeLine(ctx.io.stdout, `Created invoice ${res2.body?.invoiceId ?? "?"} (${res2.body?.amount} ${res2.body?.currency}).`));
26245
26434
  return 0;
26246
26435
  }
26247
26436
  if (verb !== "list") throw new CliError(`Unknown invoices verb: ${verb}`, 2);
26248
- const res = await requestJson(ctx, `${BASE29}/invoices`);
26437
+ const res = await requestJson(ctx, `${BASE30}/invoices`);
26249
26438
  emit2(res.body, () => {
26250
26439
  const rows = arrayOf(res.body, "invoices");
26251
26440
  if (rows.length === 0) {
@@ -26263,18 +26452,18 @@ async function runBilling(ctx, argv) {
26263
26452
  }
26264
26453
  case "checkout": {
26265
26454
  if (!options.price) throw new CliError("Usage: openwop billing checkout --price <priceId>", 2);
26266
- const res = await requestJson(ctx, `${BASE29}/checkout`, { method: "POST", body: { priceId: options.price } });
26455
+ const res = await requestJson(ctx, `${BASE30}/checkout`, { method: "POST", body: { priceId: options.price } });
26267
26456
  emit2(res.body, () => writeLine(ctx.io.stdout, `Open to complete checkout (${res.body?.mode ?? "?"}): ${res.body?.url ?? "?"}`));
26268
26457
  return 0;
26269
26458
  }
26270
26459
  case "bundle-checkout": {
26271
26460
  if (!positionals[0]) throw new CliError("Usage: openwop billing bundle-checkout <bundleId>", 2);
26272
- const res = await requestJson(ctx, `${BASE29}/bundles/${encodeURIComponent(positionals[0])}/checkout`, { method: "POST", body: {} });
26461
+ const res = await requestJson(ctx, `${BASE30}/bundles/${encodeURIComponent(positionals[0])}/checkout`, { method: "POST", body: {} });
26273
26462
  emit2(res.body, () => writeLine(ctx.io.stdout, `Open to complete checkout (${res.body?.mode ?? "?"}): ${res.body?.url ?? "?"}`));
26274
26463
  return 0;
26275
26464
  }
26276
26465
  case "portal": {
26277
- const res = await requestJson(ctx, `${BASE29}/portal`, { method: "POST", body: {} });
26466
+ const res = await requestJson(ctx, `${BASE30}/portal`, { method: "POST", body: {} });
26278
26467
  emit2(res.body, () => writeLine(ctx.io.stdout, `Billing portal (${res.body?.mode ?? "?"}): ${res.body?.url ?? "?"}`));
26279
26468
  return 0;
26280
26469
  }
@@ -26287,19 +26476,19 @@ async function runBilling(ctx, argv) {
26287
26476
  if (!Number.isFinite(v)) throw new CliError("--value must be a number.", 2);
26288
26477
  body.value = v;
26289
26478
  }
26290
- const res = await superadmin(`${BASE29}/coupons`, body, "Creating a billing coupon");
26479
+ const res = await superadmin(`${BASE30}/coupons`, body, "Creating a billing coupon");
26291
26480
  emit2(res.body, () => writeLine(ctx.io.stdout, `Created coupon ${res.body?.code ?? options.code} (${res.body?.type} ${res.body?.value}).`));
26292
26481
  return 0;
26293
26482
  }
26294
26483
  case "sync-seats": {
26295
- const res = await superadmin(`${BASE29}/sync-seats`, {}, "Billing seat sync");
26484
+ const res = await superadmin(`${BASE30}/sync-seats`, {}, "Billing seat sync");
26296
26485
  emit2(res.body, () => writeLine(ctx.io.stdout, `Seats synced: ${res.body?.quantity ?? "?"}.`));
26297
26486
  return 0;
26298
26487
  }
26299
26488
  case "import": {
26300
26489
  const body = readBodyOption(ctx, options);
26301
26490
  if (!body) throw new CliError('Usage: openwop billing import (--body <json> | --body-file <path>) \u2014 {"subscriptions":[...],"balances":[...]}', 2);
26302
- const res = await superadmin(`${BASE29}/import`, body, "Billing import");
26491
+ const res = await superadmin(`${BASE30}/import`, body, "Billing import");
26303
26492
  emit2(res.body, () => writeLine(ctx.io.stdout, `Imported ${res.body?.subscriptions ?? 0} subscription(s), ${res.body?.balances ?? 0} balance(s).`));
26304
26493
  return 0;
26305
26494
  }
@@ -26370,15 +26559,15 @@ Run \`openwop site-config --help\` for usage.`);
26370
26559
  }
26371
26560
 
26372
26561
  // src/cli/runtimePosture.ts
26373
- var BASE30 = "/v1/host/openwop-app/runtime-posture";
26562
+ var BASE31 = "/v1/host/openwop-app/runtime-posture";
26374
26563
  var SURFACE5 = "Runtime posture";
26375
26564
  var RUNTIME_POSTURE_HELP = `Usage:
26376
26565
  openwop runtime-posture [get] [--json]
26377
26566
  openwop runtime-posture request --warm|--cold [--json]
26378
26567
 
26379
26568
  The serving service's runtime posture (host extension, SUPER-ADMIN gated).
26380
- get GET ${BASE30} live posture (serving revision, warm/cold, rollout)
26381
- request POST ${BASE30}/change-requests ask for a warm/cold change; returns the commands to run
26569
+ get GET ${BASE31} live posture (serving revision, warm/cold, rollout)
26570
+ request POST ${BASE31}/change-requests ask for a warm/cold change; returns the commands to run
26382
26571
 
26383
26572
  A change request APPLIES NOTHING: the host records it in the audit log and returns the
26384
26573
  exact commands for an operator holding deploy rights. Without a super-admin principal
@@ -26402,7 +26591,7 @@ async function runRuntimePosture(ctx, argv) {
26402
26591
  return 0;
26403
26592
  }
26404
26593
  if (sub === "get") {
26405
- const res2 = await gatedRequest(ctx, BASE30, void 0, SURFACE5, "superadmin");
26594
+ const res2 = await gatedRequest(ctx, BASE31, void 0, SURFACE5, "superadmin");
26406
26595
  if (ctx.json) {
26407
26596
  writeJson(ctx.io.stdout, res2.body);
26408
26597
  return 0;
@@ -26421,7 +26610,7 @@ async function runRuntimePosture(ctx, argv) {
26421
26610
  if (sub !== "request") throw new CliError(`Unknown runtime-posture command: ${sub}
26422
26611
  Run \`openwop runtime-posture --help\` for usage.`);
26423
26612
  if (Boolean(options.warm) === Boolean(options.cold)) throw new CliError("Pass exactly one of --warm or --cold.", 2);
26424
- const res = await gatedRequest(ctx, `${BASE30}/change-requests`, { method: "POST", body: { warm: Boolean(options.warm) } }, SURFACE5, "superadmin");
26613
+ const res = await gatedRequest(ctx, `${BASE31}/change-requests`, { method: "POST", body: { warm: Boolean(options.warm) } }, SURFACE5, "superadmin");
26425
26614
  if (ctx.json) {
26426
26615
  writeJson(ctx.io.stdout, res.body);
26427
26616
  return 0;
@@ -26476,16 +26665,16 @@ Run \`openwop maintenance --help\` for usage.`);
26476
26665
  }
26477
26666
 
26478
26667
  // src/cli/menuConfig.ts
26479
- var BASE31 = "/v1/host/openwop-app/menu-config";
26668
+ var BASE32 = "/v1/host/openwop-app/menu-config";
26480
26669
  var MENU_CONFIG_HELP = `Usage:
26481
26670
  openwop menu-config [get] [--json]
26482
26671
  openwop menu-config set me (--body <json> | --body-file <path>) [--json]
26483
26672
  openwop menu-config set tenant (--body <json> | --body-file <path>) [--json]
26484
26673
 
26485
26674
  Navigation menu layout (host extension).
26486
- get GET ${BASE31} {tenant, user} layers for the caller
26487
- set me PUT ${BASE31}/me replace YOUR personal layer (signed-in user)
26488
- set tenant PUT ${BASE31}/tenant [super-admin] replace the workspace default layer
26675
+ get GET ${BASE32} {tenant, user} layers for the caller
26676
+ set me PUT ${BASE32}/me replace YOUR personal layer (signed-in user)
26677
+ set tenant PUT ${BASE32}/tenant [super-admin] replace the workspace default layer
26489
26678
 
26490
26679
  The body is the menu config object ({"items": {...}, "headers": [...]}) or the full
26491
26680
  request ({"config": {...}}). A write REPLACES that layer. \`set tenant\` reads the current
@@ -26511,7 +26700,7 @@ async function runMenuConfig(ctx, argv) {
26511
26700
  return 0;
26512
26701
  }
26513
26702
  if (sub === "get") {
26514
- const res2 = await requestJson(ctx, BASE31);
26703
+ const res2 = await requestJson(ctx, BASE32);
26515
26704
  writeJson(ctx.io.stdout, res2.body);
26516
26705
  return 0;
26517
26706
  }
@@ -26525,13 +26714,13 @@ Run \`openwop menu-config --help\` for usage.`);
26525
26714
  const body = "config" in raw ? raw : { config: raw };
26526
26715
  let res;
26527
26716
  if (layer === "me") {
26528
- res = await gatedRequest(ctx, `${BASE31}/me`, { method: "PUT", body }, "Editing your menu layout", "signed-in");
26717
+ res = await gatedRequest(ctx, `${BASE32}/me`, { method: "PUT", body }, "Editing your menu layout", "signed-in");
26529
26718
  } else {
26530
- const current = await requestJson(ctx, BASE31);
26719
+ const current = await requestJson(ctx, BASE32);
26531
26720
  const etag = current.headers.get("etag");
26532
26721
  res = await gatedRequest(
26533
26722
  ctx,
26534
- `${BASE31}/tenant`,
26723
+ `${BASE32}/tenant`,
26535
26724
  { method: "PUT", body, ...etag ? { headers: { "if-match": etag } } : {} },
26536
26725
  "Editing the workspace default menu layout",
26537
26726
  "superadmin"
@@ -26921,7 +27110,7 @@ function renderKnowledge(ctx, agentId, k) {
26921
27110
 
26922
27111
  // src/cli/compatEndpoints.ts
26923
27112
  import { readFileSync as readFileSync19 } from "node:fs";
26924
- var BASE32 = "/v1/host/openwop-app/compat-endpoints";
27113
+ var BASE33 = "/v1/host/openwop-app/compat-endpoints";
26925
27114
  var COMPAT_ENDPOINTS_HELP = `Usage:
26926
27115
  openwop compat-endpoints list --org <orgId> [--json]
26927
27116
  openwop compat-endpoints create --org <orgId> --label <l> --base-url-endpoint <https://\u2026> [--api-key-file <path> | --api-key-env <VAR>] [--model <m>]... [--vision] [--tools] [--long-context] [--json]
@@ -26971,7 +27160,7 @@ async function runCompatEndpoints(ctx, argv) {
26971
27160
  switch (sub) {
26972
27161
  case "list": {
26973
27162
  const org2 = requireOrg(options.org);
26974
- const res = await requestJson(ctx, `${BASE32}?orgId=${encodeURIComponent(org2)}`);
27163
+ const res = await requestJson(ctx, `${BASE33}?orgId=${encodeURIComponent(org2)}`);
26975
27164
  if (ctx.json) {
26976
27165
  writeJson(ctx.io.stdout, res.body);
26977
27166
  return 0;
@@ -27004,7 +27193,7 @@ async function runCompatEndpoints(ctx, argv) {
27004
27193
  const apiKey = readApiKey(ctx, options);
27005
27194
  if (apiKey) body.apiKey = apiKey;
27006
27195
  if (Array.isArray(options.model) && options.model.length) body.models = options.model;
27007
- const res = await requestJson(ctx, BASE32, { method: "POST", body });
27196
+ const res = await requestJson(ctx, BASE33, { method: "POST", body });
27008
27197
  if (ctx.json) {
27009
27198
  writeJson(ctx.io.stdout, res.body);
27010
27199
  return 0;
@@ -27018,7 +27207,7 @@ async function runCompatEndpoints(ctx, argv) {
27018
27207
  writeLine(ctx.io.stderr, `Refusing to delete ${positionals[0]} (and its stored key) without --yes.`);
27019
27208
  return 2;
27020
27209
  }
27021
- await requestJson(ctx, `${BASE32}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
27210
+ await requestJson(ctx, `${BASE33}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
27022
27211
  writeLine(ctx.io.stdout, `Deleted compat endpoint ${positionals[0]}.`);
27023
27212
  return 0;
27024
27213
  }
@@ -27048,7 +27237,7 @@ function readApiKey(ctx, options) {
27048
27237
  }
27049
27238
 
27050
27239
  // src/cli/hostEvents.ts
27051
- var BASE33 = "/v1/host/openwop-app/host-events/bindings";
27240
+ var BASE34 = "/v1/host/openwop-app/host-events/bindings";
27052
27241
  var HOST_EVENTS_HELP = `Usage:
27053
27242
  openwop host-events list [--json]
27054
27243
  openwop host-events bind --event <host.\u2026> --workflow <workflowId> [--json]
@@ -27091,7 +27280,7 @@ async function runHostEvents(ctx, argv) {
27091
27280
  }
27092
27281
  switch (sub) {
27093
27282
  case "list": {
27094
- const res = await requestJson(ctx, BASE33);
27283
+ const res = await requestJson(ctx, BASE34);
27095
27284
  if (ctx.json) {
27096
27285
  writeJson(ctx.io.stdout, res.body);
27097
27286
  return 0;
@@ -27112,7 +27301,7 @@ async function runHostEvents(ctx, argv) {
27112
27301
  case "bind": {
27113
27302
  if (!options.event || !options.workflow) throw new CliError("bind needs --event <host.\u2026> and --workflow <workflowId>.", 2);
27114
27303
  if (!String(options.event).startsWith("host.")) throw new CliError('--event must be a host-extension event name starting with "host.".', 2);
27115
- const res = await requestJson(ctx, BASE33, { method: "POST", body: { eventType: options.event, workflowId: options.workflow } });
27304
+ const res = await requestJson(ctx, BASE34, { method: "POST", body: { eventType: options.event, workflowId: options.workflow } });
27116
27305
  if (ctx.json) {
27117
27306
  writeJson(ctx.io.stdout, res.body);
27118
27307
  return 0;
@@ -27123,7 +27312,7 @@ async function runHostEvents(ctx, argv) {
27123
27312
  case "enable":
27124
27313
  case "disable": {
27125
27314
  if (positionals.length !== 1) throw new CliError(`Usage: openwop host-events ${sub} <bindingId> [--json]`, 2);
27126
- const res = await requestJson(ctx, `${BASE33}/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body: { enabled: sub === "enable" } });
27315
+ const res = await requestJson(ctx, `${BASE34}/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body: { enabled: sub === "enable" } });
27127
27316
  if (ctx.json) {
27128
27317
  writeJson(ctx.io.stdout, res.body);
27129
27318
  return 0;
@@ -27137,7 +27326,7 @@ async function runHostEvents(ctx, argv) {
27137
27326
  writeLine(ctx.io.stderr, `Refusing to delete binding ${positionals[0]} without --yes.`);
27138
27327
  return 2;
27139
27328
  }
27140
- await requestJson(ctx, `${BASE33}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
27329
+ await requestJson(ctx, `${BASE34}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
27141
27330
  writeLine(ctx.io.stdout, `Deleted binding ${positionals[0]}.`);
27142
27331
  return 0;
27143
27332
  }
@@ -27336,7 +27525,7 @@ Run \`openwop openapi --help\` for usage.`);
27336
27525
  }
27337
27526
 
27338
27527
  // src/cli/assistant.ts
27339
- var BASE34 = "/v1/host/openwop-app/assistant";
27528
+ var BASE35 = "/v1/host/openwop-app/assistant";
27340
27529
  var ASSISTANT_HELP = `Usage:
27341
27530
  openwop assistant workspace [--json]
27342
27531
  openwop assistant briefing [--json]
@@ -27399,7 +27588,7 @@ async function runAssistant(ctx, argv) {
27399
27588
  return await runWorkspace2(ctx, rest);
27400
27589
  case "briefing":
27401
27590
  case "brief":
27402
- return await simpleRead(ctx, rest, `${BASE34}/briefing`, (b) => writeLine(ctx.io.stdout, JSON.stringify(b?.brief ?? b, null, 2)));
27591
+ return await simpleRead(ctx, rest, `${BASE35}/briefing`, (b) => writeLine(ctx.io.stdout, JSON.stringify(b?.brief ?? b, null, 2)));
27403
27592
  case "projects":
27404
27593
  case "project":
27405
27594
  return await runProjects2(ctx, rest);
@@ -27412,7 +27601,7 @@ async function runAssistant(ctx, argv) {
27412
27601
  case "meeting":
27413
27602
  return await runMeetings(ctx, rest);
27414
27603
  case "stakeholders":
27415
- return await simpleRead(ctx, rest, `${BASE34}/stakeholders`, (b) => table2(ctx, b?.stakeholders, (s) => ({
27604
+ return await simpleRead(ctx, rest, `${BASE35}/stakeholders`, (b) => table2(ctx, b?.stakeholders, (s) => ({
27416
27605
  stakeholderId: s.stakeholderId,
27417
27606
  name: s.person?.name ?? s.person?.email ?? "",
27418
27607
  importance: String(s.importance ?? ""),
@@ -27425,7 +27614,7 @@ async function runAssistant(ctx, argv) {
27425
27614
  case "loop":
27426
27615
  return await runLoops(ctx, rest);
27427
27616
  case "health":
27428
- return await simpleRead(ctx, rest, `${BASE34}/health`, (b) => writeLine(ctx.io.stdout, JSON.stringify(b?.health ?? b, null, 2)));
27617
+ return await simpleRead(ctx, rest, `${BASE35}/health`, (b) => writeLine(ctx.io.stdout, JSON.stringify(b?.health ?? b, null, 2)));
27429
27618
  default:
27430
27619
  throw new CliError(`Unknown assistant command: ${sub}
27431
27620
  Run \`openwop assistant --help\` for usage.`, 2);
@@ -27455,7 +27644,7 @@ async function runWorkspace2(ctx, argv) {
27455
27644
  write(ctx.io.stdout, ASSISTANT_HELP);
27456
27645
  return 0;
27457
27646
  }
27458
- const res = await requestJson(ctx, `${BASE34}/workspace-conversation`, { method: "POST", body: {} });
27647
+ const res = await requestJson(ctx, `${BASE35}/workspace-conversation`, { method: "POST", body: {} });
27459
27648
  return emit(ctx, res.body, () => {
27460
27649
  writeLine(ctx.io.stdout, `${res.status === 201 ? "Opened" : "Resumed"} workspace conversation ${res.body?.sessionId}.`);
27461
27650
  writeLine(ctx.io.stdout, `Talk to the assistant: openwop chat messages send ${res.body?.sessionId} --content "\u2026"`);
@@ -27481,7 +27670,7 @@ async function runProjects2(ctx, argv) {
27481
27670
  const id = positionals[0];
27482
27671
  const needId = () => {
27483
27672
  if (!id) throw new CliError(`assistant projects ${sub} requires <projectId>.`, 2);
27484
- return `${BASE34}/projects/${enc(id)}`;
27673
+ return `${BASE35}/projects/${enc(id)}`;
27485
27674
  };
27486
27675
  const fields = () => {
27487
27676
  const body = {};
@@ -27494,7 +27683,7 @@ async function runProjects2(ctx, argv) {
27494
27683
  };
27495
27684
  switch (sub) {
27496
27685
  case "list": {
27497
- const res = await requestJson(ctx, `${BASE34}/projects`);
27686
+ const res = await requestJson(ctx, `${BASE35}/projects`);
27498
27687
  return emit(ctx, res.body, () => table2(ctx, res.body?.projects, projectRow, "No assistant projects. Create one with `openwop assistant projects create --name <n>`."));
27499
27688
  }
27500
27689
  case "get": {
@@ -27515,7 +27704,7 @@ async function runProjects2(ctx, argv) {
27515
27704
  }
27516
27705
  case "create": {
27517
27706
  if (!options.name) throw new CliError("assistant projects create requires --name <n>.", 2);
27518
- const res = await requestJson(ctx, `${BASE34}/projects`, { method: "POST", body: fields() });
27707
+ const res = await requestJson(ctx, `${BASE35}/projects`, { method: "POST", body: fields() });
27519
27708
  return emit(ctx, res.body, () => writeLine(ctx.io.stdout, `Created project ${res.body?.projectId} (${res.body?.name ?? options.name}).`));
27520
27709
  }
27521
27710
  case "update": {
@@ -27562,14 +27751,14 @@ async function runCommitments(ctx, argv) {
27562
27751
  const id = positionals[0];
27563
27752
  const needId = () => {
27564
27753
  if (!id) throw new CliError(`assistant commitments ${sub} requires <commitmentId>.`, 2);
27565
- return `${BASE34}/commitments/${enc(id)}`;
27754
+ return `${BASE35}/commitments/${enc(id)}`;
27566
27755
  };
27567
27756
  switch (sub) {
27568
27757
  case "list": {
27569
27758
  const q = new URLSearchParams();
27570
27759
  if (options.status) q.set("status", options.status);
27571
27760
  if (options.project) q.set("projectId", options.project);
27572
- const res = await requestJson(ctx, `${BASE34}/commitments${q.size ? `?${q}` : ""}`);
27761
+ const res = await requestJson(ctx, `${BASE35}/commitments${q.size ? `?${q}` : ""}`);
27573
27762
  return emit(ctx, res.body, () => table2(ctx, res.body?.commitments, commitmentRow, "No commitments."));
27574
27763
  }
27575
27764
  case "get": {
@@ -27619,7 +27808,7 @@ async function runDecisions(ctx, argv) {
27619
27808
  write(ctx.io.stdout, ASSISTANT_HELP);
27620
27809
  return 0;
27621
27810
  }
27622
- const res = await requestJson(ctx, `${BASE34}/decisions${options.project ? `?projectId=${enc(options.project)}` : ""}`);
27811
+ const res = await requestJson(ctx, `${BASE35}/decisions${options.project ? `?projectId=${enc(options.project)}` : ""}`);
27623
27812
  return emit(ctx, res.body, () => table2(ctx, res.body?.decisions, (d) => ({
27624
27813
  decisionId: d.decisionId,
27625
27814
  decidedAt: d.decidedAt ?? "",
@@ -27636,7 +27825,7 @@ async function runMeetings(ctx, argv) {
27636
27825
  return 0;
27637
27826
  }
27638
27827
  if (sub === "list") {
27639
- const res = await requestJson(ctx, `${BASE34}/meetings`);
27828
+ const res = await requestJson(ctx, `${BASE35}/meetings`);
27640
27829
  return emit(ctx, res.body, () => table2(ctx, res.body?.meetings, (m) => ({
27641
27830
  meetingId: m.meetingId,
27642
27831
  startAt: m.startAt ?? "",
@@ -27646,7 +27835,7 @@ async function runMeetings(ctx, argv) {
27646
27835
  }
27647
27836
  if (sub === "get") {
27648
27837
  if (!positionals[0]) throw new CliError("assistant meetings get requires <meetingId>.", 2);
27649
- const res = await requestJson(ctx, `${BASE34}/meetings/${enc(positionals[0])}`);
27838
+ const res = await requestJson(ctx, `${BASE35}/meetings/${enc(positionals[0])}`);
27650
27839
  const m = res.body ?? {};
27651
27840
  return emit(ctx, res.body, () => writeFields(ctx, [
27652
27841
  ["meetingId", m.meetingId],
@@ -27675,11 +27864,11 @@ async function runPending(ctx, argv) {
27675
27864
  const id = positionals[0];
27676
27865
  const needId = () => {
27677
27866
  if (!id) throw new CliError(`assistant pending ${sub} requires <actionId>.`, 2);
27678
- return `${BASE34}/pending-actions/${enc(id)}`;
27867
+ return `${BASE35}/pending-actions/${enc(id)}`;
27679
27868
  };
27680
27869
  switch (sub) {
27681
27870
  case "list": {
27682
- const res = await requestJson(ctx, `${BASE34}/pending-actions${options.status ? `?status=${enc(options.status)}` : ""}`);
27871
+ const res = await requestJson(ctx, `${BASE35}/pending-actions${options.status ? `?status=${enc(options.status)}` : ""}`);
27683
27872
  return emit(ctx, res.body, () => table2(ctx, res.body?.pendingActions, (a) => ({
27684
27873
  actionId: a.actionId,
27685
27874
  kind: a.kind ?? "",
@@ -27723,7 +27912,7 @@ async function runLoops(ctx, argv) {
27723
27912
  return 0;
27724
27913
  }
27725
27914
  if (sub === "list") {
27726
- const res = await requestJson(ctx, `${BASE34}/loops`);
27915
+ const res = await requestJson(ctx, `${BASE35}/loops`);
27727
27916
  return emit(ctx, res.body, () => table2(ctx, res.body?.loops, (l) => ({
27728
27917
  loopId: l.loopId,
27729
27918
  label: l.label ?? "",
@@ -27738,7 +27927,7 @@ async function runLoops(ctx, argv) {
27738
27927
  if (!loopId) throw new CliError(`assistant loops ${sub} requires <loopId>.`, 2);
27739
27928
  const body = {};
27740
27929
  if (sub === "enable" && options.cron) body.cronExpr = options.cron;
27741
- const res = await requestJson(ctx, `${BASE34}/loops/${enc(loopId)}/${sub}`, { method: "POST", body });
27930
+ const res = await requestJson(ctx, `${BASE35}/loops/${enc(loopId)}/${sub}`, { method: "POST", body });
27742
27931
  return emit(ctx, res.body, () => writeLine(ctx.io.stdout, `Loop ${loopId}: ${res.body?.enabled ? "enabled" : "disabled"} (job ${res.body?.jobId ?? ""}).`));
27743
27932
  }
27744
27933
  throw new CliError(`Unknown assistant loops command: ${sub}
@@ -27746,7 +27935,7 @@ Run \`openwop assistant --help\` for usage.`, 2);
27746
27935
  }
27747
27936
 
27748
27937
  // src/cli/channels.ts
27749
- var BASE35 = "/v1/host/openwop-app/channels";
27938
+ var BASE36 = "/v1/host/openwop-app/channels";
27750
27939
  var CHANNELS_HELP = `Usage:
27751
27940
  openwop channels list [--json]
27752
27941
  openwop channels create --name <n> [--description d] [--private] [--member <userId>]... [--agent <agentId>]... [--json]
@@ -27845,7 +28034,7 @@ function oneId(argv, usage6, spec = {}) {
27845
28034
  const { options, positionals } = parseOptions(argv, { ...spec, bool: ["--help", ...spec.bool ?? []] });
27846
28035
  if (options.help) return { help: true, options, positionals };
27847
28036
  if (!positionals[0]) throw new CliError(`Usage: openwop channels ${usage6}`, 2);
27848
- return { help: false, options, positionals, path: `${BASE35}/${enc(positionals[0])}` };
28037
+ return { help: false, options, positionals, path: `${BASE36}/${enc(positionals[0])}` };
27849
28038
  }
27850
28039
  function channelSummary(res) {
27851
28040
  const c = res?.channel ?? {};
@@ -27858,7 +28047,7 @@ async function runList4(ctx, argv) {
27858
28047
  write(ctx.io.stdout, CHANNELS_HELP);
27859
28048
  return 0;
27860
28049
  }
27861
- const res = await requestJson(ctx, BASE35);
28050
+ const res = await requestJson(ctx, BASE36);
27862
28051
  return emit(ctx, res.body, () => {
27863
28052
  const rows = Array.isArray(res.body?.channels) ? res.body.channels : [];
27864
28053
  if (!rows.length) {
@@ -27891,7 +28080,7 @@ async function runCreate(ctx, argv) {
27891
28080
  if (options.description) body.description = options.description;
27892
28081
  if (Array.isArray(options.member) && options.member.length) body.memberUserIds = options.member;
27893
28082
  if (Array.isArray(options.agent) && options.agent.length) body.agentIds = options.agent;
27894
- const res = await requestJson(ctx, BASE35, { method: "POST", body });
28083
+ const res = await requestJson(ctx, BASE36, { method: "POST", body });
27895
28084
  return emit(ctx, res.body, () => writeLine(ctx.io.stdout, `Created channel ${channelSummary(res.body)}.`));
27896
28085
  }
27897
28086
  async function runGet5(ctx, argv) {
@@ -28861,7 +29050,7 @@ Run \`openwop agent-author --help\` for usage.`, 2);
28861
29050
  }
28862
29051
 
28863
29052
  // src/cli/workflowAuthor.ts
28864
- var BASE36 = "/v1/host/openwop-app/workflow-author";
29053
+ var BASE37 = "/v1/host/openwop-app/workflow-author";
28865
29054
  var WORKFLOW_AUTHOR_HELP = `Usage:
28866
29055
  openwop workflow-author catalog [--json]
28867
29056
  openwop workflow-author draft --intent <text> [--provider p] [--model m] [--max-attempts n]
@@ -28897,7 +29086,7 @@ async function runWorkflowAuthor(ctx, argv) {
28897
29086
  return 0;
28898
29087
  }
28899
29088
  if (sub === "catalog") {
28900
- const res = await requestJson(ctx, `${BASE36}/catalog`);
29089
+ const res = await requestJson(ctx, `${BASE37}/catalog`);
28901
29090
  return emit(ctx, res.body, () => {
28902
29091
  const nodes = Array.isArray(res.body?.nodes) ? res.body.nodes : [];
28903
29092
  if (!nodes.length) writeLine(ctx.io.stdout, "No authorable nodes in this workspace.");
@@ -28913,7 +29102,7 @@ async function runWorkflowAuthor(ctx, argv) {
28913
29102
  if (options.model) body.model = options.model;
28914
29103
  const maxAttempts = intFlag(options.maxAttempts, "--max-attempts");
28915
29104
  if (maxAttempts) body.maxAttempts = maxAttempts;
28916
- const res = await requestJson(ctx, `${BASE36}/draft`, { method: "POST", body, headers: idempotencyHeaders(options.idempotencyKey) });
29105
+ const res = await requestJson(ctx, `${BASE37}/draft`, { method: "POST", body, headers: idempotencyHeaders(options.idempotencyKey) });
28917
29106
  const runId = res.body?.runId;
28918
29107
  if (!options.follow) {
28919
29108
  return emit(ctx, res.body, () => {
@@ -28940,7 +29129,7 @@ Run \`openwop workflow-author --help\` for usage.`, 2);
28940
29129
  }
28941
29130
 
28942
29131
  // src/cli/workflowProposals.ts
28943
- var BASE37 = "/v1/host/openwop-app/workflow-proposals/admin/policies";
29132
+ var BASE38 = "/v1/host/openwop-app/workflow-proposals/admin/policies";
28944
29133
  var WORKFLOW_PROPOSALS_HELP = `Usage:
28945
29134
  openwop workflow-proposals policies --tenant <tenantId> [--json]
28946
29135
  openwop workflow-proposals enable --tenant <tenantId> --agent-profile <agentProfileId> [--json]
@@ -28971,7 +29160,7 @@ async function runWorkflowProposals(ctx, argv) {
28971
29160
  }
28972
29161
  if (!options.tenant) throw new CliError("workflow-proposals requires --tenant <tenantId>.", 2);
28973
29162
  if (sub === "policies" || sub === "list") {
28974
- const res = await requestJson(ctx, `${BASE37}?tenantId=${enc(options.tenant)}`);
29163
+ const res = await requestJson(ctx, `${BASE38}?tenantId=${enc(options.tenant)}`);
28975
29164
  return emit(ctx, res.body, () => {
28976
29165
  const items = Array.isArray(res.body?.items) ? res.body.items : [];
28977
29166
  if (!items.length) {
@@ -28983,7 +29172,7 @@ async function runWorkflowProposals(ctx, argv) {
28983
29172
  }
28984
29173
  if (sub === "enable" || sub === "disable") {
28985
29174
  if (!options.agentProfile) throw new CliError(`workflow-proposals ${sub} requires --agent-profile <agentProfileId>.`, 2);
28986
- const path = `${BASE37}/${enc(options.tenant)}/${enc(options.agentProfile)}`;
29175
+ const path = `${BASE38}/${enc(options.tenant)}/${enc(options.agentProfile)}`;
28987
29176
  const res = await requestJson(ctx, path, { method: sub === "enable" ? "PUT" : "DELETE" });
28988
29177
  return emit(ctx, res.body, () => writeLine(ctx.io.stdout, sub === "enable" ? `Auto-approval enabled for ${options.agentProfile} in ${options.tenant}.` : res.body?.removed ? `Auto-approval disabled for ${options.agentProfile} in ${options.tenant}.` : `No policy was set for ${options.agentProfile} in ${options.tenant}.`));
28989
29178
  }
@@ -29929,6 +30118,10 @@ async function runCli(argv, options = {}) {
29929
30118
  return await runConformance(ctx, commandArgs);
29930
30119
  case "onboard":
29931
30120
  return await runOnboard(ctx, commandArgs);
30121
+ case "login":
30122
+ return await runLogin(ctx, commandArgs);
30123
+ case "logout":
30124
+ return await runLogout(ctx, commandArgs);
29932
30125
  case "providers":
29933
30126
  case "provider":
29934
30127
  return await runProviders(ctx, commandArgs);
@@ -30257,6 +30450,8 @@ var HELP_MAP = {
30257
30450
  packs: PACKS_HELP,
30258
30451
  pack: PACKS_HELP,
30259
30452
  onboard: ONBOARD_HELP,
30453
+ login: LOGIN_HELP,
30454
+ logout: LOGIN_HELP,
30260
30455
  providers: PROVIDERS_HELP,
30261
30456
  provider: PROVIDERS_HELP,
30262
30457
  agents: AGENTS_HELP,
@@ -30455,6 +30650,7 @@ Exit codes:
30455
30650
 
30456
30651
  Commands:
30457
30652
  onboard Guided first-run setup (host + provider + model + BYOK key)
30653
+ login | logout Sign this terminal in to an openwop-app host by a code (no key to copy)
30458
30654
  providers list List stored BYOK credential refs (never values)
30459
30655
  providers add Store a credential ref against the configured host
30460
30656
  providers remove Remove a credential ref