@openwop/cli 1.3.0 → 1.4.0

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/openwop.js CHANGED
@@ -579,7 +579,7 @@ function errText(err) {
579
579
  // package.json
580
580
  var package_default = {
581
581
  name: "@openwop/cli",
582
- version: "1.3.0",
582
+ version: "1.4.0",
583
583
  description: "OpenWOP CLI for operating the workflow-engine demo app and OpenWOP-compatible hosts.",
584
584
  license: "Apache-2.0",
585
585
  type: "module",
@@ -1079,7 +1079,7 @@ import { createInterface as createInterface2 } from "node:readline";
1079
1079
 
1080
1080
  // src/ids.ts
1081
1081
  function projectTenantBoundId(id) {
1082
- if (id.includes("~")) return id;
1082
+ if (id.includes("~") && !id.includes("/")) return id;
1083
1083
  let out = "";
1084
1084
  for (const byte of new TextEncoder().encode(id)) {
1085
1085
  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;
@@ -14860,6 +14890,153 @@ async function runOnboard(ctx, argv) {
14860
14890
  return 0;
14861
14891
  }
14862
14892
 
14893
+ // src/cli/login.ts
14894
+ import { hostname } from "node:os";
14895
+ var BASE10 = "/v1/host/openwop-app/cli-login";
14896
+ var LOGIN_HELP = `Usage:
14897
+ openwop login [--label <name>] [--timeout <seconds>] [--profile <name>] [--json]
14898
+ openwop logout [--profile <name>] [--json]
14899
+
14900
+ Sign this terminal in to an openwop-app host without copying an API key
14901
+ (host extension under ${BASE10}).
14902
+
14903
+ login Shows a short code. Open the host's Access \u2192 API keys page, type the
14904
+ code under "Sign in the OpenWOP CLI", and approve. The host then issues
14905
+ this CLI a key (30 days by default), saved to your config file.
14906
+ logout Revokes that key on the host and removes it from your config file.
14907
+
14908
+ The key is never printed. It is listed in the app as "CLI: <label>", where you can
14909
+ revoke it. Sign in again to renew.
14910
+
14911
+ --label <name> What to call this terminal in the app (default: this machine's hostname).
14912
+ --timeout <seconds> Give up waiting for approval after this long (default: the host's own limit).
14913
+ --profile <name> Use the named config profile.
14914
+
14915
+ Approve a code ONLY if you just ran \`openwop login\` yourself.
14916
+
14917
+ Exit codes: 0 signed in / signed out \xB7 1 denied, expired or not available on this host \xB7 2 usage.
14918
+
14919
+ Examples:
14920
+ export OPENWOP_BASE_URL=https://app.openwop.dev/api
14921
+ openwop login
14922
+ openwop login --label "work laptop"
14923
+ openwop logout
14924
+ `;
14925
+ var sleep2 = (ms2) => new Promise((r) => setTimeout(r, ms2));
14926
+ function approvalUrl(baseUrl, path) {
14927
+ const p = typeof path === "string" && path.startsWith("/") ? path : "/access?tab=api-keys";
14928
+ try {
14929
+ return `${new URL(baseUrl).origin}${p}`;
14930
+ } catch {
14931
+ return p;
14932
+ }
14933
+ }
14934
+ function notAvailable(ctx) {
14935
+ return new CliError(
14936
+ `${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.`,
14937
+ 1
14938
+ );
14939
+ }
14940
+ async function runLogin(ctx, argv) {
14941
+ const { options } = parseOptions(argv, { bool: ["--help"], value: ["--label", "--timeout", "--profile"] });
14942
+ if (options.help) {
14943
+ write(ctx.io.stdout, LOGIN_HELP);
14944
+ return 0;
14945
+ }
14946
+ const timeoutS = options.timeout === void 0 ? void 0 : Number(options.timeout);
14947
+ if (timeoutS !== void 0 && !(Number.isFinite(timeoutS) && timeoutS > 0)) {
14948
+ writeLine(ctx.io.stderr, "--timeout must be a positive number of seconds.");
14949
+ return 2;
14950
+ }
14951
+ const label = String(options.label ?? hostname() ?? "this machine").slice(0, 60);
14952
+ const anon = { ...ctx, apiKey: void 0 };
14953
+ let started;
14954
+ try {
14955
+ started = (await requestJson(anon, `${BASE10}/start`, { method: "POST", body: { label } })).body;
14956
+ } catch (err) {
14957
+ if (err instanceof HttpError && err.status === 404) throw notAvailable(ctx);
14958
+ throw err;
14959
+ }
14960
+ const deviceCode = started?.deviceCode;
14961
+ const userCode = started?.userCode;
14962
+ if (typeof deviceCode !== "string" || typeof userCode !== "string") throw notAvailable(ctx);
14963
+ writeLine(ctx.io.stderr, "");
14964
+ writeLine(ctx.io.stderr, ` 1. Open ${approvalUrl(ctx.baseUrl, started.verificationPath)}`);
14965
+ writeLine(ctx.io.stderr, ` 2. Enter ${userCode} under "Sign in the OpenWOP CLI", and approve.`);
14966
+ writeLine(ctx.io.stderr, "");
14967
+ writeLine(ctx.io.stderr, "Waiting for approval\u2026");
14968
+ const hostLimitS = Number.isFinite(Number(started.expiresIn)) ? Number(started.expiresIn) : 600;
14969
+ const deadline = Date.now() + Math.min(timeoutS ?? hostLimitS, hostLimitS) * 1e3;
14970
+ let intervalS = Number.isFinite(Number(started.interval)) ? Number(started.interval) : 3;
14971
+ for (; ; ) {
14972
+ if (Date.now() >= deadline) throw new CliError("Timed out waiting for approval. Run `openwop login` again for a new code.", 1);
14973
+ await sleep2(Math.max(0, intervalS) * 1e3);
14974
+ let res;
14975
+ try {
14976
+ res = (await requestJson(anon, `${BASE10}/poll`, { method: "POST", body: { deviceCode } })).body;
14977
+ } catch (err) {
14978
+ if (err instanceof HttpError && err.status === 404) {
14979
+ throw new CliError("That sign-in expired or was already used. Run `openwop login` again for a new code.", 1);
14980
+ }
14981
+ throw err;
14982
+ }
14983
+ if (res?.status === "pending") {
14984
+ if (Number.isFinite(Number(res.interval))) intervalS = Number(res.interval);
14985
+ continue;
14986
+ }
14987
+ if (res?.status === "denied") throw new CliError("The sign-in was denied in the app.", 1);
14988
+ if (res?.status === "failed") throw new CliError(`The host could not issue a key: ${String(res.message ?? "unknown reason")}`, 1);
14989
+ if (res?.status === "approved" && typeof res.token === "string") {
14990
+ const configPath = configPathFor(options.profile, ctx.env);
14991
+ saveConfig(configPath, mergeConfig(readConfigSafe(configPath), { host: { baseUrl: ctx.baseUrl, apiKey: res.token } }));
14992
+ const key = res.key ?? {};
14993
+ if (ctx.json) {
14994
+ writeJson(ctx.io.stdout, { status: "signed-in", keyId: key.keyId ?? null, name: key.name ?? null, expiresAt: key.expiresAt ?? null, configPath });
14995
+ return 0;
14996
+ }
14997
+ writeLine(ctx.io.stdout, `Signed in to ${ctx.baseUrl} as \u201C${key.name ?? `CLI: ${label}`}\u201D.`);
14998
+ if (key.expiresAt) writeLine(ctx.io.stdout, `The key expires ${String(key.expiresAt).slice(0, 10)}; run \`openwop login\` again to renew.`);
14999
+ writeLine(ctx.io.stdout, `Saved to ${configPath}. Revoke it any time with \`openwop logout\` or in the app.`);
15000
+ return 0;
15001
+ }
15002
+ throw new CliError("The host answered the sign-in in a shape this CLI does not understand.", 1);
15003
+ }
15004
+ }
15005
+ async function runLogout(ctx, argv) {
15006
+ const { options } = parseOptions(argv, { bool: ["--help"], value: ["--profile"] });
15007
+ if (options.help) {
15008
+ write(ctx.io.stdout, LOGIN_HELP);
15009
+ return 0;
15010
+ }
15011
+ const configPath = configPathFor(options.profile, ctx.env);
15012
+ const config = readConfigSafe(configPath);
15013
+ const saved = config?.host?.apiKey;
15014
+ if (typeof saved !== "string" || saved.length === 0) {
15015
+ if (ctx.json) writeJson(ctx.io.stdout, { status: "not-signed-in" });
15016
+ else writeLine(ctx.io.stdout, "Not signed in: no key is saved in your config.");
15017
+ return 0;
15018
+ }
15019
+ let revoked = false;
15020
+ let note = "";
15021
+ try {
15022
+ await requestJson({ ...ctx, apiKey: saved }, `${BASE10}/logout`, { method: "POST", body: {} });
15023
+ revoked = true;
15024
+ } catch (err) {
15025
+ if (err instanceof HttpError && err.status === 401) note = "The host no longer recognised the key (already revoked or expired).";
15026
+ 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.";
15027
+ 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.";
15028
+ else throw err;
15029
+ }
15030
+ unsetByPath(config, "host.apiKey");
15031
+ saveConfig(configPath, config);
15032
+ if (ctx.json) {
15033
+ writeJson(ctx.io.stdout, { status: "signed-out", revokedOnHost: revoked, ...note ? { note } : {} });
15034
+ return 0;
15035
+ }
15036
+ 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}`);
15037
+ return 0;
15038
+ }
15039
+
14863
15040
  // src/cli/providers.ts
14864
15041
  var PROVIDERS_HELP = `Usage:
14865
15042
  openwop providers list [--json]
@@ -18086,7 +18263,7 @@ Examples:
18086
18263
  openwop profiles pin r_123
18087
18264
  openwop profiles activity --limit 10 --status failed
18088
18265
  `;
18089
- var BASE10 = "/v1/host/openwop-app/profiles";
18266
+ var BASE11 = "/v1/host/openwop-app/profiles";
18090
18267
  async function profilesRequest(ctx, path, options, notFoundMsg) {
18091
18268
  try {
18092
18269
  return await requestJson(ctx, path, options);
@@ -18183,7 +18360,7 @@ async function runList2(ctx, argv) {
18183
18360
  write(ctx.io.stdout, "Usage: openwop profiles list [--json]\n");
18184
18361
  return 0;
18185
18362
  }
18186
- const res = await profilesRequest(ctx, BASE10);
18363
+ const res = await profilesRequest(ctx, BASE11);
18187
18364
  if (ctx.json) {
18188
18365
  writeJson(ctx.io.stdout, res.body);
18189
18366
  return 0;
@@ -18209,7 +18386,7 @@ async function runGet3(ctx, argv) {
18209
18386
  write(ctx.io.stdout, "Usage: openwop profiles get [<userId>] [--json]\n");
18210
18387
  return options.help ? 0 : 2;
18211
18388
  }
18212
- const path = positionals.length === 1 ? `${BASE10}/${encodeURIComponent(positionals[0])}` : `${BASE10}/me`;
18389
+ const path = positionals.length === 1 ? `${BASE11}/${encodeURIComponent(positionals[0])}` : `${BASE11}/me`;
18213
18390
  const res = await profilesRequest(ctx, path, void 0, `Profile not found: ${positionals[0] ?? "me"}`);
18214
18391
  if (ctx.json) {
18215
18392
  writeJson(ctx.io.stdout, res.body);
@@ -18228,7 +18405,7 @@ async function runActivity(ctx, argv) {
18228
18405
  if (options.limit !== void 0) qs2.set("limit", String(options.limit));
18229
18406
  if (options.status !== void 0) qs2.set("status", String(options.status));
18230
18407
  const q = qs2.toString();
18231
- const res = await profilesRequest(ctx, `${BASE10}/me/activity${q ? `?${q}` : ""}`);
18408
+ const res = await profilesRequest(ctx, `${BASE11}/me/activity${q ? `?${q}` : ""}`);
18232
18409
  if (ctx.json) {
18233
18410
  writeJson(ctx.io.stdout, res.body);
18234
18411
  return 0;
@@ -18267,7 +18444,7 @@ async function runEdit(ctx, argv) {
18267
18444
  if (Array.isArray(options.interests)) body.interests = options.interests;
18268
18445
  if (Array.isArray(options.growthInterests)) body.growthInterests = options.growthInterests;
18269
18446
  if (options.location !== void 0 || Array.isArray(options.link)) {
18270
- const current = (await profilesRequest(ctx, `${BASE10}/me`)).body?.contact ?? {};
18447
+ const current = (await profilesRequest(ctx, `${BASE11}/me`)).body?.contact ?? {};
18271
18448
  const contact = { ...current };
18272
18449
  if (options.location !== void 0) contact.location = options.location;
18273
18450
  if (Array.isArray(options.link)) {
@@ -18294,7 +18471,7 @@ async function runEdit(ctx, argv) {
18294
18471
  }
18295
18472
  if (Object.keys(avail).length) body.availability = avail;
18296
18473
  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 });
18474
+ const res = await profilesRequest(ctx, `${BASE11}/me`, { method: "PATCH", body });
18298
18475
  if (ctx.json) {
18299
18476
  writeJson(ctx.io.stdout, res.body);
18300
18477
  return 0;
@@ -18320,7 +18497,7 @@ async function runSkillsSet(ctx, argv) {
18320
18497
  if (!Number.isFinite(prof2) || prof2 < 1 || prof2 > 5) throw new CliError(`--skill proficiency must be 1..5 (got '${e}')`, 2);
18321
18498
  return { name, proficiency: prof2 };
18322
18499
  });
18323
- const res = await profilesRequest(ctx, `${BASE10}/me/skills`, { method: "PUT", body: { skills } });
18500
+ const res = await profilesRequest(ctx, `${BASE11}/me/skills`, { method: "PUT", body: { skills } });
18324
18501
  if (ctx.json) {
18325
18502
  writeJson(ctx.io.stdout, res.body);
18326
18503
  return 0;
@@ -18335,7 +18512,7 @@ async function runPortfolioAdd(ctx, argv) {
18335
18512
  write(ctx.io.stdout, "Usage: openwop profiles portfolio add --token <mediaToken> [--json]\n");
18336
18513
  return options.help ? 0 : 2;
18337
18514
  }
18338
- const res = await profilesRequest(ctx, `${BASE10}/me/portfolio`, { method: "POST", body: { token: options.token } });
18515
+ const res = await profilesRequest(ctx, `${BASE11}/me/portfolio`, { method: "POST", body: { token: options.token } });
18339
18516
  if (ctx.json) {
18340
18517
  writeJson(ctx.io.stdout, res.body);
18341
18518
  return 0;
@@ -18349,7 +18526,7 @@ async function runPortfolioRemove(ctx, argv) {
18349
18526
  write(ctx.io.stdout, "Usage: openwop profiles portfolio remove <mediaToken> [--json]\n");
18350
18527
  return options.help ? 0 : 2;
18351
18528
  }
18352
- const res = await profilesRequest(ctx, `${BASE10}/me/portfolio/${encodeURIComponent(positionals[0])}`, { method: "DELETE" }, `Portfolio asset not found: ${positionals[0]}`);
18529
+ const res = await profilesRequest(ctx, `${BASE11}/me/portfolio/${encodeURIComponent(positionals[0])}`, { method: "DELETE" }, `Portfolio asset not found: ${positionals[0]}`);
18353
18530
  if (ctx.json) {
18354
18531
  writeJson(ctx.io.stdout, res.body);
18355
18532
  return 0;
@@ -18364,7 +18541,7 @@ async function runPin(ctx, argv, pinned) {
18364
18541
  `);
18365
18542
  return options.help ? 0 : 2;
18366
18543
  }
18367
- const path = `${BASE10}/me/pinned-agents/${encodeURIComponent(positionals[0])}`;
18544
+ const path = `${BASE11}/me/pinned-agents/${encodeURIComponent(positionals[0])}`;
18368
18545
  const res = await profilesRequest(ctx, path, { method: pinned ? "PUT" : "DELETE" }, `Agent not found: ${positionals[0]}`);
18369
18546
  if (ctx.json) {
18370
18547
  writeJson(ctx.io.stdout, res.body);
@@ -18381,7 +18558,7 @@ async function runEndorse(ctx, argv, add) {
18381
18558
  return options.help ? 0 : 2;
18382
18559
  }
18383
18560
  const [userId, skill] = positionals;
18384
- const path = `${BASE10}/${encodeURIComponent(userId)}/skills/${encodeURIComponent(skill)}/endorse`;
18561
+ const path = `${BASE11}/${encodeURIComponent(userId)}/skills/${encodeURIComponent(skill)}/endorse`;
18385
18562
  const res = await profilesRequest(ctx, path, { method: add ? "POST" : "DELETE" }, `Profile or skill not found: ${userId} / ${skill}`);
18386
18563
  if (ctx.json) {
18387
18564
  writeJson(ctx.io.stdout, res.body);
@@ -19476,7 +19653,7 @@ Examples:
19476
19653
  openwop auth saml validate --idp-url http://localhost:9100/idp --variant valid
19477
19654
  openwop auth scim provision --op create-user --user-name jo@acme.com --email jo@acme.com
19478
19655
  `;
19479
- var BASE11 = "/v1/host/openwop-app/auth";
19656
+ var BASE12 = "/v1/host/openwop-app/auth";
19480
19657
  var SECRET_KEY2 = /secret|token|password|private[-_]?key|client[-_]?secret|api[-_]?key|credential|bearer|assertion|certificate|cert|pem/i;
19481
19658
  function redact2(value) {
19482
19659
  return redactSecrets(value, { secret: SECRET_KEY2 });
@@ -19527,7 +19704,7 @@ async function runStatus(ctx, argv) {
19527
19704
  const profiles = disc.ok && Array.isArray(disc.body?.capabilities?.auth?.profiles) ? disc.body.capabilities.auth.profiles : [];
19528
19705
  const samlAdvertised = profiles.includes("openwop-auth-saml");
19529
19706
  const scimAdvertised = profiles.includes("openwop-auth-scim");
19530
- const metaReachable = samlAdvertised ? (await probeEndpoint(ctx, `${BASE11}/saml/sso/metadata`)).ok : false;
19707
+ const metaReachable = samlAdvertised ? (await probeEndpoint(ctx, `${BASE12}/saml/sso/metadata`)).ok : false;
19531
19708
  if (ctx.json) {
19532
19709
  writeJson(ctx.io.stdout, {
19533
19710
  authProfilesAdvertised: profiles,
@@ -19552,7 +19729,7 @@ async function runSamlMetadata(ctx, argv) {
19552
19729
  write(ctx.io.stdout, "Usage: openwop auth saml metadata [--json]\n");
19553
19730
  return 0;
19554
19731
  }
19555
- const res = await authRequest(ctx, `${BASE11}/saml/sso/metadata`, { auth: false }, "SAML SSO is not configured on this host (set OPENWOP_SAML_*).");
19732
+ const res = await authRequest(ctx, `${BASE12}/saml/sso/metadata`, { auth: false }, "SAML SSO is not configured on this host (set OPENWOP_SAML_*).");
19556
19733
  const xml = typeof res.body?.raw === "string" ? res.body.raw : typeof res.body === "string" ? res.body : JSON.stringify(res.body);
19557
19734
  if (ctx.json) {
19558
19735
  writeJson(ctx.io.stdout, { metadata: xml });
@@ -19570,7 +19747,7 @@ async function runSamlLoginUrl(ctx, argv) {
19570
19747
  }
19571
19748
  const returnTo = options.returnTo ?? "/";
19572
19749
  const base16 = ctx.baseUrl.endsWith("/") ? ctx.baseUrl : `${ctx.baseUrl}/`;
19573
- const url = new URL(`${BASE11}/saml/sso/login?returnTo=${encodeURIComponent(returnTo)}`.replace(/^\//, ""), base16);
19750
+ const url = new URL(`${BASE12}/saml/sso/login?returnTo=${encodeURIComponent(returnTo)}`.replace(/^\//, ""), base16);
19574
19751
  const res = await ctx.fetchImpl(url, { method: "GET", redirect: "manual" });
19575
19752
  if (res.status === 404) {
19576
19753
  throw new CliError("SAML SSO is not configured on this host (set OPENWOP_SAML_*).", 1);
@@ -19595,7 +19772,7 @@ async function runSamlValidate(ctx, argv) {
19595
19772
  const body = { idpUrl: options.idpUrl, variant: options.variant };
19596
19773
  let res;
19597
19774
  try {
19598
- res = await requestJson(ctx, `${BASE11}/saml/validate`, { method: "POST", body });
19775
+ res = await requestJson(ctx, `${BASE12}/saml/validate`, { method: "POST", body });
19599
19776
  } catch (err) {
19600
19777
  if (err instanceof HttpError && err.status === 404) {
19601
19778
  throw new CliError("SAML validation seam not configured on this host (set OPENWOP_TEST_SAML_IDP_URL).", 1);
@@ -19638,7 +19815,7 @@ async function runScimProvision(ctx, argv) {
19638
19815
  if (options.displayName !== void 0) user.displayName = options.displayName;
19639
19816
  const body = { op: options.op, user };
19640
19817
  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).");
19818
+ 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
19819
  const safe = redact2(res.body ?? {});
19643
19820
  if (ctx.json) {
19644
19821
  writeJson(ctx.io.stdout, safe);
@@ -19662,7 +19839,7 @@ async function runBreakGlass(ctx, argv) {
19662
19839
  const token = await readSecretInput(ctx, options.tokenFile !== void 0 ? { valueFile: options.tokenFile } : {}, "Break-glass token");
19663
19840
  let res;
19664
19841
  try {
19665
- res = await requestJson(ctx, `${BASE11}/break-glass`, { method: "POST", body: { token } });
19842
+ res = await requestJson(ctx, `${BASE12}/break-glass`, { method: "POST", body: { token } });
19666
19843
  } catch (err) {
19667
19844
  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
19845
  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 +20168,9 @@ function exitForState(state) {
19991
20168
  return 1;
19992
20169
  }
19993
20170
  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);
20171
+ const record = await advertisedRecord(ctx, (b) => b.capabilities?.agents?.proposals ?? b.agents?.proposals);
20172
+ if (record === void 0) return;
20173
+ const advertised = !!record;
19998
20174
  if (!advertised) {
19999
20175
  throw new CliError(
20000
20176
  "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 +20499,9 @@ function exitForState2(state) {
20323
20499
  return 1;
20324
20500
  }
20325
20501
  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);
20502
+ const record = await advertisedRecord(ctx, (b) => b.capabilities?.agents?.goals ?? b.agents?.goals);
20503
+ if (record === void 0) return;
20504
+ const advertised = !!record;
20330
20505
  if (!advertised) {
20331
20506
  throw new CliError(
20332
20507
  "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 +20769,9 @@ function redact3(value) {
20594
20769
  return redactSecrets(value, { secret: SECRET_KEY3, keep: REF_ONLY_KEY });
20595
20770
  }
20596
20771
  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);
20772
+ const record = await advertisedRecord(ctx, (b) => b.capabilities?.portability ?? b.portability);
20773
+ if (record === void 0) return;
20774
+ const advertised = !!record;
20601
20775
  if (!advertised) {
20602
20776
  throw new CliError(
20603
20777
  "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 +21018,7 @@ function exitForState3(state) {
20844
21018
  return 1;
20845
21019
  }
20846
21020
  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;
21021
+ return advertisedRecord(ctx, (b) => b.capabilities?.triggerBridge ?? b.triggerBridge);
20851
21022
  }
20852
21023
  async function ensureAdvertised6(ctx) {
20853
21024
  const tb = await triggerBridgeCaps(ctx);
@@ -21107,10 +21278,7 @@ function exitForState4(state) {
21107
21278
  return 1;
21108
21279
  }
21109
21280
  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;
21281
+ return advertisedRecord(ctx, (b) => b.capabilities?.a2a ?? b.a2a);
21114
21282
  }
21115
21283
  async function runStatus2(ctx, argv) {
21116
21284
  const { options } = parseOptions(argv, { bool: ["--help"] });
@@ -21127,7 +21295,7 @@ async function runStatus2(ctx, argv) {
21127
21295
  writeLine(ctx.io.stdout, "This host does not advertise A2A (capabilities.a2a absent).");
21128
21296
  return 1;
21129
21297
  }
21130
- writeLine(ctx.io.stdout, `a2a.supported: ${a2a.supported ? "yes" : "no"}`);
21298
+ writeLine(ctx.io.stdout, `a2a.supported: ${a2a.supported ?? true ? "yes" : "no"}`);
21131
21299
  if (a2a.agentCardUrl) writeLine(ctx.io.stdout, `agentCardUrl: ${a2a.agentCardUrl}`);
21132
21300
  writeLine(ctx.io.stdout, `streaming: ${a2a.streaming ? "yes" : "no"}`);
21133
21301
  writeLine(ctx.io.stdout, `pushNotifications: ${a2a.pushNotifications ? "yes" : "no"}`);
@@ -21316,7 +21484,7 @@ Run \`openwop docs --help\` for usage.`);
21316
21484
  }
21317
21485
 
21318
21486
  // src/cli/knowledgeSync.ts
21319
- var BASE12 = "/v1/host/openwop-app/knowledge-sync";
21487
+ var BASE13 = "/v1/host/openwop-app/knowledge-sync";
21320
21488
  var KNOWLEDGE_SYNC_HELP = `Usage:
21321
21489
  openwop knowledge-sync list --org <orgId> [--json]
21322
21490
  openwop knowledge-sync get <sourceId> [--json]
@@ -21376,12 +21544,12 @@ async function runKnowledgeSync(ctx, argv) {
21376
21544
  switch (sub) {
21377
21545
  case "list": {
21378
21546
  if (!options.org) return usage4(ctx, "list --org <orgId>");
21379
- const res = await requestJson(ctx, withQuery(BASE12, { orgId: String(options.org) }));
21547
+ const res = await requestJson(ctx, withQuery(BASE13, { orgId: String(options.org) }));
21380
21548
  return renderList(ctx, res.body, pickArray(res.body, "sources"), ["id", "provider", "collectionId", "cadence", "status", "lastSyncedAt"], "No sync sources.");
21381
21549
  }
21382
21550
  case "get": {
21383
21551
  if (!id) return usage4(ctx, "get <sourceId>");
21384
- writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE12}/${enc2(id)}`)).body);
21552
+ writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE13}/${enc2(id)}`)).body);
21385
21553
  return 0;
21386
21554
  }
21387
21555
  case "create": {
@@ -21397,12 +21565,12 @@ async function runKnowledgeSync(ctx, argv) {
21397
21565
  if (options.cadence) body.cadence = String(options.cadence);
21398
21566
  if (options.provider) body.provider = String(options.provider);
21399
21567
  if (options.noMedia) body.includeMedia = false;
21400
- const res = await requestJson(ctx, BASE12, { method: "POST", body });
21568
+ const res = await requestJson(ctx, BASE13, { method: "POST", body });
21401
21569
  return renderDone(ctx, res.body, `Created sync source ${res.body?.source?.id ?? ""}.`);
21402
21570
  }
21403
21571
  case "browse": {
21404
21572
  if (!options.org || !options.connection) return usage4(ctx, "browse --org <orgId> --connection <connectionId> [--folder id]");
21405
- const res = await requestJson(ctx, withQuery(`${BASE12}/browse`, {
21573
+ const res = await requestJson(ctx, withQuery(`${BASE13}/browse`, {
21406
21574
  orgId: String(options.org),
21407
21575
  connectionId: String(options.connection),
21408
21576
  folderId: options.folder
@@ -21411,18 +21579,18 @@ async function runKnowledgeSync(ctx, argv) {
21411
21579
  }
21412
21580
  case "update": {
21413
21581
  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 } });
21582
+ const res = await requestJson(ctx, `${BASE13}/${enc2(id)}`, { method: "PATCH", body: { includeMedia: !options.noMedia } });
21415
21583
  return renderDone(ctx, res.body, `Updated sync source ${id} (media ${options.noMedia ? "excluded" : "included"}).`);
21416
21584
  }
21417
21585
  case "pause":
21418
21586
  case "resume": {
21419
21587
  if (!id) return usage4(ctx, `${sub} <sourceId>`);
21420
- const res = await requestJson(ctx, `${BASE12}/${enc2(id)}/${sub}`, { method: "POST", body: {} });
21588
+ const res = await requestJson(ctx, `${BASE13}/${enc2(id)}/${sub}`, { method: "POST", body: {} });
21421
21589
  return renderDone(ctx, res.body, `${sub === "pause" ? "Paused" : "Resumed"} sync source ${id}.`);
21422
21590
  }
21423
21591
  case "sync": {
21424
21592
  if (!id) return usage4(ctx, "sync <sourceId>");
21425
- const res = await requestJson(ctx, `${BASE12}/${enc2(id)}/sync`, { method: "POST", body: {} });
21593
+ const res = await requestJson(ctx, `${BASE13}/${enc2(id)}/sync`, { method: "POST", body: {} });
21426
21594
  const r = res.body?.result ?? {};
21427
21595
  const counts = Object.entries(r).filter(([, v]) => typeof v === "number").map(([k, v]) => `${k}=${v}`).join(" ");
21428
21596
  return renderDone(ctx, res.body, `Synced source ${id}${counts ? `: ${counts}` : "."}`);
@@ -21430,7 +21598,7 @@ async function runKnowledgeSync(ctx, argv) {
21430
21598
  case "delete": {
21431
21599
  if (!id) return usage4(ctx, "delete <sourceId> [--yes]");
21432
21600
  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" });
21601
+ const res = await requestJson(ctx, `${BASE13}/${enc2(id)}`, { method: "DELETE" });
21434
21602
  return renderDone(ctx, res.body, `Deleted sync source ${id}.`);
21435
21603
  }
21436
21604
  default:
@@ -21442,7 +21610,7 @@ Run \`openwop knowledge-sync --help\` for usage.`);
21442
21610
  // src/cli/entities.ts
21443
21611
  import { readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "node:fs";
21444
21612
  import { resolve as resolvePath9 } from "node:path";
21445
- var BASE13 = "/v1/host/openwop-app/entities";
21613
+ var BASE14 = "/v1/host/openwop-app/entities";
21446
21614
  var PUBLIC_BASE2 = "/v1/host/openwop-app/public-entities";
21447
21615
  var ENTITIES_HELP = `Usage:
21448
21616
  openwop entities locale-context [--json]
@@ -21564,7 +21732,7 @@ async function runEntities(ctx, argv) {
21564
21732
  const q = (path, extra = {}) => withQuery(path, { projectId: project4, ...extra });
21565
21733
  switch (family) {
21566
21734
  case "locale-context": {
21567
- const res = await requestJson(ctx, q(`${BASE13}/locale-context`));
21735
+ const res = await requestJson(ctx, q(`${BASE14}/locale-context`));
21568
21736
  if (ctx.json) {
21569
21737
  writeJson(ctx.io.stdout, res.body);
21570
21738
  return 0;
@@ -21596,10 +21764,10 @@ Run \`openwop entities --help\` for usage.`);
21596
21764
  }
21597
21765
  async function types(ctx, sub, pos, o, project4, q) {
21598
21766
  const name = pos[0];
21599
- const typePath = (n) => `${BASE13}/types/${enc2(n)}`;
21767
+ const typePath = (n) => `${BASE14}/types/${enc2(n)}`;
21600
21768
  switch (sub) {
21601
21769
  case "list": {
21602
- const res = await requestJson(ctx, q(`${BASE13}/types`));
21770
+ const res = await requestJson(ctx, q(`${BASE14}/types`));
21603
21771
  return renderList(
21604
21772
  ctx,
21605
21773
  res.body,
@@ -21623,7 +21791,7 @@ async function types(ctx, sub, pos, o, project4, q) {
21623
21791
  projectId: project4
21624
21792
  });
21625
21793
  if (!body.name) return usage5(ctx, "types create --name <n> [--fields <json>]");
21626
- const res = await requestJson(ctx, `${BASE13}/types`, { method: "POST", body });
21794
+ const res = await requestJson(ctx, `${BASE14}/types`, { method: "POST", body });
21627
21795
  return renderDone(ctx, res.body, `Created entity type ${res.body?.name ?? body.name}.`);
21628
21796
  }
21629
21797
  case "update": {
@@ -21698,7 +21866,7 @@ async function types(ctx, sub, pos, o, project4, q) {
21698
21866
  async function records(ctx, sub, pos, o, _project, q) {
21699
21867
  const [type, id] = pos;
21700
21868
  if (!type) return usage5(ctx, `${sub} <type>${sub === "list" || sub === "create" ? "" : " <entityId>"}`);
21701
- const coll = `${BASE13}/types/${enc2(type)}/entities`;
21869
+ const coll = `${BASE14}/types/${enc2(type)}/entities`;
21702
21870
  const recordBody = () => mergeBody(ctx, o, {
21703
21871
  values: o.values !== void 0 ? parseJsonFlag("--values", o.values) : void 0,
21704
21872
  termIds: csv(o.termIds),
@@ -21743,7 +21911,7 @@ async function records(ctx, sub, pos, o, _project, q) {
21743
21911
  async function taxonomies(ctx, sub, pos, o, project4, q) {
21744
21912
  switch (sub) {
21745
21913
  case "list": {
21746
- const res = await requestJson(ctx, q(`${BASE13}/taxonomies`));
21914
+ const res = await requestJson(ctx, q(`${BASE14}/taxonomies`));
21747
21915
  return renderList(ctx, res.body, pickArray(res.body, "taxonomies"), ["name", "displayName"], "No taxonomies.");
21748
21916
  }
21749
21917
  case "create": {
@@ -21753,14 +21921,14 @@ async function taxonomies(ctx, sub, pos, o, project4, q) {
21753
21921
  projectId: project4
21754
21922
  });
21755
21923
  if (!body.name) return usage5(ctx, "taxonomies create --name <n> [--display-name d]");
21756
- const res = await requestJson(ctx, `${BASE13}/taxonomies`, { method: "POST", body });
21924
+ const res = await requestJson(ctx, `${BASE14}/taxonomies`, { method: "POST", body });
21757
21925
  return renderDone(ctx, res.body, `Created taxonomy ${res.body?.name ?? body.name}.`);
21758
21926
  }
21759
21927
  case "delete": {
21760
21928
  const name = pos[0];
21761
21929
  if (!name) return usage5(ctx, "taxonomies delete <taxonomy> --yes");
21762
21930
  confirm(o, `taxonomy ${name}`);
21763
- await requestJson(ctx, q(`${BASE13}/taxonomies/${enc2(name)}`), { method: "DELETE" });
21931
+ await requestJson(ctx, q(`${BASE14}/taxonomies/${enc2(name)}`), { method: "DELETE" });
21764
21932
  writeLine(ctx.io.stdout, `Deleted taxonomy ${name}.`);
21765
21933
  return 0;
21766
21934
  }
@@ -21771,7 +21939,7 @@ async function taxonomies(ctx, sub, pos, o, project4, q) {
21771
21939
  async function terms(ctx, sub, pos, o, project4, q) {
21772
21940
  const [tax, slug] = pos;
21773
21941
  if (!tax) return usage5(ctx, `terms ${sub} <taxonomy>`);
21774
- const coll = `${BASE13}/taxonomies/${enc2(tax)}/terms`;
21942
+ const coll = `${BASE14}/taxonomies/${enc2(tax)}/terms`;
21775
21943
  switch (sub) {
21776
21944
  case "list": {
21777
21945
  const res = await requestJson(ctx, q(coll));
@@ -21823,7 +21991,7 @@ async function terms(ctx, sub, pos, o, project4, q) {
21823
21991
  async function relationships(ctx, sub, pos, o, project4, q) {
21824
21992
  switch (sub) {
21825
21993
  case "list": {
21826
- const res = await requestJson(ctx, q(`${BASE13}/relationships`));
21994
+ const res = await requestJson(ctx, q(`${BASE14}/relationships`));
21827
21995
  return renderList(ctx, res.body, pickArray(res.body, "relationships"), ["fromTypeName", "toTypeName", "cardinality", "onDelete"], "No relationships.");
21828
21996
  }
21829
21997
  case "create": {
@@ -21835,14 +22003,14 @@ async function relationships(ctx, sub, pos, o, project4, q) {
21835
22003
  projectId: project4
21836
22004
  });
21837
22005
  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 });
22006
+ const res = await requestJson(ctx, `${BASE14}/relationships`, { method: "POST", body });
21839
22007
  return renderDone(ctx, res.body, `Created relationship ${body.fromTypeName} \u2192 ${body.toTypeName}.`);
21840
22008
  }
21841
22009
  case "delete": {
21842
22010
  const [from, to] = pos;
21843
22011
  if (!from || !to) return usage5(ctx, "relationships delete <fromType> <toType> --yes");
21844
22012
  confirm(o, `relationship ${from} \u2192 ${to}`);
21845
- await requestJson(ctx, q(`${BASE13}/relationships/${enc2(from)}/${enc2(to)}`), { method: "DELETE" });
22013
+ await requestJson(ctx, q(`${BASE14}/relationships/${enc2(from)}/${enc2(to)}`), { method: "DELETE" });
21846
22014
  writeLine(ctx.io.stdout, `Deleted relationship ${from} \u2192 ${to}.`);
21847
22015
  return 0;
21848
22016
  }
@@ -22430,7 +22598,7 @@ Run \`openwop production --help\` for usage.`);
22430
22598
  }
22431
22599
 
22432
22600
  // src/cli/tutorials.ts
22433
- var BASE14 = "/v1/host/openwop-app/tutorials";
22601
+ var BASE15 = "/v1/host/openwop-app/tutorials";
22434
22602
  var TUTORIALS_HELP = `Usage:
22435
22603
  openwop tutorials list [--json]
22436
22604
  openwop tutorials get <slug> [--json]
@@ -22438,9 +22606,9 @@ var TUTORIALS_HELP = `Usage:
22438
22606
  openwop tutorials progress set <tutorialId> --steps <id,id,...> [--json]
22439
22607
  openwop tutorials progress clear <tutorialId> [--json]
22440
22608
 
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,
22609
+ The tutorial library (GET ${BASE15}) and one tutorial in full (GET ${BASE15}/<slug>).
22610
+ \`progress\` reads YOUR saved steps (GET ${BASE15}/progress); \`progress set\` replaces a
22611
+ tutorial's completed steps and \`progress clear\` resets them (POST ${BASE15}/progress,
22444
22612
  body { tutorialId, completedStepIds, mode? }). The server stamps the user \u2014 you can
22445
22613
  only write your own progress; anonymous sessions cannot save.
22446
22614
 
@@ -22464,7 +22632,7 @@ async function runTutorials(ctx, argv) {
22464
22632
  }
22465
22633
  switch (sub) {
22466
22634
  case "list": {
22467
- const res = await requestJson(ctx, BASE14);
22635
+ const res = await requestJson(ctx, BASE15);
22468
22636
  const items = Array.isArray(res.body?.tutorials) ? res.body.tutorials : [];
22469
22637
  const code2 = renderList(ctx, res.body, items, ["id", "category", "title", "source"], "No tutorials.");
22470
22638
  if (!ctx.json && res.body?.degraded) writeLine(ctx.io.stdout, "(shipped copy \u2014 the tenant library was unreachable)");
@@ -22476,13 +22644,13 @@ async function runTutorials(ctx, argv) {
22476
22644
  write(ctx.io.stderr, "Usage: openwop tutorials get <slug>\n");
22477
22645
  return 2;
22478
22646
  }
22479
- writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE14}/${enc2(slug)}`)).body);
22647
+ writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE15}/${enc2(slug)}`)).body);
22480
22648
  return 0;
22481
22649
  }
22482
22650
  case "progress": {
22483
22651
  const action = positionals[0];
22484
22652
  if (!action) {
22485
- const res2 = await requestJson(ctx, `${BASE14}/progress`);
22653
+ const res2 = await requestJson(ctx, `${BASE15}/progress`);
22486
22654
  const items = Array.isArray(res2.body?.progress) ? res2.body.progress : [];
22487
22655
  const code2 = renderList(
22488
22656
  ctx,
@@ -22505,7 +22673,7 @@ async function runTutorials(ctx, argv) {
22505
22673
  return 2;
22506
22674
  }
22507
22675
  const body = action === "set" ? { tutorialId, completedStepIds: csv(options.steps) ?? [] } : { tutorialId, completedStepIds: [], mode: "clear" };
22508
- const res = await requestJson(ctx, `${BASE14}/progress`, { method: "POST", body });
22676
+ const res = await requestJson(ctx, `${BASE15}/progress`, { method: "POST", body });
22509
22677
  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
22678
  }
22511
22679
  default:
@@ -22515,16 +22683,16 @@ Run \`openwop tutorials --help\` for usage.`);
22515
22683
  }
22516
22684
 
22517
22685
  // src/cli/walkthroughs.ts
22518
- var BASE15 = "/v1/host/openwop-app/walkthroughs";
22686
+ var BASE16 = "/v1/host/openwop-app/walkthroughs";
22519
22687
  var WALKTHROUGHS_HELP = `Usage:
22520
22688
  openwop walkthroughs progress [--json]
22521
22689
  openwop walkthroughs progress set <walkthroughId> --status started|completed --run <runId> [--json]
22522
22690
  openwop walkthroughs funnel <walkthroughId> [--json]
22523
22691
 
22524
- \`progress\` reads your walkthrough progress (GET ${BASE15}/progress); \`progress set\`
22525
- records one (POST ${BASE15}/progress, body { walkthroughId, status, runId }).
22692
+ \`progress\` reads your walkthrough progress (GET ${BASE16}/progress); \`progress set\`
22693
+ records one (POST ${BASE16}/progress, body { walkthroughId, status, runId }).
22526
22694
  \`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.
22695
+ (GET ${BASE16}/funnel?walkthroughId=\u2026) \u2014 a recent-window view over the newest runs.
22528
22696
 
22529
22697
  Exit codes: 0 ok \xB7 2 usage / validation / feature off \xB7 4 forbidden.
22530
22698
 
@@ -22552,10 +22720,10 @@ async function runWalkthroughs(ctx, argv) {
22552
22720
  write(ctx.io.stderr, "Usage: openwop walkthroughs progress set <walkthroughId> --status started|completed --run <runId>\n");
22553
22721
  return 2;
22554
22722
  }
22555
- const res2 = await requestJson(ctx, `${BASE15}/progress`, { method: "POST", body: { walkthroughId, status: String(options.status), runId: String(options.run) } });
22723
+ const res2 = await requestJson(ctx, `${BASE16}/progress`, { method: "POST", body: { walkthroughId, status: String(options.status), runId: String(options.run) } });
22556
22724
  return renderDone(ctx, res2.body, `Recorded ${walkthroughId} as ${String(options.status)}.`);
22557
22725
  }
22558
- const res = await requestJson(ctx, `${BASE15}/progress`);
22726
+ const res = await requestJson(ctx, `${BASE16}/progress`);
22559
22727
  const items = Array.isArray(res.body?.progress) ? res.body.progress : [];
22560
22728
  return renderList(ctx, res.body, items, ["walkthroughId", "status", "runId", "updatedAt"], "No walkthrough progress.");
22561
22729
  }
@@ -22565,7 +22733,7 @@ async function runWalkthroughs(ctx, argv) {
22565
22733
  write(ctx.io.stderr, "Usage: openwop walkthroughs funnel <walkthroughId>\n");
22566
22734
  return 2;
22567
22735
  }
22568
- const res = await requestJson(ctx, withQuery(`${BASE15}/funnel`, { walkthroughId }));
22736
+ const res = await requestJson(ctx, withQuery(`${BASE16}/funnel`, { walkthroughId }));
22569
22737
  if (ctx.json) {
22570
22738
  writeJson(ctx.io.stdout, res.body);
22571
22739
  return 0;
@@ -22586,7 +22754,7 @@ Run \`openwop walkthroughs --help\` for usage.`);
22586
22754
  }
22587
22755
 
22588
22756
  // src/cli/widgets.ts
22589
- var BASE16 = "/v1/host/openwop-app/widgets";
22757
+ var BASE17 = "/v1/host/openwop-app/widgets";
22590
22758
  var WIDGETS_HELP = `Usage:
22591
22759
  openwop widgets list [--json]
22592
22760
  openwop widgets summary [--json]
@@ -22594,8 +22762,8 @@ var WIDGETS_HELP = `Usage:
22594
22762
  openwop widgets archive <widgetId> [--json]
22595
22763
  openwop widgets seed [--json]
22596
22764
 
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
22765
+ The reference example domain (GET/POST ${BASE17}, GET ${BASE17}/summary,
22766
+ POST ${BASE17}/<id>/archive, POST ${BASE17}/seed). Mounted only when the host sets
22599
22767
  OPENWOP_EXAMPLE_WIDGETS_ENABLED=true. \`seed\` is idempotent (\`seeded: false\` on a
22600
22768
  re-run). Archiving an already-archived widget is a conflict (exit 2), never a
22601
22769
  silent success.
@@ -22621,12 +22789,12 @@ async function runWidgets(ctx, argv) {
22621
22789
  try {
22622
22790
  switch (sub) {
22623
22791
  case "list": {
22624
- const res = await requestJson(ctx, BASE16);
22792
+ const res = await requestJson(ctx, BASE17);
22625
22793
  const items = Array.isArray(res.body?.widgets) ? res.body.widgets : [];
22626
22794
  return renderList(ctx, res.body, items, ["id", "name", "status"], "No widgets.");
22627
22795
  }
22628
22796
  case "summary": {
22629
- const res = await requestJson(ctx, `${BASE16}/summary`);
22797
+ const res = await requestJson(ctx, `${BASE17}/summary`);
22630
22798
  if (ctx.json) {
22631
22799
  writeJson(ctx.io.stdout, res.body);
22632
22800
  return 0;
@@ -22640,7 +22808,7 @@ async function runWidgets(ctx, argv) {
22640
22808
  write(ctx.io.stderr, "widgets create needs --name.\n");
22641
22809
  return 2;
22642
22810
  }
22643
- const res = await requestJson(ctx, BASE16, { method: "POST", body: { name: String(options.name) } });
22811
+ const res = await requestJson(ctx, BASE17, { method: "POST", body: { name: String(options.name) } });
22644
22812
  return renderDone(ctx, res.body, `Created widget ${res.body?.id ?? ""} (${String(options.name)}).`);
22645
22813
  }
22646
22814
  case "archive": {
@@ -22649,11 +22817,11 @@ async function runWidgets(ctx, argv) {
22649
22817
  write(ctx.io.stderr, "Usage: openwop widgets archive <widgetId>\n");
22650
22818
  return 2;
22651
22819
  }
22652
- const res = await requestJson(ctx, `${BASE16}/${enc2(id)}/archive`, { method: "POST", body: {} });
22820
+ const res = await requestJson(ctx, `${BASE17}/${enc2(id)}/archive`, { method: "POST", body: {} });
22653
22821
  return renderDone(ctx, res.body, `Archived widget ${id}.`);
22654
22822
  }
22655
22823
  case "seed": {
22656
- const res = await requestJson(ctx, `${BASE16}/seed`, { method: "POST", body: {} });
22824
+ const res = await requestJson(ctx, `${BASE17}/seed`, { method: "POST", body: {} });
22657
22825
  return renderDone(ctx, res.body, res.body?.seeded === false ? "Example widgets already seeded (no-op)." : "Seeded example widgets.");
22658
22826
  }
22659
22827
  default:
@@ -22673,17 +22841,17 @@ Run \`openwop widgets --help\` for usage.`);
22673
22841
  }
22674
22842
 
22675
22843
  // src/cli/uiState.ts
22676
- var BASE17 = "/v1/host/openwop-app/ui-state";
22844
+ var BASE18 = "/v1/host/openwop-app/ui-state";
22677
22845
  var UI_STATE_HELP = `Usage:
22678
22846
  openwop ui-state list [--resource-type <t>] [--resource-id <id>] [--json]
22679
22847
  openwop ui-state get <resourceType> <resourceId> <key> [--json]
22680
22848
  openwop ui-state set <resourceType> <resourceId> <key> --value <json> [--json]
22681
22849
  openwop ui-state delete <resourceType> <resourceId> <key>
22682
22850
 
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,
22851
+ \`list\` reads your rows (GET ${BASE18}?resourceType=&resourceId=); \`get\` filters one
22852
+ key from that list. \`set\` writes one key (PUT ${BASE18}, body { resourceType,
22685
22853
  resourceId, key, value }); --value is parsed as JSON, falling back to a string.
22686
- \`delete\` removes one key (DELETE ${BASE17}/<type>/<id>/<key>).
22854
+ \`delete\` removes one key (DELETE ${BASE18}/<type>/<id>/<key>).
22687
22855
 
22688
22856
  Exit codes: 0 ok \xB7 2 usage / not found \xB7 4 forbidden.
22689
22857
 
@@ -22706,7 +22874,7 @@ async function runUiState(ctx, argv) {
22706
22874
  const [resourceType, resourceId, key] = positionals;
22707
22875
  switch (sub) {
22708
22876
  case "list": {
22709
- const res = await requestJson(ctx, withQuery(BASE17, { resourceType: options.resourceType, resourceId: options.resourceId }));
22877
+ const res = await requestJson(ctx, withQuery(BASE18, { resourceType: options.resourceType, resourceId: options.resourceId }));
22710
22878
  const items = Array.isArray(res.body?.items) ? res.body.items : [];
22711
22879
  return renderList(ctx, res.body, items, ["resourceType", "resourceId", "key", "value"], "No saved UI state.");
22712
22880
  }
@@ -22715,7 +22883,7 @@ async function runUiState(ctx, argv) {
22715
22883
  write(ctx.io.stderr, "Usage: openwop ui-state get <resourceType> <resourceId> <key>\n");
22716
22884
  return 2;
22717
22885
  }
22718
- const res = await requestJson(ctx, withQuery(BASE17, { resourceType, resourceId }));
22886
+ const res = await requestJson(ctx, withQuery(BASE18, { resourceType, resourceId }));
22719
22887
  const items = Array.isArray(res.body?.items) ? res.body.items : [];
22720
22888
  const hit = items.find((i) => i?.key === key);
22721
22889
  if (!hit) throw new CliError(`No ui-state for ${resourceType}/${resourceId}/${key}.`, 2);
@@ -22734,7 +22902,7 @@ async function runUiState(ctx, argv) {
22734
22902
  } catch {
22735
22903
  value = String(options.value);
22736
22904
  }
22737
- const res = await requestJson(ctx, BASE17, { method: "PUT", body: { resourceType, resourceId, key, value } });
22905
+ const res = await requestJson(ctx, BASE18, { method: "PUT", body: { resourceType, resourceId, key, value } });
22738
22906
  return renderDone(ctx, res.body, `Saved ${resourceType}/${resourceId}/${key}.`);
22739
22907
  }
22740
22908
  case "delete": {
@@ -22742,7 +22910,7 @@ async function runUiState(ctx, argv) {
22742
22910
  write(ctx.io.stderr, "Usage: openwop ui-state delete <resourceType> <resourceId> <key>\n");
22743
22911
  return 2;
22744
22912
  }
22745
- await requestJson(ctx, `${BASE17}/${enc2(resourceType)}/${enc2(resourceId)}/${enc2(key)}`, { method: "DELETE" });
22913
+ await requestJson(ctx, `${BASE18}/${enc2(resourceType)}/${enc2(resourceId)}/${enc2(key)}`, { method: "DELETE" });
22746
22914
  writeLine(ctx.io.stdout, `Deleted ${resourceType}/${resourceId}/${key}.`);
22747
22915
  return 0;
22748
22916
  }
@@ -22755,8 +22923,8 @@ Run \`openwop ui-state --help\` for usage.`);
22755
22923
  // src/cli/uiPlugin.ts
22756
22924
  import { writeFileSync as writeFileSync16 } from "node:fs";
22757
22925
  import { resolve as resolvePath11 } from "node:path";
22758
- var BASE18 = "/v1/host/openwop-app/ui-plugin";
22759
- var RPC_PRODUCT = `${BASE18}/rpc`;
22926
+ var BASE19 = "/v1/host/openwop-app/ui-plugin";
22927
+ var RPC_PRODUCT = `${BASE19}/rpc`;
22760
22928
  var RPC_CONFORMANCE = "/v1/host/sample/ui-plugin/rpc";
22761
22929
  var UI_PLUGIN_HELP = `Usage:
22762
22930
  openwop ui-plugin packs [--json]
@@ -22767,11 +22935,11 @@ var UI_PLUGIN_HELP = `Usage:
22767
22935
  openwop ui-plugin rpc --body <json> | --body-file <path>
22768
22936
 
22769
22937
  \`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
22938
+ (GET ${BASE19}/packs). \`entry\` fetches a plugin's sandboxed entry bundle
22939
+ (GET ${BASE19}/packs/<name>/plugins/<id>/entry); \`trusted-entry\` fetches a signed
22940
+ pack's main-frame ES module (GET ${BASE19}/trusted/<name>/plugins/<id>/entry.mjs \u2014
22773
22941
  404 unless the trusted-plugins toggle is on and the signature verifies).
22774
- \`demo-artifact\` ensures the idempotent demo artifact (POST ${BASE18}/demo-artifact).
22942
+ \`demo-artifact\` ensures the idempotent demo artifact (POST ${BASE19}/demo-artifact).
22775
22943
  \`rpc\` sends one ui-plugin/1 request (POST ${RPC_PRODUCT}, body { message }); the
22776
22944
  message is { openwop: "ui-plugin/1", type: "request", id (integer,
22777
22945
  default 1), method, params } unless you pass a
@@ -22812,7 +22980,7 @@ async function runUiPlugin(ctx, argv) {
22812
22980
  }
22813
22981
  switch (sub) {
22814
22982
  case "packs": {
22815
- const res = await requestJson(ctx, `${BASE18}/packs`);
22983
+ const res = await requestJson(ctx, `${BASE19}/packs`);
22816
22984
  const items = Array.isArray(res.body?.plugins) ? res.body.plugins : [];
22817
22985
  const code2 = renderList(ctx, res.body, items, ["packName", "pluginId", "tier"], "No UI plugins served.");
22818
22986
  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 +22994,7 @@ async function runUiPlugin(ctx, argv) {
22826
22994
  `);
22827
22995
  return 2;
22828
22996
  }
22829
- const path = sub === "entry" ? `${BASE18}/packs/${enc2(name)}/plugins/${enc2(pluginId)}/entry` : `${BASE18}/trusted/${enc2(name)}/plugins/${enc2(pluginId)}/entry.mjs`;
22997
+ const path = sub === "entry" ? `${BASE19}/packs/${enc2(name)}/plugins/${enc2(pluginId)}/entry` : `${BASE19}/trusted/${enc2(name)}/plugins/${enc2(pluginId)}/entry.mjs`;
22830
22998
  const res = await fetchRawText(ctx, path);
22831
22999
  if (options.output) {
22832
23000
  writeFileSync16(resolvePath11(ctx.cwd, String(options.output)), res.text);
@@ -22838,7 +23006,7 @@ async function runUiPlugin(ctx, argv) {
22838
23006
  return 0;
22839
23007
  }
22840
23008
  case "demo-artifact": {
22841
- const res = await requestJson(ctx, `${BASE18}/demo-artifact`, { method: "POST", body: {} });
23009
+ const res = await requestJson(ctx, `${BASE19}/demo-artifact`, { method: "POST", body: {} });
22842
23010
  return renderDone(ctx, res.body, `Demo artifact ${res.body?.artifactId ?? ""} (version ${res.body?.version ?? ""}).`);
22843
23011
  }
22844
23012
  case "rpc": {
@@ -22868,18 +23036,18 @@ Run \`openwop ui-plugin --help\` for usage.`);
22868
23036
  }
22869
23037
 
22870
23038
  // src/cli/canvasCollab.ts
22871
- var BASE19 = "/v1/host/openwop-app/canvas-collab";
23039
+ var BASE20 = "/v1/host/openwop-app/canvas-collab";
22872
23040
  var CANVAS_COLLAB_HELP = `Usage:
22873
23041
  openwop canvas-collab ticket <canvasId> [--json]
22874
23042
  openwop canvas-collab claim-seed <canvasId> [--json]
22875
23043
  openwop canvas-collab debug [--json]
22876
23044
 
22877
23045
  \`ticket\` mints a short-lived collaboration ticket for one canvas
22878
- (POST ${BASE19}/<canvasId>/ticket) \u2014 a credential for the collaboration socket's
23046
+ (POST ${BASE20}/<canvasId>/ticket) \u2014 a credential for the collaboration socket's
22879
23047
  ?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
23048
+ seeder election (POST ${BASE20}/<canvasId>/claim-seed) \u2192 { seed: true } for the one
22881
23049
  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).
23050
+ rooms (GET ${BASE20}/_debug; super-admin only; lifecycle metadata, never content).
22883
23051
 
22884
23052
  Exit codes: 0 ok \xB7 2 usage / not found (uniform) \xB7 4 forbidden (debug without super-admin).
22885
23053
 
@@ -22913,7 +23081,7 @@ async function runCanvasCollab(ctx, argv) {
22913
23081
  write(ctx.io.stderr, "Usage: openwop canvas-collab ticket <canvasId>\n");
22914
23082
  return 2;
22915
23083
  }
22916
- const res = await requestJson(ctx, `${BASE19}/${enc2(id)}/ticket`, { method: "POST", body: {} });
23084
+ const res = await requestJson(ctx, `${BASE20}/${enc2(id)}/ticket`, { method: "POST", body: {} });
22917
23085
  return printTicketOnce(ctx, res.body, `canvas ${id}`);
22918
23086
  }
22919
23087
  case "claim-seed": {
@@ -22921,13 +23089,13 @@ async function runCanvasCollab(ctx, argv) {
22921
23089
  write(ctx.io.stderr, "Usage: openwop canvas-collab claim-seed <canvasId>\n");
22922
23090
  return 2;
22923
23091
  }
22924
- const res = await requestJson(ctx, `${BASE19}/${enc2(id)}/claim-seed`, { method: "POST", body: {} });
23092
+ const res = await requestJson(ctx, `${BASE20}/${enc2(id)}/claim-seed`, { method: "POST", body: {} });
22925
23093
  if (ctx.json) writeJson(ctx.io.stdout, res.body);
22926
23094
  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
23095
  return 0;
22928
23096
  }
22929
23097
  case "debug": {
22930
- const res = await requestJson(ctx, `${BASE19}/_debug`);
23098
+ const res = await requestJson(ctx, `${BASE20}/_debug`);
22931
23099
  const items = Array.isArray(res.body?.instanceRooms) ? res.body.instanceRooms : [];
22932
23100
  return renderList(ctx, res.body, items, ["roomId", "tenantId", "clients", "openedAt"], "No live collaboration rooms on this instance.");
22933
23101
  }
@@ -22938,14 +23106,14 @@ Run \`openwop canvas-collab --help\` for usage.`);
22938
23106
  }
22939
23107
 
22940
23108
  // src/cli/workflowCollab.ts
22941
- var BASE20 = "/v1/host/openwop-app/workflow-collab";
23109
+ var BASE21 = "/v1/host/openwop-app/workflow-collab";
22942
23110
  var WORKFLOW_COLLAB_HELP = `Usage:
22943
23111
  openwop workflow-collab ticket <workflowId> [--json]
22944
23112
  openwop workflow-collab claim-seed <workflowId> [--json]
22945
23113
 
22946
23114
  \`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 }.
23115
+ (POST ${BASE21}/<workflowId>/ticket) \u2014 shown once, never stored. \`claim-seed\` runs
23116
+ the seeder election (POST ${BASE21}/<workflowId>/claim-seed) \u2192 { seed }.
22949
23117
 
22950
23118
  Exit codes: 0 ok \xB7 2 usage / not found / feature off \xB7 4 forbidden.
22951
23119
 
@@ -22972,11 +23140,11 @@ async function runWorkflowCollab(ctx, argv) {
22972
23140
  }
22973
23141
  switch (sub) {
22974
23142
  case "ticket": {
22975
- const res = await requestJson(ctx, `${BASE20}/${enc2(id)}/ticket`, { method: "POST", body: {} });
23143
+ const res = await requestJson(ctx, `${BASE21}/${enc2(id)}/ticket`, { method: "POST", body: {} });
22976
23144
  return printTicketOnce(ctx, res.body, `workflow ${id}`);
22977
23145
  }
22978
23146
  case "claim-seed": {
22979
- const res = await requestJson(ctx, `${BASE20}/${enc2(id)}/claim-seed`, { method: "POST", body: {} });
23147
+ const res = await requestJson(ctx, `${BASE21}/${enc2(id)}/claim-seed`, { method: "POST", body: {} });
22980
23148
  if (ctx.json) writeJson(ctx.io.stdout, res.body);
22981
23149
  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
23150
  return 0;
@@ -23020,7 +23188,7 @@ Run \`openwop canvas-packs --help\` for usage.`);
23020
23188
  }
23021
23189
 
23022
23190
  // src/cli/present.ts
23023
- var BASE21 = "/v1/host/openwop-app/present";
23191
+ var BASE22 = "/v1/host/openwop-app/present";
23024
23192
  var ACTIONS = ["next", "prev", "goto", "blank"];
23025
23193
  var PRESENT_HELP = `Usage:
23026
23194
  openwop present outline <token> [--json]
@@ -23030,11 +23198,11 @@ var PRESENT_HELP = `Usage:
23030
23198
  openwop present events <token> [--max <n>] [--timeout <seconds>] [--json]
23031
23199
 
23032
23200
  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
23201
+ credential). \`outline\` reads { title, frames, current? } (GET ${BASE22}/<token>/outline).
23202
+ \`command\` sends a navigation command (POST ${BASE22}/<token>/command, body
23035
23203
  { 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
23204
+ (POST ${BASE22}/<token>/state, body { current }). \`events\` follows the navigation
23205
+ feed (GET ${BASE22}/<token>/events, server-sent events) \u2014 one line per event, or one
23038
23206
  JSON object per line with --json; stops after --max events or --timeout seconds.
23039
23207
 
23040
23208
  Exit codes: 0 ok \xB7 2 usage / unknown or expired session.
@@ -23061,7 +23229,7 @@ async function runPresent(ctx, argv) {
23061
23229
  `);
23062
23230
  return 2;
23063
23231
  }
23064
- const base16 = `${BASE21}/${enc2(token)}`;
23232
+ const base16 = `${BASE22}/${enc2(token)}`;
23065
23233
  switch (sub) {
23066
23234
  case "outline": {
23067
23235
  const res = await requestJson(ctx, `${base16}/outline`, { auth: false });
@@ -24069,8 +24237,8 @@ async function runCampaignBrief(ctx, argv) {
24069
24237
  }
24070
24238
 
24071
24239
  // src/cli/campaignConnectors.ts
24072
- var BASE22 = `${APP}/campaign-connectors`;
24073
- var orgBase2 = (org2) => `${BASE22}/orgs/${enc2(org2)}`;
24240
+ var BASE23 = `${APP}/campaign-connectors`;
24241
+ var orgBase2 = (org2) => `${BASE23}/orgs/${enc2(org2)}`;
24074
24242
  var CAMPAIGN_CONNECTORS_HELP = `Usage:
24075
24243
  openwop campaign-connectors platforms [--json]
24076
24244
  openwop campaign-connectors sync --org <orgId> --platform meta|google [--json]
@@ -24119,7 +24287,7 @@ var TABLE2 = {
24119
24287
  usage: "platforms [--json]",
24120
24288
  args: 0,
24121
24289
  run: async (ctx) => {
24122
- const body = (await requestJson(ctx, `${BASE22}/platforms`)).body;
24290
+ const body = (await requestJson(ctx, `${BASE23}/platforms`)).body;
24123
24291
  if (ctx.json) {
24124
24292
  writeJson(ctx.io.stdout, body);
24125
24293
  return 0;
@@ -24136,7 +24304,7 @@ var TABLE2 = {
24136
24304
  requires: ["platform"],
24137
24305
  run: async (ctx, a) => {
24138
24306
  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;
24307
+ const body = (await requestJson(ctx, `${BASE23}/sync`, { method: "POST", body: req2 })).body;
24140
24308
  return done(ctx, body, `Sync ${body?.outcome ?? "done"}${body?.imported !== void 0 ? ` (${body.imported} imported)` : ""}.`);
24141
24309
  }
24142
24310
  },
@@ -24149,7 +24317,7 @@ var TABLE2 = {
24149
24317
  run: async (ctx, a) => {
24150
24318
  const o = a.options;
24151
24319
  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;
24320
+ const body = (await requestJson(ctx, `${BASE23}/audience-sync`, { method: "POST", body: req2 })).body;
24153
24321
  return done(ctx, body, `Audience sync: ${body?.outcome ?? "done"}.`);
24154
24322
  }
24155
24323
  },
@@ -24172,7 +24340,7 @@ var TABLE2 = {
24172
24340
  writeLine(ctx.io.stderr, "openwop: missing --csv-file");
24173
24341
  return 2;
24174
24342
  }
24175
- const body = (await requestJson(ctx, `${BASE22}/import`, { method: "POST", body: req2 })).body;
24343
+ const body = (await requestJson(ctx, `${BASE23}/import`, { method: "POST", body: req2 })).body;
24176
24344
  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
24345
  }
24178
24346
  },
@@ -24182,7 +24350,7 @@ var TABLE2 = {
24182
24350
  value: ["--org", "--campaign"],
24183
24351
  run: async (ctx, a) => listOut(
24184
24352
  ctx,
24185
- (await requestJson(ctx, `${BASE22}/records${qs({ orgId: orgOf(a), campaignId: a.options.campaign })}`)).body,
24353
+ (await requestJson(ctx, `${BASE23}/records${qs({ orgId: orgOf(a), campaignId: a.options.campaign })}`)).body,
24186
24354
  "records",
24187
24355
  ["date", "platform", "campaignId", "impressions", "clicks", "spend", "conversions"],
24188
24356
  "No performance records."
@@ -24192,7 +24360,7 @@ var TABLE2 = {
24192
24360
  usage: "kpi --org <orgId> [--campaign <id>] [--json]",
24193
24361
  args: 0,
24194
24362
  value: ["--org", "--campaign"],
24195
- run: async (ctx, a) => detail(ctx, (await requestJson(ctx, `${BASE22}/kpi${qs({ orgId: orgOf(a), campaignId: a.options.campaign })}`)).body)
24363
+ run: async (ctx, a) => detail(ctx, (await requestJson(ctx, `${BASE23}/kpi${qs({ orgId: orgOf(a), campaignId: a.options.campaign })}`)).body)
24196
24364
  },
24197
24365
  "sync-status": {
24198
24366
  usage: "sync-status --org <orgId> [--json]",
@@ -24314,7 +24482,7 @@ async function runCampaignConnectors(ctx, argv) {
24314
24482
  }
24315
24483
 
24316
24484
  // src/cli/campaignIntel.ts
24317
- var BASE23 = `${APP}/campaign-intel`;
24485
+ var BASE24 = `${APP}/campaign-intel`;
24318
24486
  var CAMPAIGN_INTEL_HELP = `Usage:
24319
24487
  openwop campaign-intel budget --org <orgId> [--campaign <id>] [--json]
24320
24488
  openwop campaign-intel forecast --org <orgId> [--campaign <id>] [--json]
@@ -24353,7 +24521,7 @@ var read = (name, withCampaign) => ({
24353
24521
  args: 0,
24354
24522
  value: ["--org", ...withCampaign ? ["--campaign"] : []],
24355
24523
  run: async (ctx, a) => {
24356
- const body = (await requestJson(ctx, `${BASE23}/${name}${qs({ orgId: orgOf2(a), campaignId: withCampaign ? a.options.campaign : void 0 })}`)).body;
24524
+ const body = (await requestJson(ctx, `${BASE24}/${name}${qs({ orgId: orgOf2(a), campaignId: withCampaign ? a.options.campaign : void 0 })}`)).body;
24357
24525
  if (name === "anomalies") return listOut(ctx, body, "anomalies", ["date", "platform", "campaignId", "metric", "severity", "message"], "No anomalies.");
24358
24526
  if (name === "forecast") return listOut(ctx, body, "forecasts", ["campaignId", "platform", "horizonDays", "projectedSpend", "projectedConversions"], "No forecasts (no performance records).");
24359
24527
  return detail(ctx, body);
@@ -24388,7 +24556,7 @@ var TABLE3 = {
24388
24556
  return 2;
24389
24557
  }
24390
24558
  }
24391
- return detail(ctx, (await requestJson(ctx, `${BASE23}/plan-budget`, { method: "POST", body: req2 })).body);
24559
+ return detail(ctx, (await requestJson(ctx, `${BASE24}/plan-budget`, { method: "POST", body: req2 })).body);
24392
24560
  }
24393
24561
  },
24394
24562
  apply: {
@@ -24413,7 +24581,7 @@ var TABLE3 = {
24413
24581
  dailyBudgetMinor: o.dailyBudgetMinor !== void 0 ? num(o.dailyBudgetMinor, "--daily-budget-minor") : void 0,
24414
24582
  dryRun: dryRun ? true : void 0
24415
24583
  });
24416
- const body = (await requestJson(ctx, `${BASE23}/recommendations/apply`, { method: "POST", body: req2 })).body;
24584
+ const body = (await requestJson(ctx, `${BASE24}/recommendations/apply`, { method: "POST", body: req2 })).body;
24417
24585
  return done(ctx, body, `Budget ${dryRun ? "preview" : "update"}: ${body?.outcome ?? "done"}.`);
24418
24586
  }
24419
24587
  }
@@ -24473,7 +24641,7 @@ async function runCampaignJourneys(ctx, argv) {
24473
24641
  }
24474
24642
 
24475
24643
  // src/cli/cdp.ts
24476
- var BASE24 = `${APP}/cdp`;
24644
+ var BASE25 = `${APP}/cdp`;
24477
24645
  var CDP_HELP = `Usage:
24478
24646
  openwop cdp resolve --type <identifierType> --value <v> [--json]
24479
24647
  openwop cdp schemas list [--json]
@@ -24520,7 +24688,7 @@ var limited = (path, key, columns, empty) => ({
24520
24688
  usage: `${path === "governance-decisions" ? "governance" : path === "collected-events" ? "events" : path} [--limit <n>] [--json]`,
24521
24689
  args: 0,
24522
24690
  value: ["--limit"],
24523
- run: async (ctx, a) => listOut(ctx, (await requestJson(ctx, `${BASE24}/${path}${qs({ limit: a.options.limit })}`)).body, key, columns, empty)
24691
+ run: async (ctx, a) => listOut(ctx, (await requestJson(ctx, `${BASE25}/${path}${qs({ limit: a.options.limit })}`)).body, key, columns, empty)
24524
24692
  });
24525
24693
  var TABLE5 = {
24526
24694
  resolve: {
@@ -24528,7 +24696,7 @@ var TABLE5 = {
24528
24696
  args: 0,
24529
24697
  value: ["--type", "--value"],
24530
24698
  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)
24699
+ run: async (ctx, a) => detail(ctx, (await requestJson(ctx, `${BASE25}/identity/resolve${qs({ type: a.options.type, value: a.options.value })}`)).body)
24532
24700
  },
24533
24701
  collect: {
24534
24702
  usage: "collect --event-type <t> [--payload-json '{...}'] [--dedupe-key <k>] [--json]",
@@ -24543,7 +24711,7 @@ var TABLE5 = {
24543
24711
  payload: o.payloadJson !== void 0 ? parseJsonFlag2(o.payloadJson, "--payload-json") : void 0,
24544
24712
  dedupeKey: o.dedupeKey
24545
24713
  });
24546
- const body = (await requestJson(ctx, `${BASE24}/collect`, { method: "POST", body: req2 })).body;
24714
+ const body = (await requestJson(ctx, `${BASE25}/collect`, { method: "POST", body: req2 })).body;
24547
24715
  return done(ctx, body, `Collected ${req2.eventType} event ${body?.eventId ?? body?.event?.eventId ?? ""}${body?.deduped ? " (deduplicated)" : ""}.`);
24548
24716
  }
24549
24717
  },
@@ -24558,7 +24726,7 @@ var TABLE5 = {
24558
24726
  writeLine(ctx.io.stderr, "openwop: missing --events-json or --events-file");
24559
24727
  return 2;
24560
24728
  }
24561
- return detail(ctx, (await requestJson(ctx, `${BASE24}/collect/batch`, { method: "POST", body: req2 })).body);
24729
+ return detail(ctx, (await requestJson(ctx, `${BASE25}/collect/batch`, { method: "POST", body: req2 })).body);
24562
24730
  }
24563
24731
  },
24564
24732
  import: {
@@ -24578,7 +24746,7 @@ var TABLE5 = {
24578
24746
  writeLine(ctx.io.stderr, "openwop: missing --csv-file");
24579
24747
  return 2;
24580
24748
  }
24581
- return detail(ctx, (await requestJson(ctx, `${BASE24}/collect/import`, { method: "POST", body: req2 })).body);
24749
+ return detail(ctx, (await requestJson(ctx, `${BASE25}/collect/import`, { method: "POST", body: req2 })).body);
24582
24750
  }
24583
24751
  },
24584
24752
  events: limited("collected-events", "events", ["eventId", "eventType", ["at", (e) => e.at ?? e.receivedAt ?? e.createdAt], "dedupeKey"], "No collected events."),
@@ -24588,7 +24756,7 @@ var TABLE5 = {
24588
24756
  usage: "audit-verify [--json]",
24589
24757
  args: 0,
24590
24758
  run: async (ctx) => {
24591
- const body = (await requestJson(ctx, `${BASE24}/audit-chain/verify`)).body;
24759
+ const body = (await requestJson(ctx, `${BASE25}/audit-chain/verify`)).body;
24592
24760
  if (ctx.json) {
24593
24761
  writeJson(ctx.io.stdout, body);
24594
24762
  return body?.valid === false || body?.ok === false ? 1 : 0;
@@ -24606,7 +24774,7 @@ var SCHEMAS = {
24606
24774
  args: 0,
24607
24775
  run: async (ctx) => listOut(
24608
24776
  ctx,
24609
- (await requestJson(ctx, `${BASE24}/event-schemas`)).body,
24777
+ (await requestJson(ctx, `${BASE25}/event-schemas`)).body,
24610
24778
  "schemas",
24611
24779
  ["eventType", "version", ["updatedAt", (s) => s.updatedAt ?? s.registeredAt ?? s.createdAt]],
24612
24780
  "No event schemas registered."
@@ -24627,7 +24795,7 @@ var SCHEMAS = {
24627
24795
  writeLine(ctx.io.stderr, "openwop: missing --schema-json or --schema-file");
24628
24796
  return 2;
24629
24797
  }
24630
- const body = (await requestJson(ctx, `${BASE24}/event-schemas`, { method: "POST", body: req2 })).body;
24798
+ const body = (await requestJson(ctx, `${BASE25}/event-schemas`, { method: "POST", body: req2 })).body;
24631
24799
  return done(ctx, body, `Registered the ${req2.eventType} event schema${body?.version !== void 0 ? ` (version ${body.version})` : ""}.`);
24632
24800
  }
24633
24801
  },
@@ -24640,7 +24808,7 @@ var SCHEMAS = {
24640
24808
  const req2 = assign({ ...a.body }, { payload: jsonFrom(ctx, a.options.payloadJson, a.options.payloadFile, "--payload-json", "--payload-file") });
24641
24809
  let body;
24642
24810
  try {
24643
- body = (await requestJson(ctx, `${BASE24}/event-schemas/${enc2(a.positionals[0])}/validate`, { method: "POST", body: req2 })).body;
24811
+ body = (await requestJson(ctx, `${BASE25}/event-schemas/${enc2(a.positionals[0])}/validate`, { method: "POST", body: req2 })).body;
24644
24812
  } catch (err) {
24645
24813
  if (!(err instanceof HttpError) || err.status !== 422) throw err;
24646
24814
  body = err.body;
@@ -24664,7 +24832,7 @@ async function runCdp(ctx, argv) {
24664
24832
  }
24665
24833
 
24666
24834
  // src/cli/destinationSync.ts
24667
- var BASE25 = `${APP}/destination-sync/syncs`;
24835
+ var BASE26 = `${APP}/destination-sync/syncs`;
24668
24836
  var DESTINATION_SYNC_HELP = `Usage:
24669
24837
  openwop destination-sync list [--json]
24670
24838
  openwop destination-sync get <syncId> [--json]
@@ -24698,14 +24866,14 @@ function jsonFrom2(ctx, inline, file, inlineFlag, fileFlag) {
24698
24866
  if (file !== void 0) return parseJsonFlag2(readText(ctx, String(file), fileFlag), fileFlag);
24699
24867
  return void 0;
24700
24868
  }
24701
- var item = (id) => `${BASE25}/${enc2(id)}`;
24869
+ var item = (id) => `${BASE26}/${enc2(id)}`;
24702
24870
  var TABLE6 = {
24703
24871
  list: {
24704
24872
  usage: "list [--json]",
24705
24873
  args: 0,
24706
24874
  run: async (ctx) => listOut(
24707
24875
  ctx,
24708
- (await requestJson(ctx, BASE25)).body,
24876
+ (await requestJson(ctx, BASE26)).body,
24709
24877
  "syncs",
24710
24878
  [["id", (s) => s.id ?? s.syncId], "name", "destinationKind", "sourceObject", "syncMode", "cursor"],
24711
24879
  "No destination syncs."
@@ -24733,7 +24901,7 @@ var TABLE6 = {
24733
24901
  cursorField: o.cursorField,
24734
24902
  connectionId: o.connection
24735
24903
  });
24736
- const body = (await requestJson(ctx, BASE25, { method: "POST", body: req2 })).body;
24904
+ const body = (await requestJson(ctx, BASE26, { method: "POST", body: req2 })).body;
24737
24905
  return done(ctx, body, `Created destination sync ${body?.id ?? body?.syncId ?? ""} (${req2.name}).`);
24738
24906
  }
24739
24907
  },
@@ -25563,7 +25731,7 @@ async function runPublic(ctx, argv) {
25563
25731
  }
25564
25732
 
25565
25733
  // src/cli/vault.ts
25566
- var BASE26 = "/v1/host/openwop-app/vault";
25734
+ var BASE27 = "/v1/host/openwop-app/vault";
25567
25735
  var SURFACE2 = "The secrets vault";
25568
25736
  var VAULT_HELP = `Usage:
25569
25737
  openwop vault list [--json]
@@ -25575,10 +25743,10 @@ The super-admin secrets vault (host extension). Every route is SUPER-ADMIN gated
25575
25743
  without a super-admin principal the command fails closed with exit 4 and says how to
25576
25744
  get one (OPENWOP_SUPERADMIN_TENANTS on the server).
25577
25745
 
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)
25746
+ list GET ${BASE27} inventory of refs (never values)
25747
+ set POST ${BASE27}/secrets store a new secret under a ref
25748
+ rotate POST ${BASE27}/secrets/:credentialRef/rotate overwrite an EXISTING secret
25749
+ delete DELETE ${BASE27}/secrets/:credentialRef remove (refused while the ref has live consumers unless --force)
25582
25750
 
25583
25751
  SECRETS ARE REFS, NEVER VALUES. The value for set/rotate is read from --value-file or a
25584
25752
  no-echo prompt (a piped line also works) \u2014 never from a command-line flag. The host's
@@ -25634,7 +25802,7 @@ async function vaultList(ctx, argv) {
25634
25802
  write(ctx.io.stdout, VAULT_HELP);
25635
25803
  return 0;
25636
25804
  }
25637
- const res = await gatedRequest(ctx, BASE26, void 0, SURFACE2, "superadmin");
25805
+ const res = await gatedRequest(ctx, BASE27, void 0, SURFACE2, "superadmin");
25638
25806
  if (ctx.json) {
25639
25807
  writeJson(ctx.io.stdout, res.body);
25640
25808
  return 0;
@@ -25665,7 +25833,7 @@ async function vaultWrite(ctx, argv, verb) {
25665
25833
  const scope = scopeOf(options);
25666
25834
  const value = await readSecretInput(ctx, options, `Secret value for "${ref}"`);
25667
25835
  const body = { value, ...scope === "host" ? { scope: "host" } : {} };
25668
- const path = verb === "set" ? `${BASE26}/secrets` : `${BASE26}/secrets/${encodeURIComponent(ref)}/rotate`;
25836
+ const path = verb === "set" ? `${BASE27}/secrets` : `${BASE27}/secrets/${encodeURIComponent(ref)}/rotate`;
25669
25837
  if (verb === "set") body.credentialRef = ref;
25670
25838
  const res = await gatedRequest(ctx, path, { method: "POST", body }, SURFACE2, "superadmin");
25671
25839
  if (ctx.json) {
@@ -25689,7 +25857,7 @@ async function vaultDelete(ctx, argv) {
25689
25857
  if (scopeOf(options) === "host") qs2.set("scope", "host");
25690
25858
  if (options.force) qs2.set("force", "true");
25691
25859
  const q = qs2.toString() ? `?${qs2}` : "";
25692
- const res = await gatedRequest(ctx, `${BASE26}/secrets/${encodeURIComponent(positionals[0])}${q}`, { method: "DELETE" }, SURFACE2, "superadmin");
25860
+ const res = await gatedRequest(ctx, `${BASE27}/secrets/${encodeURIComponent(positionals[0])}${q}`, { method: "DELETE" }, SURFACE2, "superadmin");
25693
25861
  if (ctx.json) {
25694
25862
  writeJson(ctx.io.stdout, res.body);
25695
25863
  return 0;
@@ -25699,27 +25867,40 @@ async function vaultDelete(ctx, argv) {
25699
25867
  }
25700
25868
 
25701
25869
  // src/cli/developerKeys.ts
25702
- var BASE27 = "/v1/host/openwop-app/developer-keys";
25870
+ var BASE28 = "/v1/host/openwop-app/developer-keys";
25703
25871
  var SURFACE3 = "Managing developer API keys";
25704
25872
  var DEVELOPER_KEYS_HELP = `Usage:
25705
25873
  openwop developer-keys list [--json]
25706
25874
  openwop developer-keys create --name <n> [--scope <s>]... [--expires-at <iso>] [--json]
25707
25875
  openwop developer-keys revoke <keyId> --yes [--json]
25708
25876
 
25709
- Developer API keys for this workspace (host extension under ${BASE27}). Requires an
25877
+ Developer API keys for this workspace (host extension under ${BASE28}). Requires an
25710
25878
  authenticated principal (not an anonymous session); an admin or owner sees and can
25711
25879
  revoke every key in the workspace, anyone else only their own.
25712
25880
 
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)
25881
+ list GET ${BASE28} keys (public fields only \u2014 never the token or its hash)
25882
+ create POST ${BASE28} issue a key; the token is shown ONCE
25883
+ revoke DELETE ${BASE28}/:keyId revoke (404 when the key is not yours / not in this workspace)
25716
25884
 
25717
25885
  ONE-TIME SECRET: \`create\` prints the new token exactly once, on stdout, with a warning.
25718
25886
  Store it immediately \u2014 the host keeps only a hash and cannot show it again. The CLI does
25719
25887
  not save it anywhere.
25720
25888
 
25889
+ A KEY CANNOT MANAGE KEYS: openwop-app refuses \`list\`, \`create\` and \`revoke\` (403)
25890
+ when the CLI is itself signed in with a developer key (owk_\u2026). Create and revoke keys
25891
+ in the app (Access \u2192 API keys), or sign in with an operator key.
25892
+
25893
+ SCOPES ARE REQUIRED AND NARROW: openwop-app refuses a key with no --scope, and a key is
25894
+ limited to exactly the scopes it declares. To start a run and read it back a key needs
25895
+ runs:create AND runs:read; reaching the rest of the workspace (agents, memory, BYOK)
25896
+ needs workspace:read / workspace:write. \`--scope '*'\` is a key with your full authority.
25897
+ A scope the host does not know is refused by name.
25898
+
25899
+ EXPIRY: openwop-app gives a key 90 days unless --expires-at says otherwise, and refuses
25900
+ an expiry in the past or more than its maximum (366 days by default) away.
25901
+
25721
25902
  --name <n> (create) Display name. Required.
25722
- --scope <s> (create) Scope granted to the key (repeatable).
25903
+ --scope <s> (create) Scope granted to the key (repeatable; at least one).
25723
25904
  --expires-at <iso> (create) Expiry timestamp.
25724
25905
  --yes (revoke) Required confirmation.
25725
25906
 
@@ -25727,7 +25908,7 @@ Exit codes: 0 ok \xB7 2 usage / not found \xB7 4 not signed in.
25727
25908
 
25728
25909
  Examples:
25729
25910
  openwop developer-keys list
25730
- openwop developer-keys create --name "CI deploy" --scope runs:write
25911
+ openwop developer-keys create --name "CI deploy" --scope runs:create --scope runs:read
25731
25912
  openwop developer-keys revoke dk:0123abcd --yes
25732
25913
  `;
25733
25914
  async function runDeveloperKeys(ctx, argv) {
@@ -25756,7 +25937,7 @@ async function keysList(ctx, argv) {
25756
25937
  write(ctx.io.stdout, DEVELOPER_KEYS_HELP);
25757
25938
  return 0;
25758
25939
  }
25759
- const res = await gatedRequest(ctx, BASE27, void 0, SURFACE3, "signed-in");
25940
+ const res = await gatedRequest(ctx, BASE28, void 0, SURFACE3, "signed-in");
25760
25941
  if (ctx.json) {
25761
25942
  writeJson(ctx.io.stdout, res.body);
25762
25943
  return 0;
@@ -25785,7 +25966,7 @@ async function keysCreate(ctx, argv) {
25785
25966
  const body = { name: options.name };
25786
25967
  if (Array.isArray(options.scope) && options.scope.length) body.scopes = options.scope;
25787
25968
  if (options.expiresAt) body.expiresAt = options.expiresAt;
25788
- const res = await gatedRequest(ctx, BASE27, { method: "POST", body }, SURFACE3, "signed-in");
25969
+ const res = await gatedRequest(ctx, BASE28, { method: "POST", body }, SURFACE3, "signed-in");
25789
25970
  writeLine(ctx.io.stderr, "WARNING: this token is shown ONCE. Store it now \u2014 it cannot be retrieved again.");
25790
25971
  if (ctx.json) {
25791
25972
  writeJson(ctx.io.stdout, res.body);
@@ -25806,7 +25987,7 @@ async function keysRevoke(ctx, argv) {
25806
25987
  writeLine(ctx.io.stderr, `Refusing to revoke ${positionals[0]} without --yes.`);
25807
25988
  return 2;
25808
25989
  }
25809
- await gatedRequest(ctx, `${BASE27}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" }, SURFACE3, "signed-in");
25990
+ await gatedRequest(ctx, `${BASE28}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" }, SURFACE3, "signed-in");
25810
25991
  if (ctx.json) {
25811
25992
  writeJson(ctx.io.stdout, { keyId: positionals[0], revoked: true });
25812
25993
  return 0;
@@ -25875,44 +26056,44 @@ Run \`openwop custom-domains --help\` for usage.`);
25875
26056
  })), ["hostname", "status", "verifiedAt", "lastError"]));
25876
26057
  return 0;
25877
26058
  }
25878
- const hostname = positionals[0];
25879
- if (!hostname) throw new CliError(`Usage: openwop custom-domains ${sub} <hostname> --org <orgId>`, 2);
26059
+ const hostname2 = positionals[0];
26060
+ if (!hostname2) throw new CliError(`Usage: openwop custom-domains ${sub} <hostname> --org <orgId>`, 2);
25880
26061
  if (sub === "add") {
25881
- const res2 = await requestJson(ctx, base14(orgId), { method: "POST", body: { hostname } });
26062
+ const res2 = await requestJson(ctx, base14(orgId), { method: "POST", body: { hostname: hostname2 } });
25882
26063
  if (ctx.json) {
25883
26064
  writeJson(ctx.io.stdout, res2.body);
25884
26065
  return 0;
25885
26066
  }
25886
26067
  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}\`.`);
26068
+ writeLine(ctx.io.stdout, `Added ${d.hostname ?? hostname2} (status ${d.status ?? "pending"}).`);
26069
+ 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
26070
  return 0;
25890
26071
  }
25891
26072
  if (sub === "verify") {
25892
- const res2 = await requestJson(ctx, `${base14(orgId)}/${encodeURIComponent(hostname)}/verify`, { method: "POST", body: {} });
26073
+ const res2 = await requestJson(ctx, `${base14(orgId)}/${encodeURIComponent(hostname2)}/verify`, { method: "POST", body: {} });
25893
26074
  if (ctx.json) {
25894
26075
  writeJson(ctx.io.stdout, res2.body);
25895
26076
  return 0;
25896
26077
  }
25897
26078
  const d = res2.body?.domain ?? {};
25898
- writeLine(ctx.io.stdout, `${d.hostname ?? hostname}: ${d.status ?? "?"}${d.lastError ? ` \u2014 ${d.lastError}` : ""}`);
26079
+ writeLine(ctx.io.stdout, `${d.hostname ?? hostname2}: ${d.status ?? "?"}${d.lastError ? ` \u2014 ${d.lastError}` : ""}`);
25899
26080
  return d.status === "live" ? 0 : 3;
25900
26081
  }
25901
26082
  if (!options.yes) {
25902
- writeLine(ctx.io.stderr, `Refusing to remove ${hostname} without --yes.`);
26083
+ writeLine(ctx.io.stderr, `Refusing to remove ${hostname2} without --yes.`);
25903
26084
  return 2;
25904
26085
  }
25905
- const res = await requestJson(ctx, `${base14(orgId)}/${encodeURIComponent(hostname)}`, { method: "DELETE" });
26086
+ const res = await requestJson(ctx, `${base14(orgId)}/${encodeURIComponent(hostname2)}`, { method: "DELETE" });
25906
26087
  if (ctx.json) {
25907
26088
  writeJson(ctx.io.stdout, res.body);
25908
26089
  return 0;
25909
26090
  }
25910
- writeLine(ctx.io.stdout, `Removed ${hostname}.`);
26091
+ writeLine(ctx.io.stdout, `Removed ${hostname2}.`);
25911
26092
  return 0;
25912
26093
  }
25913
26094
 
25914
26095
  // src/cli/environments.ts
25915
- var BASE28 = "/v1/host/openwop-app/environments";
26096
+ var BASE29 = "/v1/host/openwop-app/environments";
25916
26097
  var PROTECTION = ["open", "protected", "locked"];
25917
26098
  var ENVIRONMENTS_HELP = `Usage:
25918
26099
  openwop environments list [--drift] [--json]
@@ -25929,21 +26110,21 @@ var ENVIRONMENTS_HELP = `Usage:
25929
26110
  openwop environments apply --snapshot <hash> [--json]
25930
26111
  openwop environments promotions [--json]
25931
26112
 
25932
- Config environments + promotion (host extension under ${BASE28}; toggle 'environments').
26113
+ Config environments + promotion (host extension under ${BASE29}; toggle 'environments').
25933
26114
  Reads need workspace:read; every write needs host:members:manage in the workspace.
25934
26115
 
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
26116
+ list GET ${BASE29}[?drift=1] environments, config domains, app version
26117
+ create POST ${BASE29} add an environment to the chain
26118
+ ensure-chain POST ${BASE29}/ensure-chain create the default dev \u2192 staging \u2192 prod chain
26119
+ protect PATCH ${BASE29}/:name/protection set open | protected | locked
26120
+ settings GET/PATCH ${BASE29}/settings the promotion-approval gate
26121
+ snapshots GET ${BASE29}/snapshots stored config snapshots
26122
+ snapshot POST ${BASE29}/snapshots snapshot the current live config
26123
+ preview POST ${BASE29}/preview diff a snapshot against an environment (no change)
26124
+ promote POST ${BASE29}/promote promote an env's snapshot to the next (or --to) env
26125
+ rollback POST ${BASE29}/rollback point an env back at a historical snapshot
26126
+ apply POST ${BASE29}/apply apply a snapshot to the live config
26127
+ promotions GET ${BASE29}/promotions the promotion ledger
25947
26128
 
25948
26129
  Exit codes: 0 ok \xB7 3 queued for approval (202 pending_approval) \xB7 2 usage / not found /
25949
26130
  feature off \xB7 4 permission denied.
@@ -25976,7 +26157,7 @@ async function runEnvironments(ctx, argv) {
25976
26157
  };
25977
26158
  switch (sub) {
25978
26159
  case "list": {
25979
- const res = await requestJson(ctx, `${BASE28}${options.drift ? "?drift=1" : ""}`);
26160
+ const res = await requestJson(ctx, `${BASE29}${options.drift ? "?drift=1" : ""}`);
25980
26161
  emit2(res.body, () => {
25981
26162
  const envs = arrayOf(res.body, "environments");
25982
26163
  if (envs.length === 0) {
@@ -26003,35 +26184,35 @@ async function runEnvironments(ctx, argv) {
26003
26184
  body.order = n;
26004
26185
  }
26005
26186
  if (options.protection !== void 0) body.protection = protectionOf(options.protection);
26006
- const res = await requestJson(ctx, BASE28, { method: "POST", body });
26187
+ const res = await requestJson(ctx, BASE29, { method: "POST", body });
26007
26188
  emit2(res.body, () => writeLine(ctx.io.stdout, `Created environment ${res.body?.name ?? name} (order ${res.body?.order ?? "?"}, ${res.body?.protection ?? "?"}).`));
26008
26189
  return 0;
26009
26190
  }
26010
26191
  case "ensure-chain": {
26011
- const res = await requestJson(ctx, `${BASE28}/ensure-chain`, { method: "POST", body: {} });
26192
+ const res = await requestJson(ctx, `${BASE29}/ensure-chain`, { method: "POST", body: {} });
26012
26193
  emit2(res.body, () => writeLine(ctx.io.stdout, `Chain: ${arrayOf(res.body, "environments").map((e) => e.name).join(" \u2192 ")}`));
26013
26194
  return 0;
26014
26195
  }
26015
26196
  case "protect": {
26016
26197
  const name = positionals[0];
26017
26198
  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) } });
26199
+ const res = await requestJson(ctx, `${BASE29}/${encodeURIComponent(name)}/protection`, { method: "PATCH", body: { protection: protectionOf(options.protection) } });
26019
26200
  emit2(res.body, () => writeLine(ctx.io.stdout, `${name} is now ${res.body?.protection ?? options.protection}.`));
26020
26201
  return 0;
26021
26202
  }
26022
26203
  case "settings": {
26023
26204
  if (positionals[0] === "set") {
26024
26205
  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) } });
26206
+ const res2 = await requestJson(ctx, `${BASE29}/settings`, { method: "PATCH", body: { requireApprovalForPromotion: parseBool("--require-approval", options.requireApproval) } });
26026
26207
  emit2(res2.body, () => writeLine(ctx.io.stdout, `requireApprovalForPromotion: ${res2.body?.requireApprovalForPromotion}`));
26027
26208
  return 0;
26028
26209
  }
26029
- const res = await requestJson(ctx, `${BASE28}/settings`);
26210
+ const res = await requestJson(ctx, `${BASE29}/settings`);
26030
26211
  emit2(res.body, () => writeLine(ctx.io.stdout, `requireApprovalForPromotion: ${res.body?.requireApprovalForPromotion ?? false}`));
26031
26212
  return 0;
26032
26213
  }
26033
26214
  case "snapshots": {
26034
- const res = await requestJson(ctx, `${BASE28}/snapshots`);
26215
+ const res = await requestJson(ctx, `${BASE29}/snapshots`);
26035
26216
  emit2(res.body, () => {
26036
26217
  const snaps = arrayOf(res.body, "snapshots");
26037
26218
  if (snaps.length === 0) {
@@ -26051,13 +26232,13 @@ async function runEnvironments(ctx, argv) {
26051
26232
  case "snapshot": {
26052
26233
  const body = {};
26053
26234
  if (options.sourceEnv) body.sourceEnv = options.sourceEnv;
26054
- const res = await requestJson(ctx, `${BASE28}/snapshots`, { method: "POST", body });
26235
+ const res = await requestJson(ctx, `${BASE29}/snapshots`, { method: "POST", body });
26055
26236
  emit2(res.body, () => writeLine(ctx.io.stdout, `Snapshot ${res.body?.hash ?? "?"}${res.body?.sourceEnv ? ` (from ${res.body.sourceEnv})` : ""}.`));
26056
26237
  return 0;
26057
26238
  }
26058
26239
  case "preview": {
26059
26240
  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 } });
26241
+ const res = await requestJson(ctx, `${BASE29}/preview`, { method: "POST", body: { toEnv: options.to, snapshotHash: options.snapshot } });
26061
26242
  writeJson(ctx.io.stdout, res.body);
26062
26243
  return 0;
26063
26244
  }
@@ -26065,18 +26246,18 @@ async function runEnvironments(ctx, argv) {
26065
26246
  if (!options.from) throw new CliError("Usage: openwop environments promote --from <env> [--to <env>]", 2);
26066
26247
  const body = { fromEnv: options.from };
26067
26248
  if (options.to) body.toEnv = options.to;
26068
- return movement(ctx, await requestJson(ctx, `${BASE28}/promote`, { method: "POST", body }), "Promoted");
26249
+ return movement(ctx, await requestJson(ctx, `${BASE29}/promote`, { method: "POST", body }), "Promoted");
26069
26250
  }
26070
26251
  case "rollback": {
26071
26252
  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");
26253
+ return movement(ctx, await requestJson(ctx, `${BASE29}/rollback`, { method: "POST", body: { env: options.env, snapshotHash: options.snapshot } }), "Rolled back");
26073
26254
  }
26074
26255
  case "apply": {
26075
26256
  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");
26257
+ return movement(ctx, await requestJson(ctx, `${BASE29}/apply`, { method: "POST", body: { snapshotHash: options.snapshot } }), "Applied");
26077
26258
  }
26078
26259
  case "promotions": {
26079
- const res = await requestJson(ctx, `${BASE28}/promotions`);
26260
+ const res = await requestJson(ctx, `${BASE29}/promotions`);
26080
26261
  emit2(res.body, () => {
26081
26262
  const rows = arrayOf(res.body, "promotions");
26082
26263
  if (rows.length === 0) {
@@ -26121,7 +26302,7 @@ function movement(ctx, res, verb) {
26121
26302
  }
26122
26303
 
26123
26304
  // src/cli/billing.ts
26124
- var BASE29 = "/v1/host/openwop-app/billing";
26305
+ var BASE30 = "/v1/host/openwop-app/billing";
26125
26306
  var BILLING_HELP = `Usage:
26126
26307
  openwop billing subscription [--json]
26127
26308
  openwop billing balance [--json]
@@ -26139,7 +26320,7 @@ var BILLING_HELP = `Usage:
26139
26320
  openwop billing sync-seats [--json]
26140
26321
  openwop billing import (--body <json> | --body-file <path>) [--json]
26141
26322
 
26142
- Workspace billing (host extension under ${BASE29}; toggle 'billing').
26323
+ Workspace billing (host extension under ${BASE30}; toggle 'billing').
26143
26324
  subscription GET /subscription plan tier, status, seats, period
26144
26325
  balance GET /balance purchased token balance
26145
26326
  entitlements GET /entitlements the plan's allowed features + limits
@@ -26186,7 +26367,7 @@ async function runBilling(ctx, argv) {
26186
26367
  const superadmin = (path, body, surface) => gatedRequest(ctx, path, { method: "POST", body }, surface, "superadmin");
26187
26368
  switch (sub) {
26188
26369
  case "subscription": {
26189
- const res = await requestJson(ctx, `${BASE29}/subscription`);
26370
+ const res = await requestJson(ctx, `${BASE30}/subscription`);
26190
26371
  emit2(res.body, () => {
26191
26372
  const s = res.body ?? {};
26192
26373
  writeLine(ctx.io.stdout, `plan: ${s.planTier ?? "?"} (${s.status ?? "?"})`);
@@ -26197,12 +26378,12 @@ async function runBilling(ctx, argv) {
26197
26378
  return 0;
26198
26379
  }
26199
26380
  case "balance": {
26200
- const res = await requestJson(ctx, `${BASE29}/balance`);
26381
+ const res = await requestJson(ctx, `${BASE30}/balance`);
26201
26382
  emit2(res.body, () => writeLine(ctx.io.stdout, `tokens available: ${res.body?.totalAvailable ?? 0} (purchased total ${res.body?.purchasedTokensTotal ?? 0})`));
26202
26383
  return 0;
26203
26384
  }
26204
26385
  case "entitlements": {
26205
- const res = await requestJson(ctx, `${BASE29}/entitlements`);
26386
+ const res = await requestJson(ctx, `${BASE30}/entitlements`);
26206
26387
  emit2(res.body, () => {
26207
26388
  const e = res.body ?? {};
26208
26389
  writeLine(ctx.io.stdout, `plan: ${e.plan ?? "?"}`);
@@ -26213,7 +26394,7 @@ async function runBilling(ctx, argv) {
26213
26394
  return 0;
26214
26395
  }
26215
26396
  case "bundles": {
26216
- const res = await requestJson(ctx, `${BASE29}/bundles`);
26397
+ const res = await requestJson(ctx, `${BASE30}/bundles`);
26217
26398
  emit2(res.body, () => {
26218
26399
  const rows = arrayOf(res.body, "bundles");
26219
26400
  if (rows.length === 0) {
@@ -26233,19 +26414,19 @@ async function runBilling(ctx, argv) {
26233
26414
  const verb = positionals[0] ?? "list";
26234
26415
  if (verb === "get") {
26235
26416
  if (!positionals[1]) throw new CliError("Usage: openwop billing invoices get <invoiceId>", 2);
26236
- const res2 = await requestJson(ctx, `${BASE29}/invoices/${encodeURIComponent(positionals[1])}`);
26417
+ const res2 = await requestJson(ctx, `${BASE30}/invoices/${encodeURIComponent(positionals[1])}`);
26237
26418
  emit2(res2.body, () => writeLine(ctx.io.stdout, res2.body?.markdown ?? JSON.stringify(res2.body, null, 2)));
26238
26419
  return 0;
26239
26420
  }
26240
26421
  if (verb === "create") {
26241
26422
  const amount = Number(options.amount);
26242
26423
  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");
26424
+ const res2 = await superadmin(`${BASE30}/invoices`, { amount, ...options.currency ? { currency: options.currency } : {} }, "Generating an invoice");
26244
26425
  emit2(res2.body, () => writeLine(ctx.io.stdout, `Created invoice ${res2.body?.invoiceId ?? "?"} (${res2.body?.amount} ${res2.body?.currency}).`));
26245
26426
  return 0;
26246
26427
  }
26247
26428
  if (verb !== "list") throw new CliError(`Unknown invoices verb: ${verb}`, 2);
26248
- const res = await requestJson(ctx, `${BASE29}/invoices`);
26429
+ const res = await requestJson(ctx, `${BASE30}/invoices`);
26249
26430
  emit2(res.body, () => {
26250
26431
  const rows = arrayOf(res.body, "invoices");
26251
26432
  if (rows.length === 0) {
@@ -26263,18 +26444,18 @@ async function runBilling(ctx, argv) {
26263
26444
  }
26264
26445
  case "checkout": {
26265
26446
  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 } });
26447
+ const res = await requestJson(ctx, `${BASE30}/checkout`, { method: "POST", body: { priceId: options.price } });
26267
26448
  emit2(res.body, () => writeLine(ctx.io.stdout, `Open to complete checkout (${res.body?.mode ?? "?"}): ${res.body?.url ?? "?"}`));
26268
26449
  return 0;
26269
26450
  }
26270
26451
  case "bundle-checkout": {
26271
26452
  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: {} });
26453
+ const res = await requestJson(ctx, `${BASE30}/bundles/${encodeURIComponent(positionals[0])}/checkout`, { method: "POST", body: {} });
26273
26454
  emit2(res.body, () => writeLine(ctx.io.stdout, `Open to complete checkout (${res.body?.mode ?? "?"}): ${res.body?.url ?? "?"}`));
26274
26455
  return 0;
26275
26456
  }
26276
26457
  case "portal": {
26277
- const res = await requestJson(ctx, `${BASE29}/portal`, { method: "POST", body: {} });
26458
+ const res = await requestJson(ctx, `${BASE30}/portal`, { method: "POST", body: {} });
26278
26459
  emit2(res.body, () => writeLine(ctx.io.stdout, `Billing portal (${res.body?.mode ?? "?"}): ${res.body?.url ?? "?"}`));
26279
26460
  return 0;
26280
26461
  }
@@ -26287,19 +26468,19 @@ async function runBilling(ctx, argv) {
26287
26468
  if (!Number.isFinite(v)) throw new CliError("--value must be a number.", 2);
26288
26469
  body.value = v;
26289
26470
  }
26290
- const res = await superadmin(`${BASE29}/coupons`, body, "Creating a billing coupon");
26471
+ const res = await superadmin(`${BASE30}/coupons`, body, "Creating a billing coupon");
26291
26472
  emit2(res.body, () => writeLine(ctx.io.stdout, `Created coupon ${res.body?.code ?? options.code} (${res.body?.type} ${res.body?.value}).`));
26292
26473
  return 0;
26293
26474
  }
26294
26475
  case "sync-seats": {
26295
- const res = await superadmin(`${BASE29}/sync-seats`, {}, "Billing seat sync");
26476
+ const res = await superadmin(`${BASE30}/sync-seats`, {}, "Billing seat sync");
26296
26477
  emit2(res.body, () => writeLine(ctx.io.stdout, `Seats synced: ${res.body?.quantity ?? "?"}.`));
26297
26478
  return 0;
26298
26479
  }
26299
26480
  case "import": {
26300
26481
  const body = readBodyOption(ctx, options);
26301
26482
  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");
26483
+ const res = await superadmin(`${BASE30}/import`, body, "Billing import");
26303
26484
  emit2(res.body, () => writeLine(ctx.io.stdout, `Imported ${res.body?.subscriptions ?? 0} subscription(s), ${res.body?.balances ?? 0} balance(s).`));
26304
26485
  return 0;
26305
26486
  }
@@ -26370,15 +26551,15 @@ Run \`openwop site-config --help\` for usage.`);
26370
26551
  }
26371
26552
 
26372
26553
  // src/cli/runtimePosture.ts
26373
- var BASE30 = "/v1/host/openwop-app/runtime-posture";
26554
+ var BASE31 = "/v1/host/openwop-app/runtime-posture";
26374
26555
  var SURFACE5 = "Runtime posture";
26375
26556
  var RUNTIME_POSTURE_HELP = `Usage:
26376
26557
  openwop runtime-posture [get] [--json]
26377
26558
  openwop runtime-posture request --warm|--cold [--json]
26378
26559
 
26379
26560
  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
26561
+ get GET ${BASE31} live posture (serving revision, warm/cold, rollout)
26562
+ request POST ${BASE31}/change-requests ask for a warm/cold change; returns the commands to run
26382
26563
 
26383
26564
  A change request APPLIES NOTHING: the host records it in the audit log and returns the
26384
26565
  exact commands for an operator holding deploy rights. Without a super-admin principal
@@ -26402,7 +26583,7 @@ async function runRuntimePosture(ctx, argv) {
26402
26583
  return 0;
26403
26584
  }
26404
26585
  if (sub === "get") {
26405
- const res2 = await gatedRequest(ctx, BASE30, void 0, SURFACE5, "superadmin");
26586
+ const res2 = await gatedRequest(ctx, BASE31, void 0, SURFACE5, "superadmin");
26406
26587
  if (ctx.json) {
26407
26588
  writeJson(ctx.io.stdout, res2.body);
26408
26589
  return 0;
@@ -26421,7 +26602,7 @@ async function runRuntimePosture(ctx, argv) {
26421
26602
  if (sub !== "request") throw new CliError(`Unknown runtime-posture command: ${sub}
26422
26603
  Run \`openwop runtime-posture --help\` for usage.`);
26423
26604
  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");
26605
+ const res = await gatedRequest(ctx, `${BASE31}/change-requests`, { method: "POST", body: { warm: Boolean(options.warm) } }, SURFACE5, "superadmin");
26425
26606
  if (ctx.json) {
26426
26607
  writeJson(ctx.io.stdout, res.body);
26427
26608
  return 0;
@@ -26476,16 +26657,16 @@ Run \`openwop maintenance --help\` for usage.`);
26476
26657
  }
26477
26658
 
26478
26659
  // src/cli/menuConfig.ts
26479
- var BASE31 = "/v1/host/openwop-app/menu-config";
26660
+ var BASE32 = "/v1/host/openwop-app/menu-config";
26480
26661
  var MENU_CONFIG_HELP = `Usage:
26481
26662
  openwop menu-config [get] [--json]
26482
26663
  openwop menu-config set me (--body <json> | --body-file <path>) [--json]
26483
26664
  openwop menu-config set tenant (--body <json> | --body-file <path>) [--json]
26484
26665
 
26485
26666
  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
26667
+ get GET ${BASE32} {tenant, user} layers for the caller
26668
+ set me PUT ${BASE32}/me replace YOUR personal layer (signed-in user)
26669
+ set tenant PUT ${BASE32}/tenant [super-admin] replace the workspace default layer
26489
26670
 
26490
26671
  The body is the menu config object ({"items": {...}, "headers": [...]}) or the full
26491
26672
  request ({"config": {...}}). A write REPLACES that layer. \`set tenant\` reads the current
@@ -26511,7 +26692,7 @@ async function runMenuConfig(ctx, argv) {
26511
26692
  return 0;
26512
26693
  }
26513
26694
  if (sub === "get") {
26514
- const res2 = await requestJson(ctx, BASE31);
26695
+ const res2 = await requestJson(ctx, BASE32);
26515
26696
  writeJson(ctx.io.stdout, res2.body);
26516
26697
  return 0;
26517
26698
  }
@@ -26525,13 +26706,13 @@ Run \`openwop menu-config --help\` for usage.`);
26525
26706
  const body = "config" in raw ? raw : { config: raw };
26526
26707
  let res;
26527
26708
  if (layer === "me") {
26528
- res = await gatedRequest(ctx, `${BASE31}/me`, { method: "PUT", body }, "Editing your menu layout", "signed-in");
26709
+ res = await gatedRequest(ctx, `${BASE32}/me`, { method: "PUT", body }, "Editing your menu layout", "signed-in");
26529
26710
  } else {
26530
- const current = await requestJson(ctx, BASE31);
26711
+ const current = await requestJson(ctx, BASE32);
26531
26712
  const etag = current.headers.get("etag");
26532
26713
  res = await gatedRequest(
26533
26714
  ctx,
26534
- `${BASE31}/tenant`,
26715
+ `${BASE32}/tenant`,
26535
26716
  { method: "PUT", body, ...etag ? { headers: { "if-match": etag } } : {} },
26536
26717
  "Editing the workspace default menu layout",
26537
26718
  "superadmin"
@@ -26921,7 +27102,7 @@ function renderKnowledge(ctx, agentId, k) {
26921
27102
 
26922
27103
  // src/cli/compatEndpoints.ts
26923
27104
  import { readFileSync as readFileSync19 } from "node:fs";
26924
- var BASE32 = "/v1/host/openwop-app/compat-endpoints";
27105
+ var BASE33 = "/v1/host/openwop-app/compat-endpoints";
26925
27106
  var COMPAT_ENDPOINTS_HELP = `Usage:
26926
27107
  openwop compat-endpoints list --org <orgId> [--json]
26927
27108
  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 +27152,7 @@ async function runCompatEndpoints(ctx, argv) {
26971
27152
  switch (sub) {
26972
27153
  case "list": {
26973
27154
  const org2 = requireOrg(options.org);
26974
- const res = await requestJson(ctx, `${BASE32}?orgId=${encodeURIComponent(org2)}`);
27155
+ const res = await requestJson(ctx, `${BASE33}?orgId=${encodeURIComponent(org2)}`);
26975
27156
  if (ctx.json) {
26976
27157
  writeJson(ctx.io.stdout, res.body);
26977
27158
  return 0;
@@ -27004,7 +27185,7 @@ async function runCompatEndpoints(ctx, argv) {
27004
27185
  const apiKey = readApiKey(ctx, options);
27005
27186
  if (apiKey) body.apiKey = apiKey;
27006
27187
  if (Array.isArray(options.model) && options.model.length) body.models = options.model;
27007
- const res = await requestJson(ctx, BASE32, { method: "POST", body });
27188
+ const res = await requestJson(ctx, BASE33, { method: "POST", body });
27008
27189
  if (ctx.json) {
27009
27190
  writeJson(ctx.io.stdout, res.body);
27010
27191
  return 0;
@@ -27018,7 +27199,7 @@ async function runCompatEndpoints(ctx, argv) {
27018
27199
  writeLine(ctx.io.stderr, `Refusing to delete ${positionals[0]} (and its stored key) without --yes.`);
27019
27200
  return 2;
27020
27201
  }
27021
- await requestJson(ctx, `${BASE32}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
27202
+ await requestJson(ctx, `${BASE33}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
27022
27203
  writeLine(ctx.io.stdout, `Deleted compat endpoint ${positionals[0]}.`);
27023
27204
  return 0;
27024
27205
  }
@@ -27048,7 +27229,7 @@ function readApiKey(ctx, options) {
27048
27229
  }
27049
27230
 
27050
27231
  // src/cli/hostEvents.ts
27051
- var BASE33 = "/v1/host/openwop-app/host-events/bindings";
27232
+ var BASE34 = "/v1/host/openwop-app/host-events/bindings";
27052
27233
  var HOST_EVENTS_HELP = `Usage:
27053
27234
  openwop host-events list [--json]
27054
27235
  openwop host-events bind --event <host.\u2026> --workflow <workflowId> [--json]
@@ -27091,7 +27272,7 @@ async function runHostEvents(ctx, argv) {
27091
27272
  }
27092
27273
  switch (sub) {
27093
27274
  case "list": {
27094
- const res = await requestJson(ctx, BASE33);
27275
+ const res = await requestJson(ctx, BASE34);
27095
27276
  if (ctx.json) {
27096
27277
  writeJson(ctx.io.stdout, res.body);
27097
27278
  return 0;
@@ -27112,7 +27293,7 @@ async function runHostEvents(ctx, argv) {
27112
27293
  case "bind": {
27113
27294
  if (!options.event || !options.workflow) throw new CliError("bind needs --event <host.\u2026> and --workflow <workflowId>.", 2);
27114
27295
  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 } });
27296
+ const res = await requestJson(ctx, BASE34, { method: "POST", body: { eventType: options.event, workflowId: options.workflow } });
27116
27297
  if (ctx.json) {
27117
27298
  writeJson(ctx.io.stdout, res.body);
27118
27299
  return 0;
@@ -27123,7 +27304,7 @@ async function runHostEvents(ctx, argv) {
27123
27304
  case "enable":
27124
27305
  case "disable": {
27125
27306
  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" } });
27307
+ const res = await requestJson(ctx, `${BASE34}/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body: { enabled: sub === "enable" } });
27127
27308
  if (ctx.json) {
27128
27309
  writeJson(ctx.io.stdout, res.body);
27129
27310
  return 0;
@@ -27137,7 +27318,7 @@ async function runHostEvents(ctx, argv) {
27137
27318
  writeLine(ctx.io.stderr, `Refusing to delete binding ${positionals[0]} without --yes.`);
27138
27319
  return 2;
27139
27320
  }
27140
- await requestJson(ctx, `${BASE33}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
27321
+ await requestJson(ctx, `${BASE34}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
27141
27322
  writeLine(ctx.io.stdout, `Deleted binding ${positionals[0]}.`);
27142
27323
  return 0;
27143
27324
  }
@@ -27336,7 +27517,7 @@ Run \`openwop openapi --help\` for usage.`);
27336
27517
  }
27337
27518
 
27338
27519
  // src/cli/assistant.ts
27339
- var BASE34 = "/v1/host/openwop-app/assistant";
27520
+ var BASE35 = "/v1/host/openwop-app/assistant";
27340
27521
  var ASSISTANT_HELP = `Usage:
27341
27522
  openwop assistant workspace [--json]
27342
27523
  openwop assistant briefing [--json]
@@ -27399,7 +27580,7 @@ async function runAssistant(ctx, argv) {
27399
27580
  return await runWorkspace2(ctx, rest);
27400
27581
  case "briefing":
27401
27582
  case "brief":
27402
- return await simpleRead(ctx, rest, `${BASE34}/briefing`, (b) => writeLine(ctx.io.stdout, JSON.stringify(b?.brief ?? b, null, 2)));
27583
+ return await simpleRead(ctx, rest, `${BASE35}/briefing`, (b) => writeLine(ctx.io.stdout, JSON.stringify(b?.brief ?? b, null, 2)));
27403
27584
  case "projects":
27404
27585
  case "project":
27405
27586
  return await runProjects2(ctx, rest);
@@ -27412,7 +27593,7 @@ async function runAssistant(ctx, argv) {
27412
27593
  case "meeting":
27413
27594
  return await runMeetings(ctx, rest);
27414
27595
  case "stakeholders":
27415
- return await simpleRead(ctx, rest, `${BASE34}/stakeholders`, (b) => table2(ctx, b?.stakeholders, (s) => ({
27596
+ return await simpleRead(ctx, rest, `${BASE35}/stakeholders`, (b) => table2(ctx, b?.stakeholders, (s) => ({
27416
27597
  stakeholderId: s.stakeholderId,
27417
27598
  name: s.person?.name ?? s.person?.email ?? "",
27418
27599
  importance: String(s.importance ?? ""),
@@ -27425,7 +27606,7 @@ async function runAssistant(ctx, argv) {
27425
27606
  case "loop":
27426
27607
  return await runLoops(ctx, rest);
27427
27608
  case "health":
27428
- return await simpleRead(ctx, rest, `${BASE34}/health`, (b) => writeLine(ctx.io.stdout, JSON.stringify(b?.health ?? b, null, 2)));
27609
+ return await simpleRead(ctx, rest, `${BASE35}/health`, (b) => writeLine(ctx.io.stdout, JSON.stringify(b?.health ?? b, null, 2)));
27429
27610
  default:
27430
27611
  throw new CliError(`Unknown assistant command: ${sub}
27431
27612
  Run \`openwop assistant --help\` for usage.`, 2);
@@ -27455,7 +27636,7 @@ async function runWorkspace2(ctx, argv) {
27455
27636
  write(ctx.io.stdout, ASSISTANT_HELP);
27456
27637
  return 0;
27457
27638
  }
27458
- const res = await requestJson(ctx, `${BASE34}/workspace-conversation`, { method: "POST", body: {} });
27639
+ const res = await requestJson(ctx, `${BASE35}/workspace-conversation`, { method: "POST", body: {} });
27459
27640
  return emit(ctx, res.body, () => {
27460
27641
  writeLine(ctx.io.stdout, `${res.status === 201 ? "Opened" : "Resumed"} workspace conversation ${res.body?.sessionId}.`);
27461
27642
  writeLine(ctx.io.stdout, `Talk to the assistant: openwop chat messages send ${res.body?.sessionId} --content "\u2026"`);
@@ -27481,7 +27662,7 @@ async function runProjects2(ctx, argv) {
27481
27662
  const id = positionals[0];
27482
27663
  const needId = () => {
27483
27664
  if (!id) throw new CliError(`assistant projects ${sub} requires <projectId>.`, 2);
27484
- return `${BASE34}/projects/${enc(id)}`;
27665
+ return `${BASE35}/projects/${enc(id)}`;
27485
27666
  };
27486
27667
  const fields = () => {
27487
27668
  const body = {};
@@ -27494,7 +27675,7 @@ async function runProjects2(ctx, argv) {
27494
27675
  };
27495
27676
  switch (sub) {
27496
27677
  case "list": {
27497
- const res = await requestJson(ctx, `${BASE34}/projects`);
27678
+ const res = await requestJson(ctx, `${BASE35}/projects`);
27498
27679
  return emit(ctx, res.body, () => table2(ctx, res.body?.projects, projectRow, "No assistant projects. Create one with `openwop assistant projects create --name <n>`."));
27499
27680
  }
27500
27681
  case "get": {
@@ -27515,7 +27696,7 @@ async function runProjects2(ctx, argv) {
27515
27696
  }
27516
27697
  case "create": {
27517
27698
  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() });
27699
+ const res = await requestJson(ctx, `${BASE35}/projects`, { method: "POST", body: fields() });
27519
27700
  return emit(ctx, res.body, () => writeLine(ctx.io.stdout, `Created project ${res.body?.projectId} (${res.body?.name ?? options.name}).`));
27520
27701
  }
27521
27702
  case "update": {
@@ -27562,14 +27743,14 @@ async function runCommitments(ctx, argv) {
27562
27743
  const id = positionals[0];
27563
27744
  const needId = () => {
27564
27745
  if (!id) throw new CliError(`assistant commitments ${sub} requires <commitmentId>.`, 2);
27565
- return `${BASE34}/commitments/${enc(id)}`;
27746
+ return `${BASE35}/commitments/${enc(id)}`;
27566
27747
  };
27567
27748
  switch (sub) {
27568
27749
  case "list": {
27569
27750
  const q = new URLSearchParams();
27570
27751
  if (options.status) q.set("status", options.status);
27571
27752
  if (options.project) q.set("projectId", options.project);
27572
- const res = await requestJson(ctx, `${BASE34}/commitments${q.size ? `?${q}` : ""}`);
27753
+ const res = await requestJson(ctx, `${BASE35}/commitments${q.size ? `?${q}` : ""}`);
27573
27754
  return emit(ctx, res.body, () => table2(ctx, res.body?.commitments, commitmentRow, "No commitments."));
27574
27755
  }
27575
27756
  case "get": {
@@ -27619,7 +27800,7 @@ async function runDecisions(ctx, argv) {
27619
27800
  write(ctx.io.stdout, ASSISTANT_HELP);
27620
27801
  return 0;
27621
27802
  }
27622
- const res = await requestJson(ctx, `${BASE34}/decisions${options.project ? `?projectId=${enc(options.project)}` : ""}`);
27803
+ const res = await requestJson(ctx, `${BASE35}/decisions${options.project ? `?projectId=${enc(options.project)}` : ""}`);
27623
27804
  return emit(ctx, res.body, () => table2(ctx, res.body?.decisions, (d) => ({
27624
27805
  decisionId: d.decisionId,
27625
27806
  decidedAt: d.decidedAt ?? "",
@@ -27636,7 +27817,7 @@ async function runMeetings(ctx, argv) {
27636
27817
  return 0;
27637
27818
  }
27638
27819
  if (sub === "list") {
27639
- const res = await requestJson(ctx, `${BASE34}/meetings`);
27820
+ const res = await requestJson(ctx, `${BASE35}/meetings`);
27640
27821
  return emit(ctx, res.body, () => table2(ctx, res.body?.meetings, (m) => ({
27641
27822
  meetingId: m.meetingId,
27642
27823
  startAt: m.startAt ?? "",
@@ -27646,7 +27827,7 @@ async function runMeetings(ctx, argv) {
27646
27827
  }
27647
27828
  if (sub === "get") {
27648
27829
  if (!positionals[0]) throw new CliError("assistant meetings get requires <meetingId>.", 2);
27649
- const res = await requestJson(ctx, `${BASE34}/meetings/${enc(positionals[0])}`);
27830
+ const res = await requestJson(ctx, `${BASE35}/meetings/${enc(positionals[0])}`);
27650
27831
  const m = res.body ?? {};
27651
27832
  return emit(ctx, res.body, () => writeFields(ctx, [
27652
27833
  ["meetingId", m.meetingId],
@@ -27675,11 +27856,11 @@ async function runPending(ctx, argv) {
27675
27856
  const id = positionals[0];
27676
27857
  const needId = () => {
27677
27858
  if (!id) throw new CliError(`assistant pending ${sub} requires <actionId>.`, 2);
27678
- return `${BASE34}/pending-actions/${enc(id)}`;
27859
+ return `${BASE35}/pending-actions/${enc(id)}`;
27679
27860
  };
27680
27861
  switch (sub) {
27681
27862
  case "list": {
27682
- const res = await requestJson(ctx, `${BASE34}/pending-actions${options.status ? `?status=${enc(options.status)}` : ""}`);
27863
+ const res = await requestJson(ctx, `${BASE35}/pending-actions${options.status ? `?status=${enc(options.status)}` : ""}`);
27683
27864
  return emit(ctx, res.body, () => table2(ctx, res.body?.pendingActions, (a) => ({
27684
27865
  actionId: a.actionId,
27685
27866
  kind: a.kind ?? "",
@@ -27723,7 +27904,7 @@ async function runLoops(ctx, argv) {
27723
27904
  return 0;
27724
27905
  }
27725
27906
  if (sub === "list") {
27726
- const res = await requestJson(ctx, `${BASE34}/loops`);
27907
+ const res = await requestJson(ctx, `${BASE35}/loops`);
27727
27908
  return emit(ctx, res.body, () => table2(ctx, res.body?.loops, (l) => ({
27728
27909
  loopId: l.loopId,
27729
27910
  label: l.label ?? "",
@@ -27738,7 +27919,7 @@ async function runLoops(ctx, argv) {
27738
27919
  if (!loopId) throw new CliError(`assistant loops ${sub} requires <loopId>.`, 2);
27739
27920
  const body = {};
27740
27921
  if (sub === "enable" && options.cron) body.cronExpr = options.cron;
27741
- const res = await requestJson(ctx, `${BASE34}/loops/${enc(loopId)}/${sub}`, { method: "POST", body });
27922
+ const res = await requestJson(ctx, `${BASE35}/loops/${enc(loopId)}/${sub}`, { method: "POST", body });
27742
27923
  return emit(ctx, res.body, () => writeLine(ctx.io.stdout, `Loop ${loopId}: ${res.body?.enabled ? "enabled" : "disabled"} (job ${res.body?.jobId ?? ""}).`));
27743
27924
  }
27744
27925
  throw new CliError(`Unknown assistant loops command: ${sub}
@@ -27746,7 +27927,7 @@ Run \`openwop assistant --help\` for usage.`, 2);
27746
27927
  }
27747
27928
 
27748
27929
  // src/cli/channels.ts
27749
- var BASE35 = "/v1/host/openwop-app/channels";
27930
+ var BASE36 = "/v1/host/openwop-app/channels";
27750
27931
  var CHANNELS_HELP = `Usage:
27751
27932
  openwop channels list [--json]
27752
27933
  openwop channels create --name <n> [--description d] [--private] [--member <userId>]... [--agent <agentId>]... [--json]
@@ -27845,7 +28026,7 @@ function oneId(argv, usage6, spec = {}) {
27845
28026
  const { options, positionals } = parseOptions(argv, { ...spec, bool: ["--help", ...spec.bool ?? []] });
27846
28027
  if (options.help) return { help: true, options, positionals };
27847
28028
  if (!positionals[0]) throw new CliError(`Usage: openwop channels ${usage6}`, 2);
27848
- return { help: false, options, positionals, path: `${BASE35}/${enc(positionals[0])}` };
28029
+ return { help: false, options, positionals, path: `${BASE36}/${enc(positionals[0])}` };
27849
28030
  }
27850
28031
  function channelSummary(res) {
27851
28032
  const c = res?.channel ?? {};
@@ -27858,7 +28039,7 @@ async function runList4(ctx, argv) {
27858
28039
  write(ctx.io.stdout, CHANNELS_HELP);
27859
28040
  return 0;
27860
28041
  }
27861
- const res = await requestJson(ctx, BASE35);
28042
+ const res = await requestJson(ctx, BASE36);
27862
28043
  return emit(ctx, res.body, () => {
27863
28044
  const rows = Array.isArray(res.body?.channels) ? res.body.channels : [];
27864
28045
  if (!rows.length) {
@@ -27891,7 +28072,7 @@ async function runCreate(ctx, argv) {
27891
28072
  if (options.description) body.description = options.description;
27892
28073
  if (Array.isArray(options.member) && options.member.length) body.memberUserIds = options.member;
27893
28074
  if (Array.isArray(options.agent) && options.agent.length) body.agentIds = options.agent;
27894
- const res = await requestJson(ctx, BASE35, { method: "POST", body });
28075
+ const res = await requestJson(ctx, BASE36, { method: "POST", body });
27895
28076
  return emit(ctx, res.body, () => writeLine(ctx.io.stdout, `Created channel ${channelSummary(res.body)}.`));
27896
28077
  }
27897
28078
  async function runGet5(ctx, argv) {
@@ -28861,7 +29042,7 @@ Run \`openwop agent-author --help\` for usage.`, 2);
28861
29042
  }
28862
29043
 
28863
29044
  // src/cli/workflowAuthor.ts
28864
- var BASE36 = "/v1/host/openwop-app/workflow-author";
29045
+ var BASE37 = "/v1/host/openwop-app/workflow-author";
28865
29046
  var WORKFLOW_AUTHOR_HELP = `Usage:
28866
29047
  openwop workflow-author catalog [--json]
28867
29048
  openwop workflow-author draft --intent <text> [--provider p] [--model m] [--max-attempts n]
@@ -28897,7 +29078,7 @@ async function runWorkflowAuthor(ctx, argv) {
28897
29078
  return 0;
28898
29079
  }
28899
29080
  if (sub === "catalog") {
28900
- const res = await requestJson(ctx, `${BASE36}/catalog`);
29081
+ const res = await requestJson(ctx, `${BASE37}/catalog`);
28901
29082
  return emit(ctx, res.body, () => {
28902
29083
  const nodes = Array.isArray(res.body?.nodes) ? res.body.nodes : [];
28903
29084
  if (!nodes.length) writeLine(ctx.io.stdout, "No authorable nodes in this workspace.");
@@ -28913,7 +29094,7 @@ async function runWorkflowAuthor(ctx, argv) {
28913
29094
  if (options.model) body.model = options.model;
28914
29095
  const maxAttempts = intFlag(options.maxAttempts, "--max-attempts");
28915
29096
  if (maxAttempts) body.maxAttempts = maxAttempts;
28916
- const res = await requestJson(ctx, `${BASE36}/draft`, { method: "POST", body, headers: idempotencyHeaders(options.idempotencyKey) });
29097
+ const res = await requestJson(ctx, `${BASE37}/draft`, { method: "POST", body, headers: idempotencyHeaders(options.idempotencyKey) });
28917
29098
  const runId = res.body?.runId;
28918
29099
  if (!options.follow) {
28919
29100
  return emit(ctx, res.body, () => {
@@ -28940,7 +29121,7 @@ Run \`openwop workflow-author --help\` for usage.`, 2);
28940
29121
  }
28941
29122
 
28942
29123
  // src/cli/workflowProposals.ts
28943
- var BASE37 = "/v1/host/openwop-app/workflow-proposals/admin/policies";
29124
+ var BASE38 = "/v1/host/openwop-app/workflow-proposals/admin/policies";
28944
29125
  var WORKFLOW_PROPOSALS_HELP = `Usage:
28945
29126
  openwop workflow-proposals policies --tenant <tenantId> [--json]
28946
29127
  openwop workflow-proposals enable --tenant <tenantId> --agent-profile <agentProfileId> [--json]
@@ -28971,7 +29152,7 @@ async function runWorkflowProposals(ctx, argv) {
28971
29152
  }
28972
29153
  if (!options.tenant) throw new CliError("workflow-proposals requires --tenant <tenantId>.", 2);
28973
29154
  if (sub === "policies" || sub === "list") {
28974
- const res = await requestJson(ctx, `${BASE37}?tenantId=${enc(options.tenant)}`);
29155
+ const res = await requestJson(ctx, `${BASE38}?tenantId=${enc(options.tenant)}`);
28975
29156
  return emit(ctx, res.body, () => {
28976
29157
  const items = Array.isArray(res.body?.items) ? res.body.items : [];
28977
29158
  if (!items.length) {
@@ -28983,7 +29164,7 @@ async function runWorkflowProposals(ctx, argv) {
28983
29164
  }
28984
29165
  if (sub === "enable" || sub === "disable") {
28985
29166
  if (!options.agentProfile) throw new CliError(`workflow-proposals ${sub} requires --agent-profile <agentProfileId>.`, 2);
28986
- const path = `${BASE37}/${enc(options.tenant)}/${enc(options.agentProfile)}`;
29167
+ const path = `${BASE38}/${enc(options.tenant)}/${enc(options.agentProfile)}`;
28987
29168
  const res = await requestJson(ctx, path, { method: sub === "enable" ? "PUT" : "DELETE" });
28988
29169
  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
29170
  }
@@ -29929,6 +30110,10 @@ async function runCli(argv, options = {}) {
29929
30110
  return await runConformance(ctx, commandArgs);
29930
30111
  case "onboard":
29931
30112
  return await runOnboard(ctx, commandArgs);
30113
+ case "login":
30114
+ return await runLogin(ctx, commandArgs);
30115
+ case "logout":
30116
+ return await runLogout(ctx, commandArgs);
29932
30117
  case "providers":
29933
30118
  case "provider":
29934
30119
  return await runProviders(ctx, commandArgs);
@@ -30257,6 +30442,8 @@ var HELP_MAP = {
30257
30442
  packs: PACKS_HELP,
30258
30443
  pack: PACKS_HELP,
30259
30444
  onboard: ONBOARD_HELP,
30445
+ login: LOGIN_HELP,
30446
+ logout: LOGIN_HELP,
30260
30447
  providers: PROVIDERS_HELP,
30261
30448
  provider: PROVIDERS_HELP,
30262
30449
  agents: AGENTS_HELP,
@@ -30455,6 +30642,7 @@ Exit codes:
30455
30642
 
30456
30643
  Commands:
30457
30644
  onboard Guided first-run setup (host + provider + model + BYOK key)
30645
+ login | logout Sign this terminal in to an openwop-app host by a code (no key to copy)
30458
30646
  providers list List stored BYOK credential refs (never values)
30459
30647
  providers add Store a credential ref against the configured host
30460
30648
  providers remove Remove a credential ref