@openwop/cli 0.3.0 → 0.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/dist/cli.js +3595 -163
- package/dist/openwop.js +3594 -163
- package/package.json +1 -1
package/dist/openwop.js
CHANGED
|
@@ -151,8 +151,8 @@ function signalAvailable(env) {
|
|
|
151
151
|
if (probe.status === 0) return { channel: "signal", available: true, detail: (probe.stdout || "").trim() || "signal-cli present" };
|
|
152
152
|
return { channel: "signal", available: false, detail: "signal-cli not found on PATH \u2014 install from https://github.com/AsamK/signal-cli (or set OPENWOP_SIGNAL_DAEMON_URL)" };
|
|
153
153
|
}
|
|
154
|
-
function joinUrl(
|
|
155
|
-
return new URL(path.replace(/^\//, ""),
|
|
154
|
+
function joinUrl(base8, path) {
|
|
155
|
+
return new URL(path.replace(/^\//, ""), base8.endsWith("/") ? base8 : `${base8}/`);
|
|
156
156
|
}
|
|
157
157
|
async function signalDaemonRpc(baseUrl, method, params) {
|
|
158
158
|
const res = await fetch(joinUrl(baseUrl, "api/v1/rpc"), {
|
|
@@ -523,7 +523,7 @@ function errText(err) {
|
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
// src/constants.ts
|
|
526
|
-
var VERSION = "0.
|
|
526
|
+
var VERSION = "0.18.2";
|
|
527
527
|
var DEFAULT_BASE_URL = "http://localhost:8080";
|
|
528
528
|
var DEFAULT_REGISTRY_URL = "https://packs.openwop.dev";
|
|
529
529
|
var DEFAULT_API_KEY = "sample-token";
|
|
@@ -607,6 +607,7 @@ function extractGlobalOptions(argv, env = process.env) {
|
|
|
607
607
|
const globals = {
|
|
608
608
|
baseUrl: void 0,
|
|
609
609
|
apiKey: void 0,
|
|
610
|
+
profile: void 0,
|
|
610
611
|
json: false,
|
|
611
612
|
quiet: false,
|
|
612
613
|
verbose: false,
|
|
@@ -626,6 +627,10 @@ function extractGlobalOptions(argv, env = process.env) {
|
|
|
626
627
|
globals.apiKey = value ?? takeValue(argv, ++i, "--api-key");
|
|
627
628
|
continue;
|
|
628
629
|
}
|
|
630
|
+
if (flag === "--profile") {
|
|
631
|
+
globals.profile = value ?? takeValue(argv, ++i, "--profile");
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
629
634
|
if (arg === "--json") {
|
|
630
635
|
globals.json = true;
|
|
631
636
|
continue;
|
|
@@ -729,11 +734,11 @@ function saveConfig(path, config) {
|
|
|
729
734
|
}
|
|
730
735
|
}
|
|
731
736
|
function mergeConfig(existing, next) {
|
|
732
|
-
const
|
|
737
|
+
const base8 = existing ?? {};
|
|
733
738
|
return {
|
|
734
|
-
...
|
|
739
|
+
...base8,
|
|
735
740
|
...next,
|
|
736
|
-
host: { ...
|
|
741
|
+
host: { ...base8.host ?? {}, ...next.host ?? {} }
|
|
737
742
|
};
|
|
738
743
|
}
|
|
739
744
|
function getByPath(obj, path) {
|
|
@@ -1236,7 +1241,7 @@ async function runNotifications(ctx, argv) {
|
|
|
1236
1241
|
write(ctx.io.stdout, NOTIFICATIONS_HELP);
|
|
1237
1242
|
return 0;
|
|
1238
1243
|
}
|
|
1239
|
-
const
|
|
1244
|
+
const base8 = "/v1/host/sample/notifications";
|
|
1240
1245
|
const rest = argv.slice(1);
|
|
1241
1246
|
switch (sub) {
|
|
1242
1247
|
case "list": {
|
|
@@ -1245,7 +1250,7 @@ async function runNotifications(ctx, argv) {
|
|
|
1245
1250
|
if (options.status) q.set("status", options.status);
|
|
1246
1251
|
if (options.archived) q.set("includeArchived", "true");
|
|
1247
1252
|
if (options.limit) q.set("limit", options.limit);
|
|
1248
|
-
const res = await requestJson(ctx, `${
|
|
1253
|
+
const res = await requestJson(ctx, `${base8}${q.toString() ? `?${q}` : ""}`);
|
|
1249
1254
|
if (ctx.json) {
|
|
1250
1255
|
writeJson(ctx.io.stdout, res.body);
|
|
1251
1256
|
return 0;
|
|
@@ -1269,7 +1274,7 @@ async function runNotifications(ctx, argv) {
|
|
|
1269
1274
|
`);
|
|
1270
1275
|
return 2;
|
|
1271
1276
|
}
|
|
1272
|
-
const res = await requestJson(ctx, `${
|
|
1277
|
+
const res = await requestJson(ctx, `${base8}/${encodeURIComponent(rest[0])}/${sub}`, { method: "POST", body: {} });
|
|
1273
1278
|
if (ctx.json) {
|
|
1274
1279
|
writeJson(ctx.io.stdout, res.body);
|
|
1275
1280
|
return 0;
|
|
@@ -1278,7 +1283,7 @@ async function runNotifications(ctx, argv) {
|
|
|
1278
1283
|
return 0;
|
|
1279
1284
|
}
|
|
1280
1285
|
case "mark-all-read": {
|
|
1281
|
-
const res = await requestJson(ctx, `${
|
|
1286
|
+
const res = await requestJson(ctx, `${base8}:mark-all-read`, { method: "POST", body: {} });
|
|
1282
1287
|
if (ctx.json) {
|
|
1283
1288
|
writeJson(ctx.io.stdout, res.body);
|
|
1284
1289
|
return 0;
|
|
@@ -1292,7 +1297,7 @@ async function runNotifications(ctx, argv) {
|
|
|
1292
1297
|
write(ctx.io.stdout, "Usage: openwop notifications delete <id> [--json]\n");
|
|
1293
1298
|
return 2;
|
|
1294
1299
|
}
|
|
1295
|
-
const res = await requestJson(ctx, `${
|
|
1300
|
+
const res = await requestJson(ctx, `${base8}/${encodeURIComponent(rest[0])}`, { method: "DELETE" });
|
|
1296
1301
|
if (ctx.json) {
|
|
1297
1302
|
writeJson(ctx.io.stdout, res.body);
|
|
1298
1303
|
return 0;
|
|
@@ -1377,9 +1382,14 @@ var PROMPTS_HELP = `Usage:
|
|
|
1377
1382
|
openwop prompts list [--kind k] [--tag t] [--limit n] [--json]
|
|
1378
1383
|
openwop prompts get <templateId> [--json]
|
|
1379
1384
|
openwop prompts render <ref> [--variables-json '{...}'] [--json]
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1385
|
+
openwop prompts create --template-id <id> --version <v> --kind <k> --text <t> [--json]
|
|
1386
|
+
openwop prompts update <templateId> [--version v] [--kind k] [--text t] [--json]
|
|
1387
|
+
openwop prompts delete <templateId> [--yes]
|
|
1388
|
+
|
|
1389
|
+
Browse, render, and manage the host's prompt library (RFC 0029, /v1/prompts).
|
|
1390
|
+
\`render\` resolves a PromptRef (templateId[@version]) against the supplied
|
|
1391
|
+
variables. \`create\` posts a new PromptTemplate; \`update\` PUTs the fields you
|
|
1392
|
+
pass to an existing template; \`delete\` removes one.
|
|
1383
1393
|
`;
|
|
1384
1394
|
async function runPrompts(ctx, argv) {
|
|
1385
1395
|
const sub = argv[0] ?? "list";
|
|
@@ -1438,6 +1448,44 @@ async function runPrompts(ctx, argv) {
|
|
|
1438
1448
|
writeJson(ctx.io.stdout, res.body);
|
|
1439
1449
|
return 0;
|
|
1440
1450
|
}
|
|
1451
|
+
case "create": {
|
|
1452
|
+
const { options } = parseOptions(rest, { value: ["--template-id", "--version", "--kind", "--text"] });
|
|
1453
|
+
if (!options.templateId || !options.version || !options.kind || !options.text) {
|
|
1454
|
+
write(ctx.io.stderr, "Usage: openwop prompts create --template-id <id> --version <v> --kind <k> --text <t> [--json]\n");
|
|
1455
|
+
return 2;
|
|
1456
|
+
}
|
|
1457
|
+
const body = { templateId: String(options.templateId), version: String(options.version), kind: String(options.kind), text: String(options.text) };
|
|
1458
|
+
const res = await requestJson(ctx, "/v1/prompts", { method: "POST", body });
|
|
1459
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
1460
|
+
else writeLine(ctx.io.stdout, `Created prompt ${body.templateId}@${body.version}`);
|
|
1461
|
+
return 0;
|
|
1462
|
+
}
|
|
1463
|
+
case "update": {
|
|
1464
|
+
const { options, positionals } = parseOptions(rest, { value: ["--version", "--kind", "--text"] });
|
|
1465
|
+
if (positionals.length !== 1) {
|
|
1466
|
+
write(ctx.io.stdout, "Usage: openwop prompts update <templateId> [--version v] [--kind k] [--text t] [--json]\n");
|
|
1467
|
+
return 2;
|
|
1468
|
+
}
|
|
1469
|
+
const body = {};
|
|
1470
|
+
if (options.version) body.version = String(options.version);
|
|
1471
|
+
if (options.kind) body.kind = String(options.kind);
|
|
1472
|
+
if (options.text) body.text = String(options.text);
|
|
1473
|
+
const res = await requestJson(ctx, `/v1/prompts/${encodeURIComponent(positionals[0])}`, { method: "PUT", body });
|
|
1474
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
1475
|
+
else writeLine(ctx.io.stdout, `Updated prompt ${positionals[0]}`);
|
|
1476
|
+
return 0;
|
|
1477
|
+
}
|
|
1478
|
+
case "delete": {
|
|
1479
|
+
const { options, positionals } = parseOptions(rest, { bool: ["--yes"] });
|
|
1480
|
+
if (positionals.length !== 1) {
|
|
1481
|
+
write(ctx.io.stdout, "Usage: openwop prompts delete <templateId> [--yes]\n");
|
|
1482
|
+
return 2;
|
|
1483
|
+
}
|
|
1484
|
+
if (!options.yes) throw new CliError(`Refusing to delete prompt ${positionals[0]} without --yes.`, 2);
|
|
1485
|
+
await requestJson(ctx, `/v1/prompts/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
1486
|
+
writeLine(ctx.io.stdout, `Deleted prompt ${positionals[0]}`);
|
|
1487
|
+
return 0;
|
|
1488
|
+
}
|
|
1441
1489
|
default:
|
|
1442
1490
|
throw new CliError(`Unknown prompts command: ${sub}
|
|
1443
1491
|
Run \`openwop prompts --help\` for usage.`);
|
|
@@ -2074,6 +2122,10 @@ async function downloadAsset(ctx, assetUrl, outPath) {
|
|
|
2074
2122
|
}
|
|
2075
2123
|
|
|
2076
2124
|
// src/cli/shared.ts
|
|
2125
|
+
function requireOrg(org) {
|
|
2126
|
+
if (!org) throw new CliError("This command is org-scoped \u2014 pass --org <orgId>.", 2);
|
|
2127
|
+
return String(org);
|
|
2128
|
+
}
|
|
2077
2129
|
function buildInputs(options) {
|
|
2078
2130
|
const fromJson = options.inputsJson ? JSON.parse(options.inputsJson) : {};
|
|
2079
2131
|
if (fromJson === null || typeof fromJson !== "object" || Array.isArray(fromJson)) {
|
|
@@ -2195,10 +2247,15 @@ async function runConfig(ctx, argv) {
|
|
|
2195
2247
|
var CATALOG_HELP = `Usage:
|
|
2196
2248
|
openwop catalog nodes [--search text] [--limit n] [--json]
|
|
2197
2249
|
openwop catalog packs [--json]
|
|
2250
|
+
openwop catalog tools [<toolId>] [--json]
|
|
2251
|
+
|
|
2252
|
+
\`tools\` reads the portable tool catalog (RFC 0078 \xA7B) \u2014 GET /v1/tools, or
|
|
2253
|
+
GET /v1/tools/<toolId> for one descriptor. This is the tools an agent/workflow
|
|
2254
|
+
may call, distinct from the node catalog + installed packs.
|
|
2198
2255
|
`;
|
|
2199
2256
|
async function runCatalog(ctx, argv) {
|
|
2200
2257
|
const sub = argv[0] ?? "nodes";
|
|
2201
|
-
const args = argv.slice(sub === "nodes" || sub === "packs" ? 1 : 0);
|
|
2258
|
+
const args = argv.slice(sub === "nodes" || sub === "packs" || sub === "tools" ? 1 : 0);
|
|
2202
2259
|
if (sub === "--help" || sub === "-h") {
|
|
2203
2260
|
write(ctx.io.stdout, CATALOG_HELP);
|
|
2204
2261
|
return 0;
|
|
@@ -2208,10 +2265,35 @@ async function runCatalog(ctx, argv) {
|
|
|
2208
2265
|
return runCatalogNodes(ctx, args);
|
|
2209
2266
|
case "packs":
|
|
2210
2267
|
return runCatalogPacks(ctx, args);
|
|
2268
|
+
case "tools":
|
|
2269
|
+
return runCatalogTools(ctx, args);
|
|
2211
2270
|
default:
|
|
2212
2271
|
throw new CliError(`Unknown catalog command: ${sub}`);
|
|
2213
2272
|
}
|
|
2214
2273
|
}
|
|
2274
|
+
async function runCatalogTools(ctx, argv) {
|
|
2275
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"] });
|
|
2276
|
+
if (options.help) {
|
|
2277
|
+
write(ctx.io.stdout, CATALOG_HELP);
|
|
2278
|
+
return 0;
|
|
2279
|
+
}
|
|
2280
|
+
const path = positionals.length === 1 ? `/v1/tools/${encodeURIComponent(positionals[0])}` : "/v1/tools";
|
|
2281
|
+
const res = await requestJson(ctx, path);
|
|
2282
|
+
if (ctx.json || positionals.length === 1) {
|
|
2283
|
+
writeJson(ctx.io.stdout, res.body);
|
|
2284
|
+
return 0;
|
|
2285
|
+
}
|
|
2286
|
+
const tools = Array.isArray(res.body?.tools) ? res.body.tools : Array.isArray(res.body) ? res.body : [];
|
|
2287
|
+
if (tools.length === 0) {
|
|
2288
|
+
writeLine(ctx.io.stdout, "No tools advertised.");
|
|
2289
|
+
return 0;
|
|
2290
|
+
}
|
|
2291
|
+
writeLine(ctx.io.stdout, formatTable(
|
|
2292
|
+
tools.map((t) => ({ toolId: t.toolId ?? t.id ?? "", title: t.title ?? t.name ?? "", effect: t.dataEffect ?? t.effect ?? "" })),
|
|
2293
|
+
["toolId", "title", "effect"]
|
|
2294
|
+
));
|
|
2295
|
+
return 0;
|
|
2296
|
+
}
|
|
2215
2297
|
async function runCatalogNodes(ctx, argv) {
|
|
2216
2298
|
const { options } = parseOptions(argv, {
|
|
2217
2299
|
bool: ["--help"],
|
|
@@ -2266,10 +2348,17 @@ var WORKFLOWS_HELP = `Usage:
|
|
|
2266
2348
|
openwop workflows get <workflowId> [--json]
|
|
2267
2349
|
openwop workflows register <workflow.json> [--json]
|
|
2268
2350
|
openwop workflows delete <workflowId> [--json]
|
|
2351
|
+
openwop workflows chains [--json]
|
|
2352
|
+
openwop workflows from-chain <chainId> [--params-json '{...}'] [--json]
|
|
2353
|
+
openwop workflows chain-pack-install --name <pack> [--version <v>] [--json]
|
|
2354
|
+
|
|
2355
|
+
\`chains\` lists the host's workflow-chain templates (ADR 0163 / RFC 0013);
|
|
2356
|
+
\`from-chain\` expands one into a registered workflow you can run; \`chain-pack-install\`
|
|
2357
|
+
installs a signed workflow-chain pack.
|
|
2269
2358
|
`;
|
|
2270
2359
|
async function runWorkflows(ctx, argv) {
|
|
2271
2360
|
const sub = argv[0] ?? "list";
|
|
2272
|
-
const args = argv.slice(["list", "get", "register", "delete", "rm"].includes(sub) ? 1 : 0);
|
|
2361
|
+
const args = argv.slice(["list", "get", "register", "delete", "rm", "chains", "from-chain", "chain-pack-install"].includes(sub) ? 1 : 0);
|
|
2273
2362
|
if (sub === "--help" || sub === "-h") {
|
|
2274
2363
|
write(ctx.io.stdout, WORKFLOWS_HELP);
|
|
2275
2364
|
return 0;
|
|
@@ -2284,10 +2373,71 @@ async function runWorkflows(ctx, argv) {
|
|
|
2284
2373
|
case "delete":
|
|
2285
2374
|
case "rm":
|
|
2286
2375
|
return runWorkflowsDelete(ctx, args);
|
|
2376
|
+
case "chains":
|
|
2377
|
+
return runWorkflowsChains(ctx, args);
|
|
2378
|
+
case "from-chain":
|
|
2379
|
+
return runWorkflowsFromChain(ctx, args);
|
|
2380
|
+
case "chain-pack-install":
|
|
2381
|
+
return runWorkflowsChainPackInstall(ctx, args);
|
|
2287
2382
|
default:
|
|
2288
2383
|
throw new CliError(`Unknown workflows command: ${sub}`);
|
|
2289
2384
|
}
|
|
2290
2385
|
}
|
|
2386
|
+
async function runWorkflowsChains(ctx, argv) {
|
|
2387
|
+
const { options } = parseOptions(argv, { bool: ["--help"] });
|
|
2388
|
+
if (options.help) {
|
|
2389
|
+
write(ctx.io.stdout, WORKFLOWS_HELP);
|
|
2390
|
+
return 0;
|
|
2391
|
+
}
|
|
2392
|
+
const res = await requestJson(ctx, "/v1/host/sample/workflow-chains");
|
|
2393
|
+
if (ctx.json) {
|
|
2394
|
+
writeJson(ctx.io.stdout, res.body);
|
|
2395
|
+
return 0;
|
|
2396
|
+
}
|
|
2397
|
+
const chains = Array.isArray(res.body?.chains) ? res.body.chains : [];
|
|
2398
|
+
if (chains.length === 0) {
|
|
2399
|
+
writeLine(ctx.io.stdout, "No workflow chains.");
|
|
2400
|
+
return 0;
|
|
2401
|
+
}
|
|
2402
|
+
writeLine(ctx.io.stdout, formatTable(
|
|
2403
|
+
chains.map((c) => ({ chainId: c.chainId ?? "", name: c.name ?? "", steps: Array.isArray(c.steps) ? c.steps.length : c.stepCount ?? "" })),
|
|
2404
|
+
["chainId", "name", "steps"]
|
|
2405
|
+
));
|
|
2406
|
+
return 0;
|
|
2407
|
+
}
|
|
2408
|
+
async function runWorkflowsFromChain(ctx, argv) {
|
|
2409
|
+
const { options, positionals } = parseOptions(argv, { value: ["--chain", "--params-json"] });
|
|
2410
|
+
const chainId = options.chain ?? positionals[0];
|
|
2411
|
+
if (!chainId) {
|
|
2412
|
+
write(ctx.io.stderr, "Usage: openwop workflows from-chain <chainId> [--params-json '{...}'] [--json]\n");
|
|
2413
|
+
return 2;
|
|
2414
|
+
}
|
|
2415
|
+
let params = {};
|
|
2416
|
+
if (options.paramsJson) {
|
|
2417
|
+
try {
|
|
2418
|
+
params = JSON.parse(String(options.paramsJson));
|
|
2419
|
+
} catch {
|
|
2420
|
+
throw new CliError("--params-json must be valid JSON.", 2);
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
const res = await requestJson(ctx, "/v1/host/sample/workflows/from-chain", { method: "POST", body: { chainId, params } });
|
|
2424
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
2425
|
+
else writeLine(ctx.io.stdout, `Registered workflow ${res.body?.workflowId ?? ""} from chain ${chainId}.`);
|
|
2426
|
+
return 0;
|
|
2427
|
+
}
|
|
2428
|
+
async function runWorkflowsChainPackInstall(ctx, argv) {
|
|
2429
|
+
const { options } = parseOptions(argv, { value: ["--name", "--version"] });
|
|
2430
|
+
if (!options.name) {
|
|
2431
|
+
write(ctx.io.stderr, "Usage: openwop workflows chain-pack-install --name <pack> [--version <v>] [--json]\n");
|
|
2432
|
+
return 2;
|
|
2433
|
+
}
|
|
2434
|
+
const body = { name: String(options.name) };
|
|
2435
|
+
if (options.version) body.version = String(options.version);
|
|
2436
|
+
const res = await requestJson(ctx, "/v1/host/sample/workflow-chain-packs/install", { method: "POST", body });
|
|
2437
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
2438
|
+
else writeLine(ctx.io.stdout, `Installed workflow-chain pack ${options.name}.`);
|
|
2439
|
+
return 0;
|
|
2440
|
+
}
|
|
2291
2441
|
async function runWorkflowsList(ctx, argv) {
|
|
2292
2442
|
const { options } = parseOptions(argv, { bool: ["--help"] });
|
|
2293
2443
|
if (options.help) {
|
|
@@ -2900,6 +3050,10 @@ var KANBAN_HELP = `Usage:
|
|
|
2900
3050
|
openwop kanban card-move <cardId> --column <columnId> [--json]
|
|
2901
3051
|
openwop kanban card-update <cardId> [--title <text>] [--description <text>] [--column <columnId>] [--workflow <id>] [--json]
|
|
2902
3052
|
openwop kanban card-delete <cardId> [--yes]
|
|
3053
|
+
openwop kanban card-assign <cardId> --assignee <subject> [--role <r>] [--no-notify] [--json]
|
|
3054
|
+
openwop kanban card-claim <cardId> [--json]
|
|
3055
|
+
openwop kanban boards-personal [--json]
|
|
3056
|
+
openwop kanban assigned [--json]
|
|
2903
3057
|
openwop kanban watch <boardId>
|
|
2904
3058
|
|
|
2905
3059
|
Kanban boards (host-extension under /v1/host/sample/kanban). A board is a lane
|
|
@@ -2925,7 +3079,7 @@ async function runKanban(ctx, argv) {
|
|
|
2925
3079
|
write(ctx.io.stdout, KANBAN_HELP);
|
|
2926
3080
|
return 0;
|
|
2927
3081
|
}
|
|
2928
|
-
const known = ["boards", "board", "board-create", "board-delete", "card-add", "card-move", "card-update", "card-delete", "watch"];
|
|
3082
|
+
const known = ["boards", "board", "board-create", "board-delete", "card-add", "card-move", "card-update", "card-delete", "card-assign", "card-claim", "boards-personal", "assigned", "watch"];
|
|
2929
3083
|
const args = argv.slice(known.includes(sub) ? 1 : 0);
|
|
2930
3084
|
switch (sub) {
|
|
2931
3085
|
case "boards":
|
|
@@ -2944,6 +3098,14 @@ async function runKanban(ctx, argv) {
|
|
|
2944
3098
|
return await cardPatch(ctx, args, false);
|
|
2945
3099
|
case "card-delete":
|
|
2946
3100
|
return await cardDelete(ctx, args);
|
|
3101
|
+
case "card-assign":
|
|
3102
|
+
return await cardAssign(ctx, args);
|
|
3103
|
+
case "card-claim":
|
|
3104
|
+
return await cardClaim(ctx, args);
|
|
3105
|
+
case "boards-personal":
|
|
3106
|
+
return await boardsPersonal(ctx, args);
|
|
3107
|
+
case "assigned":
|
|
3108
|
+
return await assignedList(ctx, args);
|
|
2947
3109
|
case "watch":
|
|
2948
3110
|
return await boardWatch(ctx, args);
|
|
2949
3111
|
default:
|
|
@@ -3079,52 +3241,2913 @@ async function cardPatch(ctx, argv, moveOnly) {
|
|
|
3079
3241
|
if (options.description) body.description = options.description;
|
|
3080
3242
|
if (options.workflow) body.workflowId = options.workflow;
|
|
3081
3243
|
}
|
|
3082
|
-
if (Object.keys(body).length === 0) throw new CliError("Nothing to update \u2014 pass at least one field.", 2);
|
|
3083
|
-
const res = await requestJson(ctx, `/v1/host/sample/kanban/cards/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body });
|
|
3084
|
-
if (ctx.json) {
|
|
3085
|
-
writeJson(ctx.io.stdout, res.body);
|
|
3244
|
+
if (Object.keys(body).length === 0) throw new CliError("Nothing to update \u2014 pass at least one field.", 2);
|
|
3245
|
+
const res = await requestJson(ctx, `/v1/host/sample/kanban/cards/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body });
|
|
3246
|
+
if (ctx.json) {
|
|
3247
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3248
|
+
return 0;
|
|
3249
|
+
}
|
|
3250
|
+
writeLine(ctx.io.stdout, `Updated card ${positionals[0]}.`);
|
|
3251
|
+
return 0;
|
|
3252
|
+
}
|
|
3253
|
+
async function cardDelete(ctx, argv) {
|
|
3254
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--yes"] });
|
|
3255
|
+
if (options.help || positionals.length !== 1) {
|
|
3256
|
+
write(ctx.io.stdout, "Usage: openwop kanban card-delete <cardId> [--yes]\n");
|
|
3257
|
+
return options.help ? 0 : 2;
|
|
3258
|
+
}
|
|
3259
|
+
if (!options.yes) {
|
|
3260
|
+
writeLine(ctx.io.stderr, `Refusing to delete card ${positionals[0]} without --yes.`);
|
|
3261
|
+
return 2;
|
|
3262
|
+
}
|
|
3263
|
+
await requestJson(ctx, `/v1/host/sample/kanban/cards/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
3264
|
+
writeLine(ctx.io.stdout, `Deleted card ${positionals[0]}.`);
|
|
3265
|
+
return 0;
|
|
3266
|
+
}
|
|
3267
|
+
async function boardWatch(ctx, argv) {
|
|
3268
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"] });
|
|
3269
|
+
if (options.help || positionals.length !== 1) {
|
|
3270
|
+
write(ctx.io.stdout, "Usage: openwop kanban watch <boardId>\n");
|
|
3271
|
+
return options.help ? 0 : 2;
|
|
3272
|
+
}
|
|
3273
|
+
const url = new URL(`v1/host/sample/kanban/boards/${encodeURIComponent(positionals[0])}/events`, ctx.baseUrl.endsWith("/") ? ctx.baseUrl : `${ctx.baseUrl}/`);
|
|
3274
|
+
const headers = { accept: "text/event-stream" };
|
|
3275
|
+
if (ctx.apiKey) headers.authorization = `Bearer ${ctx.apiKey}`;
|
|
3276
|
+
const res = await ctx.fetchImpl(url, { headers });
|
|
3277
|
+
if (!res.ok || !res.body) {
|
|
3278
|
+
throw new CliError(`Could not open the board event stream (HTTP ${res.status}).`, 1);
|
|
3279
|
+
}
|
|
3280
|
+
writeLine(ctx.io.stdout, `Watching board ${positionals[0]} (Ctrl-C to stop)\u2026`);
|
|
3281
|
+
await consumeSse(res.body, (frame) => {
|
|
3282
|
+
if (ctx.json) {
|
|
3283
|
+
writeLine(ctx.io.stdout, JSON.stringify(frame));
|
|
3284
|
+
return;
|
|
3285
|
+
}
|
|
3286
|
+
const type = frame.event ?? "message";
|
|
3287
|
+
writeLine(ctx.io.stdout, `\xB7 ${type}${frame.data ? `: ${frame.data}` : ""}`);
|
|
3288
|
+
});
|
|
3289
|
+
return 0;
|
|
3290
|
+
}
|
|
3291
|
+
async function cardAssign(ctx, argv) {
|
|
3292
|
+
const { options, positionals } = parseOptions(argv, { value: ["--assignee", "--role"], bool: ["--no-notify", "--help"] });
|
|
3293
|
+
if (options.help || positionals.length !== 1 || !options.assignee) {
|
|
3294
|
+
write(ctx.io.stdout, "Usage: openwop kanban card-assign <cardId> --assignee <subject> [--role r] [--no-notify] [--json]\n");
|
|
3295
|
+
return options.help ? 0 : 2;
|
|
3296
|
+
}
|
|
3297
|
+
const body = { assigneeId: options.assignee };
|
|
3298
|
+
if (options.role) body.assigneeRole = options.role;
|
|
3299
|
+
if (options.noNotify) body.notifyAssignee = false;
|
|
3300
|
+
const res = await requestJson(ctx, `/v1/host/sample/kanban/cards/${encodeURIComponent(positionals[0])}/assign`, { method: "POST", body });
|
|
3301
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
3302
|
+
else writeLine(ctx.io.stdout, `Assigned card ${positionals[0]} to ${options.assignee}.`);
|
|
3303
|
+
return 0;
|
|
3304
|
+
}
|
|
3305
|
+
async function cardClaim(ctx, argv) {
|
|
3306
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"] });
|
|
3307
|
+
if (options.help || positionals.length !== 1) {
|
|
3308
|
+
write(ctx.io.stdout, "Usage: openwop kanban card-claim <cardId> [--json]\n");
|
|
3309
|
+
return options.help ? 0 : 2;
|
|
3310
|
+
}
|
|
3311
|
+
const res = await requestJson(ctx, `/v1/host/sample/kanban/cards/${encodeURIComponent(positionals[0])}/claim`, { method: "POST", body: {} });
|
|
3312
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
3313
|
+
else writeLine(ctx.io.stdout, `Claimed card ${positionals[0]}.`);
|
|
3314
|
+
return 0;
|
|
3315
|
+
}
|
|
3316
|
+
async function boardsPersonal(ctx, argv) {
|
|
3317
|
+
const { options } = parseOptions(argv, { bool: ["--help"] });
|
|
3318
|
+
if (options.help) {
|
|
3319
|
+
write(ctx.io.stdout, KANBAN_HELP);
|
|
3320
|
+
return 0;
|
|
3321
|
+
}
|
|
3322
|
+
const res = await requestJson(ctx, "/v1/host/sample/kanban/boards/personal");
|
|
3323
|
+
if (ctx.json) {
|
|
3324
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3325
|
+
return 0;
|
|
3326
|
+
}
|
|
3327
|
+
const boards = Array.isArray(res.body?.boards) ? res.body.boards : [];
|
|
3328
|
+
if (boards.length === 0) {
|
|
3329
|
+
writeLine(ctx.io.stdout, "No personal boards.");
|
|
3330
|
+
return 0;
|
|
3331
|
+
}
|
|
3332
|
+
writeLine(ctx.io.stdout, formatTable(
|
|
3333
|
+
boards.map((b) => ({ boardId: b.boardId ?? b.id ?? "", name: b.name ?? "", cards: b.cardCount ?? "" })),
|
|
3334
|
+
["boardId", "name", "cards"]
|
|
3335
|
+
));
|
|
3336
|
+
return 0;
|
|
3337
|
+
}
|
|
3338
|
+
async function assignedList(ctx, argv) {
|
|
3339
|
+
const { options } = parseOptions(argv, { bool: ["--help"] });
|
|
3340
|
+
if (options.help) {
|
|
3341
|
+
write(ctx.io.stdout, KANBAN_HELP);
|
|
3342
|
+
return 0;
|
|
3343
|
+
}
|
|
3344
|
+
const res = await requestJson(ctx, "/v1/host/sample/kanban/assigned");
|
|
3345
|
+
if (ctx.json) {
|
|
3346
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3347
|
+
return 0;
|
|
3348
|
+
}
|
|
3349
|
+
const cards = Array.isArray(res.body?.cards) ? res.body.cards : [];
|
|
3350
|
+
if (cards.length === 0) {
|
|
3351
|
+
writeLine(ctx.io.stdout, "No cards assigned to you.");
|
|
3352
|
+
return 0;
|
|
3353
|
+
}
|
|
3354
|
+
writeLine(ctx.io.stdout, formatTable(
|
|
3355
|
+
cards.map((c) => ({ cardId: c.id ?? c.cardId ?? "", title: c.title ?? "", board: c.boardId ?? "", column: c.columnId ?? "" })),
|
|
3356
|
+
["cardId", "title", "board", "column"]
|
|
3357
|
+
));
|
|
3358
|
+
return 0;
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3361
|
+
// src/cli/brand.ts
|
|
3362
|
+
var PUBLIC_PATH = "/v1/host/openwop-app/public-brand";
|
|
3363
|
+
var APP_PATH = "/v1/host/openwop-app/app-brand";
|
|
3364
|
+
var CONTRAST = ["standard", "medium", "high"];
|
|
3365
|
+
var RADIUS = ["sm", "md", "lg"];
|
|
3366
|
+
var MODE = ["system", "light", "dark"];
|
|
3367
|
+
var BRAND_HELP = `Usage:
|
|
3368
|
+
openwop brand public [--json]
|
|
3369
|
+
openwop brand get [--json]
|
|
3370
|
+
openwop brand set [--name <n>] [--product-name <n>] [--accent <color>] [--neutral <color>]
|
|
3371
|
+
[--contrast standard|medium|high] [--radius sm|md|lg] [--default-mode system|light|dark]
|
|
3372
|
+
[--logo <url>] [--favicon <url>] [--identity-json <json>] [--json]
|
|
3373
|
+
|
|
3374
|
+
White-label app brand for the OpenWOP demo host (ADR 0170 / 0171). ONE brand owns the
|
|
3375
|
+
app's own runtime identity \u2014 logo, colors, fonts, name, and a generative theme \u2014 applied
|
|
3376
|
+
to the running app with no rebuild. Drives the host-extension surface
|
|
3377
|
+
GET/PUT /v1/host/openwop-app/{public-brand,app-brand}:
|
|
3378
|
+
\u2022 public \u2014 the applied identity every visitor sees (anonymous; no auth needed).
|
|
3379
|
+
\u2022 get / set \u2014 read + edit the reserved app brand; requires SUPER-ADMIN
|
|
3380
|
+
(OPENWOP_SUPERADMIN_TENANTS; authenticate as that tenant).
|
|
3381
|
+
|
|
3382
|
+
'set' is read-modify-write: it fetches the current brand, applies your flags, and PUTs
|
|
3383
|
+
the merged result \u2014 so setting --accent won't wipe the logo. The accent/neutral/contrast/
|
|
3384
|
+
radius flags feed the ADR 0171 theme generator, which derives a full, accessible light +
|
|
3385
|
+
dark theme from the seed (the accent is kept exact; text shades are solved for WCAG-AA).
|
|
3386
|
+
--identity-json REPLACES the whole identity facet (advanced \u2014 mirrors the editor's JSON tier).
|
|
3387
|
+
|
|
3388
|
+
--name <n> The brand's internal name.
|
|
3389
|
+
--product-name <n> The app's product name (shown in the wordmark / document title).
|
|
3390
|
+
--accent <color> Brand accent seed \u2014 any CSS color (oklch / #hex / rgb). Generates the ramp.
|
|
3391
|
+
--neutral <color> Optional surface/background tint seed.
|
|
3392
|
+
--contrast <lvl> Contrast target: standard (AA) | medium | high.
|
|
3393
|
+
--radius <size> Corner radius: sm | md | lg.
|
|
3394
|
+
--default-mode <m> Default theme mode: system | light | dark.
|
|
3395
|
+
--logo <url> Logo / mark URL (https, root-relative, or data:image).
|
|
3396
|
+
--favicon <url> Favicon URL.
|
|
3397
|
+
--identity-json <j> Full identity facet as JSON (REPLACES every identity field).
|
|
3398
|
+
--json Machine-readable output.
|
|
3399
|
+
|
|
3400
|
+
Examples:
|
|
3401
|
+
openwop brand public --json
|
|
3402
|
+
openwop brand set --accent 'oklch(58% 0.13 250)' --product-name 'Acme Ops'
|
|
3403
|
+
openwop brand set --identity-json '{"productName":"Acme","theme":{"accentSeed":"#0a84ff"}}'
|
|
3404
|
+
`;
|
|
3405
|
+
async function runBrand(ctx, argv) {
|
|
3406
|
+
const sub = argv[0] ?? "public";
|
|
3407
|
+
if (sub === "--help" || sub === "-h") {
|
|
3408
|
+
write(ctx.io.stdout, BRAND_HELP);
|
|
3409
|
+
return 0;
|
|
3410
|
+
}
|
|
3411
|
+
const args = argv.slice(["public", "get", "set"].includes(sub) ? 1 : 0);
|
|
3412
|
+
switch (sub) {
|
|
3413
|
+
case "public":
|
|
3414
|
+
return await brandPublic(ctx, args);
|
|
3415
|
+
case "get":
|
|
3416
|
+
return await brandGet(ctx, args);
|
|
3417
|
+
case "set":
|
|
3418
|
+
return await brandSet(ctx, args);
|
|
3419
|
+
default:
|
|
3420
|
+
throw new CliError(`Unknown brand command: ${sub}
|
|
3421
|
+
Run \`openwop brand --help\` for usage.`);
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
async function appRequest(ctx, path, options, action) {
|
|
3425
|
+
try {
|
|
3426
|
+
return await requestJson(ctx, path, options);
|
|
3427
|
+
} catch (err) {
|
|
3428
|
+
if (err instanceof HttpError && (err.status === 401 || err.status === 403)) {
|
|
3429
|
+
throw new CliError(
|
|
3430
|
+
`${action} requires super-admin on this host. Set OPENWOP_SUPERADMIN_TENANTS on the server and authenticate the CLI as that tenant (openwop onboard / --api-key).`,
|
|
3431
|
+
4
|
|
3432
|
+
);
|
|
3433
|
+
}
|
|
3434
|
+
throw err;
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
function showIdentity(ctx, identity) {
|
|
3438
|
+
const id = identity ?? {};
|
|
3439
|
+
const theme = id.theme ?? {};
|
|
3440
|
+
const logo = id.logo ?? {};
|
|
3441
|
+
writeLine(ctx.io.stdout, `productName: ${id.productName ?? "(default)"}`);
|
|
3442
|
+
writeLine(ctx.io.stdout, `accent: ${theme.accentSeed ?? id.colors?.accent ?? "(stock)"}`);
|
|
3443
|
+
if (theme.neutralSeed) writeLine(ctx.io.stdout, `neutral: ${theme.neutralSeed}`);
|
|
3444
|
+
writeLine(ctx.io.stdout, `contrast: ${theme.contrastLevel ?? "standard"}`);
|
|
3445
|
+
if (theme.radius) writeLine(ctx.io.stdout, `radius: ${theme.radius}`);
|
|
3446
|
+
writeLine(ctx.io.stdout, `defaultMode: ${theme.defaultMode ?? "system"}`);
|
|
3447
|
+
writeLine(ctx.io.stdout, `logo: ${logo.markSrc ?? "(none)"}`);
|
|
3448
|
+
writeLine(ctx.io.stdout, `favicon: ${logo.faviconSrc ?? "(none)"}`);
|
|
3449
|
+
if (theme.override && (theme.override.light || theme.override.dark)) {
|
|
3450
|
+
writeLine(ctx.io.stdout, `override: (advanced token overrides set)`);
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
async function brandPublic(ctx, argv) {
|
|
3454
|
+
const { options } = parseOptions(argv, { bool: ["--help"] });
|
|
3455
|
+
if (options.help) {
|
|
3456
|
+
write(ctx.io.stdout, "Usage: openwop brand public [--json]\n");
|
|
3457
|
+
return 0;
|
|
3458
|
+
}
|
|
3459
|
+
const res = await requestJson(ctx, PUBLIC_PATH, { auth: false });
|
|
3460
|
+
if (ctx.json) {
|
|
3461
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3462
|
+
return 0;
|
|
3463
|
+
}
|
|
3464
|
+
showIdentity(ctx, res.body?.identity);
|
|
3465
|
+
return 0;
|
|
3466
|
+
}
|
|
3467
|
+
async function brandGet(ctx, argv) {
|
|
3468
|
+
const { options } = parseOptions(argv, { bool: ["--help"] });
|
|
3469
|
+
if (options.help) {
|
|
3470
|
+
write(ctx.io.stdout, "Usage: openwop brand get [--json]\n");
|
|
3471
|
+
return 0;
|
|
3472
|
+
}
|
|
3473
|
+
const res = await appRequest(ctx, APP_PATH, {}, "Reading the app brand");
|
|
3474
|
+
if (ctx.json) {
|
|
3475
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3476
|
+
return 0;
|
|
3477
|
+
}
|
|
3478
|
+
const brand = res.body?.brand ?? {};
|
|
3479
|
+
writeLine(ctx.io.stdout, `name: ${brand.name ?? "(unnamed)"}`);
|
|
3480
|
+
showIdentity(ctx, brand.identity);
|
|
3481
|
+
return 0;
|
|
3482
|
+
}
|
|
3483
|
+
async function brandSet(ctx, argv) {
|
|
3484
|
+
const { options } = parseOptions(argv, {
|
|
3485
|
+
bool: ["--help"],
|
|
3486
|
+
value: [
|
|
3487
|
+
"--name",
|
|
3488
|
+
"--product-name",
|
|
3489
|
+
"--accent",
|
|
3490
|
+
"--neutral",
|
|
3491
|
+
"--contrast",
|
|
3492
|
+
"--radius",
|
|
3493
|
+
"--default-mode",
|
|
3494
|
+
"--logo",
|
|
3495
|
+
"--favicon",
|
|
3496
|
+
"--identity-json"
|
|
3497
|
+
]
|
|
3498
|
+
});
|
|
3499
|
+
if (options.help) {
|
|
3500
|
+
write(ctx.io.stdout, BRAND_HELP);
|
|
3501
|
+
return 0;
|
|
3502
|
+
}
|
|
3503
|
+
const enumErr = (flag, val, allowed) => {
|
|
3504
|
+
if (val !== void 0 && !allowed.includes(String(val))) {
|
|
3505
|
+
throw new CliError(`--${flag} must be one of: ${allowed.join(" | ")} (got: ${String(val)})`, 2);
|
|
3506
|
+
}
|
|
3507
|
+
};
|
|
3508
|
+
enumErr("contrast", options.contrast, CONTRAST);
|
|
3509
|
+
enumErr("radius", options.radius, RADIUS);
|
|
3510
|
+
enumErr("default-mode", options.defaultMode, MODE);
|
|
3511
|
+
const cur = await appRequest(ctx, APP_PATH, {}, "Reading the app brand");
|
|
3512
|
+
const current = cur.body?.brand ?? {};
|
|
3513
|
+
let identity;
|
|
3514
|
+
if (options.identityJson) {
|
|
3515
|
+
try {
|
|
3516
|
+
identity = JSON.parse(String(options.identityJson));
|
|
3517
|
+
} catch (err) {
|
|
3518
|
+
throw new CliError(`--identity-json is not valid JSON: ${err.message}`, 2);
|
|
3519
|
+
}
|
|
3520
|
+
if (!identity || typeof identity !== "object") throw new CliError("--identity-json must be a JSON object", 2);
|
|
3521
|
+
} else {
|
|
3522
|
+
identity = { ...current.identity ?? {} };
|
|
3523
|
+
if (options.productName) identity.productName = String(options.productName);
|
|
3524
|
+
if (options.logo || options.favicon) {
|
|
3525
|
+
identity.logo = { ...identity.logo ?? {} };
|
|
3526
|
+
if (options.logo) identity.logo.markSrc = String(options.logo);
|
|
3527
|
+
if (options.favicon) identity.logo.faviconSrc = String(options.favicon);
|
|
3528
|
+
}
|
|
3529
|
+
const theme = { ...identity.theme ?? {} };
|
|
3530
|
+
if (options.accent) theme.accentSeed = String(options.accent);
|
|
3531
|
+
if (options.neutral) theme.neutralSeed = String(options.neutral);
|
|
3532
|
+
if (options.contrast) theme.contrastLevel = String(options.contrast);
|
|
3533
|
+
if (options.radius) theme.radius = String(options.radius);
|
|
3534
|
+
if (options.defaultMode) theme.defaultMode = String(options.defaultMode);
|
|
3535
|
+
if (Object.keys(theme).length) identity.theme = theme;
|
|
3536
|
+
}
|
|
3537
|
+
const body = { identity };
|
|
3538
|
+
if (options.name) body.name = String(options.name);
|
|
3539
|
+
const res = await appRequest(ctx, APP_PATH, { method: "PUT", body }, "Updating the app brand");
|
|
3540
|
+
if (ctx.json) {
|
|
3541
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3542
|
+
return 0;
|
|
3543
|
+
}
|
|
3544
|
+
writeLine(ctx.io.stdout, "App brand updated. Applied live to the running app.");
|
|
3545
|
+
showIdentity(ctx, res.body?.brand?.identity);
|
|
3546
|
+
return 0;
|
|
3547
|
+
}
|
|
3548
|
+
|
|
3549
|
+
// src/cli/crm.ts
|
|
3550
|
+
var BASE = "/v1/host/sample/crm/contacts";
|
|
3551
|
+
var CRM_HELP = `Usage:
|
|
3552
|
+
openwop crm list [--stage <s>] [--json]
|
|
3553
|
+
openwop crm get <contactId> [--json]
|
|
3554
|
+
openwop crm create --name <name> [--email <e>] [--company <c>] [--stage <s>] [--json]
|
|
3555
|
+
openwop crm update <contactId> [--name n] [--email e] [--company c] [--stage s] [--json]
|
|
3556
|
+
openwop crm delete <contactId> [--yes]
|
|
3557
|
+
openwop crm triage <contactId> [--json]
|
|
3558
|
+
|
|
3559
|
+
CRM contacts (host-extension ${BASE}). A contact has a name, optional email /
|
|
3560
|
+
company, and a pipeline stage. \`triage\` runs the host's triage workflow over a
|
|
3561
|
+
contact and returns the run. The host is the authority; the CLI mirrors + relays.
|
|
3562
|
+
`;
|
|
3563
|
+
async function runCrm(ctx, argv) {
|
|
3564
|
+
const sub = argv[0] ?? "list";
|
|
3565
|
+
if (sub === "--help" || sub === "-h") {
|
|
3566
|
+
write(ctx.io.stdout, CRM_HELP);
|
|
3567
|
+
return 0;
|
|
3568
|
+
}
|
|
3569
|
+
const args = argv.slice(["list", "get", "create", "update", "delete", "triage"].includes(sub) ? 1 : 0);
|
|
3570
|
+
switch (sub) {
|
|
3571
|
+
case "list":
|
|
3572
|
+
return crmList(ctx, args);
|
|
3573
|
+
case "get":
|
|
3574
|
+
return crmGet(ctx, args);
|
|
3575
|
+
case "create":
|
|
3576
|
+
return crmCreate(ctx, args);
|
|
3577
|
+
case "update":
|
|
3578
|
+
return crmUpdate(ctx, args);
|
|
3579
|
+
case "delete":
|
|
3580
|
+
return crmDelete(ctx, args);
|
|
3581
|
+
case "triage":
|
|
3582
|
+
return crmTriage(ctx, args);
|
|
3583
|
+
default:
|
|
3584
|
+
throw new CliError(`Unknown crm command: ${sub}
|
|
3585
|
+
Run \`openwop crm --help\` for usage.`);
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
async function crmList(ctx, argv) {
|
|
3589
|
+
const { options } = parseOptions(argv, { bool: ["--help"], value: ["--stage"] });
|
|
3590
|
+
if (options.help) {
|
|
3591
|
+
write(ctx.io.stdout, CRM_HELP);
|
|
3592
|
+
return 0;
|
|
3593
|
+
}
|
|
3594
|
+
const q = options.stage ? `?stage=${encodeURIComponent(String(options.stage))}` : "";
|
|
3595
|
+
const res = await requestJson(ctx, `${BASE}${q}`);
|
|
3596
|
+
if (ctx.json) {
|
|
3597
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3598
|
+
return 0;
|
|
3599
|
+
}
|
|
3600
|
+
const items = Array.isArray(res.body?.contacts) ? res.body.contacts : Array.isArray(res.body) ? res.body : [];
|
|
3601
|
+
if (items.length === 0) {
|
|
3602
|
+
writeLine(ctx.io.stdout, "No contacts.");
|
|
3603
|
+
return 0;
|
|
3604
|
+
}
|
|
3605
|
+
writeLine(ctx.io.stdout, formatTable(
|
|
3606
|
+
items.map((c) => ({ id: c.id ?? "", name: c.name ?? "", stage: c.stage ?? "", email: c.email ?? "", company: c.company ?? "" })),
|
|
3607
|
+
["id", "name", "stage", "email", "company"]
|
|
3608
|
+
));
|
|
3609
|
+
return 0;
|
|
3610
|
+
}
|
|
3611
|
+
async function crmGet(ctx, argv) {
|
|
3612
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"] });
|
|
3613
|
+
if (options.help || positionals.length !== 1) {
|
|
3614
|
+
write(ctx.io.stdout, "Usage: openwop crm get <contactId> [--json]\n");
|
|
3615
|
+
return options.help ? 0 : 2;
|
|
3616
|
+
}
|
|
3617
|
+
const res = await requestJson(ctx, `${BASE}/${encodeURIComponent(positionals[0])}`);
|
|
3618
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3619
|
+
return 0;
|
|
3620
|
+
}
|
|
3621
|
+
async function crmCreate(ctx, argv) {
|
|
3622
|
+
const { options } = parseOptions(argv, { value: ["--name", "--email", "--company", "--stage"] });
|
|
3623
|
+
if (!options.name) {
|
|
3624
|
+
write(ctx.io.stderr, "Usage: openwop crm create --name <name> [--email e] [--company c] [--stage s] [--json]\n");
|
|
3625
|
+
return 2;
|
|
3626
|
+
}
|
|
3627
|
+
const body = { name: String(options.name) };
|
|
3628
|
+
for (const k of ["email", "company", "stage"]) if (options[k]) body[k] = String(options[k]);
|
|
3629
|
+
const res = await requestJson(ctx, BASE, { method: "POST", body });
|
|
3630
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
3631
|
+
else writeLine(ctx.io.stdout, `Created contact ${res.body?.id ?? ""} (${body.name}).`);
|
|
3632
|
+
return 0;
|
|
3633
|
+
}
|
|
3634
|
+
async function crmUpdate(ctx, argv) {
|
|
3635
|
+
const { options, positionals } = parseOptions(argv, { value: ["--name", "--email", "--company", "--stage"] });
|
|
3636
|
+
if (positionals.length !== 1) {
|
|
3637
|
+
write(ctx.io.stderr, "Usage: openwop crm update <contactId> [--name n] [--email e] [--company c] [--stage s] [--json]\n");
|
|
3638
|
+
return 2;
|
|
3639
|
+
}
|
|
3640
|
+
const body = {};
|
|
3641
|
+
for (const k of ["name", "email", "company", "stage"]) if (options[k]) body[k] = String(options[k]);
|
|
3642
|
+
const res = await requestJson(ctx, `${BASE}/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body });
|
|
3643
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
3644
|
+
else writeLine(ctx.io.stdout, `Updated contact ${positionals[0]}.`);
|
|
3645
|
+
return 0;
|
|
3646
|
+
}
|
|
3647
|
+
async function crmDelete(ctx, argv) {
|
|
3648
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--yes"] });
|
|
3649
|
+
if (options.help || positionals.length !== 1) {
|
|
3650
|
+
write(ctx.io.stdout, "Usage: openwop crm delete <contactId> [--yes]\n");
|
|
3651
|
+
return options.help ? 0 : 2;
|
|
3652
|
+
}
|
|
3653
|
+
if (!options.yes) throw new CliError(`Refusing to delete contact ${positionals[0]} without --yes.`, 2);
|
|
3654
|
+
await requestJson(ctx, `${BASE}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
3655
|
+
writeLine(ctx.io.stdout, `Deleted contact ${positionals[0]}.`);
|
|
3656
|
+
return 0;
|
|
3657
|
+
}
|
|
3658
|
+
async function crmTriage(ctx, argv) {
|
|
3659
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"] });
|
|
3660
|
+
if (options.help || positionals.length !== 1) {
|
|
3661
|
+
write(ctx.io.stdout, "Usage: openwop crm triage <contactId> [--json]\n");
|
|
3662
|
+
return options.help ? 0 : 2;
|
|
3663
|
+
}
|
|
3664
|
+
const res = await requestJson(ctx, `${BASE}/${encodeURIComponent(positionals[0])}/triage`, { method: "POST", body: {} });
|
|
3665
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
3666
|
+
else writeLine(ctx.io.stdout, `Triaged contact ${positionals[0]} \u2192 run ${res.body?.runId ?? "(see --json)"}.`);
|
|
3667
|
+
return 0;
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3670
|
+
// src/cli/csm.ts
|
|
3671
|
+
var BASE2 = "/v1/host/sample/csm/accounts";
|
|
3672
|
+
var CSM_HELP = `Usage:
|
|
3673
|
+
openwop csm list [--json]
|
|
3674
|
+
openwop csm get <accountId> [--json]
|
|
3675
|
+
openwop csm create --name <name> [--health-score <0-100>] [--json]
|
|
3676
|
+
openwop csm update <accountId> [--name n] [--health-score <0-100>] [--json]
|
|
3677
|
+
openwop csm delete <accountId> [--yes]
|
|
3678
|
+
|
|
3679
|
+
Customer-Success accounts (host-extension ${BASE2}). An account has a name and a
|
|
3680
|
+
health score. The host is the authority; the CLI mirrors + relays.
|
|
3681
|
+
`;
|
|
3682
|
+
async function runCsm(ctx, argv) {
|
|
3683
|
+
const sub = argv[0] ?? "list";
|
|
3684
|
+
if (sub === "--help" || sub === "-h") {
|
|
3685
|
+
write(ctx.io.stdout, CSM_HELP);
|
|
3686
|
+
return 0;
|
|
3687
|
+
}
|
|
3688
|
+
const args = argv.slice(["list", "get", "create", "update", "delete"].includes(sub) ? 1 : 0);
|
|
3689
|
+
switch (sub) {
|
|
3690
|
+
case "list":
|
|
3691
|
+
return csmList(ctx, args);
|
|
3692
|
+
case "get":
|
|
3693
|
+
return csmGet(ctx, args);
|
|
3694
|
+
case "create":
|
|
3695
|
+
return csmCreate(ctx, args);
|
|
3696
|
+
case "update":
|
|
3697
|
+
return csmUpdate(ctx, args);
|
|
3698
|
+
case "delete":
|
|
3699
|
+
return csmDelete(ctx, args);
|
|
3700
|
+
default:
|
|
3701
|
+
throw new CliError(`Unknown csm command: ${sub}
|
|
3702
|
+
Run \`openwop csm --help\` for usage.`);
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
async function csmList(ctx, argv) {
|
|
3706
|
+
const { options } = parseOptions(argv, { bool: ["--help"] });
|
|
3707
|
+
if (options.help) {
|
|
3708
|
+
write(ctx.io.stdout, CSM_HELP);
|
|
3709
|
+
return 0;
|
|
3710
|
+
}
|
|
3711
|
+
const res = await requestJson(ctx, BASE2);
|
|
3712
|
+
if (ctx.json) {
|
|
3713
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3714
|
+
return 0;
|
|
3715
|
+
}
|
|
3716
|
+
const items = Array.isArray(res.body?.accounts) ? res.body.accounts : Array.isArray(res.body) ? res.body : [];
|
|
3717
|
+
if (items.length === 0) {
|
|
3718
|
+
writeLine(ctx.io.stdout, "No accounts.");
|
|
3719
|
+
return 0;
|
|
3720
|
+
}
|
|
3721
|
+
writeLine(ctx.io.stdout, formatTable(
|
|
3722
|
+
items.map((a) => ({ id: a.id ?? "", name: a.name ?? "", healthScore: a.healthScore ?? "" })),
|
|
3723
|
+
["id", "name", "healthScore"]
|
|
3724
|
+
));
|
|
3725
|
+
return 0;
|
|
3726
|
+
}
|
|
3727
|
+
async function csmGet(ctx, argv) {
|
|
3728
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"] });
|
|
3729
|
+
if (options.help || positionals.length !== 1) {
|
|
3730
|
+
write(ctx.io.stdout, "Usage: openwop csm get <accountId> [--json]\n");
|
|
3731
|
+
return options.help ? 0 : 2;
|
|
3732
|
+
}
|
|
3733
|
+
const res = await requestJson(ctx, `${BASE2}/${encodeURIComponent(positionals[0])}`);
|
|
3734
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3735
|
+
return 0;
|
|
3736
|
+
}
|
|
3737
|
+
async function csmCreate(ctx, argv) {
|
|
3738
|
+
const { options } = parseOptions(argv, { value: ["--name", "--health-score"] });
|
|
3739
|
+
if (!options.name) {
|
|
3740
|
+
write(ctx.io.stderr, "Usage: openwop csm create --name <name> [--health-score <0-100>] [--json]\n");
|
|
3741
|
+
return 2;
|
|
3742
|
+
}
|
|
3743
|
+
const body = { name: String(options.name) };
|
|
3744
|
+
if (options.healthScore !== void 0) body.healthScore = Number(options.healthScore);
|
|
3745
|
+
const res = await requestJson(ctx, BASE2, { method: "POST", body });
|
|
3746
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
3747
|
+
else writeLine(ctx.io.stdout, `Created account ${res.body?.id ?? ""} (${String(options.name)}).`);
|
|
3748
|
+
return 0;
|
|
3749
|
+
}
|
|
3750
|
+
async function csmUpdate(ctx, argv) {
|
|
3751
|
+
const { options, positionals } = parseOptions(argv, { value: ["--name", "--health-score"] });
|
|
3752
|
+
if (positionals.length !== 1) {
|
|
3753
|
+
write(ctx.io.stderr, "Usage: openwop csm update <accountId> [--name n] [--health-score <0-100>] [--json]\n");
|
|
3754
|
+
return 2;
|
|
3755
|
+
}
|
|
3756
|
+
const body = {};
|
|
3757
|
+
if (options.name) body.name = String(options.name);
|
|
3758
|
+
if (options.healthScore !== void 0) body.healthScore = Number(options.healthScore);
|
|
3759
|
+
const res = await requestJson(ctx, `${BASE2}/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body });
|
|
3760
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
3761
|
+
else writeLine(ctx.io.stdout, `Updated account ${positionals[0]}.`);
|
|
3762
|
+
return 0;
|
|
3763
|
+
}
|
|
3764
|
+
async function csmDelete(ctx, argv) {
|
|
3765
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--yes"] });
|
|
3766
|
+
if (options.help || positionals.length !== 1) {
|
|
3767
|
+
write(ctx.io.stdout, "Usage: openwop csm delete <accountId> [--yes]\n");
|
|
3768
|
+
return options.help ? 0 : 2;
|
|
3769
|
+
}
|
|
3770
|
+
if (!options.yes) throw new CliError(`Refusing to delete account ${positionals[0]} without --yes.`, 2);
|
|
3771
|
+
await requestJson(ctx, `${BASE2}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
3772
|
+
writeLine(ctx.io.stdout, `Deleted account ${positionals[0]}.`);
|
|
3773
|
+
return 0;
|
|
3774
|
+
}
|
|
3775
|
+
|
|
3776
|
+
// src/cli/comments.ts
|
|
3777
|
+
var base = (org) => `/v1/host/sample/comments/orgs/${encodeURIComponent(org)}/comments`;
|
|
3778
|
+
var COMMENTS_HELP = `Usage:
|
|
3779
|
+
openwop comments list --org <orgId> --resource-type <t> --resource-id <id> [--json]
|
|
3780
|
+
openwop comments create --org <orgId> --resource-type <t> --resource-id <id> --body <text> [--parent <commentId>] [--json]
|
|
3781
|
+
openwop comments update <commentId> --org <orgId> --body <text> [--json]
|
|
3782
|
+
openwop comments delete <commentId> --org <orgId> [--yes]
|
|
3783
|
+
|
|
3784
|
+
Threaded comments on a (resourceType, resourceId) target (host-extension, org-scoped,
|
|
3785
|
+
ADR 0021). Every command needs --org. The host validates the resource in-org and emits
|
|
3786
|
+
a notification on add/reply; the CLI mirrors + relays.
|
|
3787
|
+
`;
|
|
3788
|
+
async function runComments(ctx, argv) {
|
|
3789
|
+
const sub = argv[0] ?? "list";
|
|
3790
|
+
if (sub === "--help" || sub === "-h") {
|
|
3791
|
+
write(ctx.io.stdout, COMMENTS_HELP);
|
|
3792
|
+
return 0;
|
|
3793
|
+
}
|
|
3794
|
+
const args = argv.slice(["list", "create", "update", "delete"].includes(sub) ? 1 : 0);
|
|
3795
|
+
switch (sub) {
|
|
3796
|
+
case "list":
|
|
3797
|
+
return commentsList(ctx, args);
|
|
3798
|
+
case "create":
|
|
3799
|
+
return commentsCreate(ctx, args);
|
|
3800
|
+
case "update":
|
|
3801
|
+
return commentsUpdate(ctx, args);
|
|
3802
|
+
case "delete":
|
|
3803
|
+
return commentsDelete(ctx, args);
|
|
3804
|
+
default:
|
|
3805
|
+
throw new CliError(`Unknown comments command: ${sub}
|
|
3806
|
+
Run \`openwop comments --help\` for usage.`);
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
async function commentsList(ctx, argv) {
|
|
3810
|
+
const { options } = parseOptions(argv, { bool: ["--help"], value: ["--org", "--resource-type", "--resource-id"] });
|
|
3811
|
+
if (options.help) {
|
|
3812
|
+
write(ctx.io.stdout, COMMENTS_HELP);
|
|
3813
|
+
return 0;
|
|
3814
|
+
}
|
|
3815
|
+
const org = requireOrg(options.org);
|
|
3816
|
+
if (!options.resourceType || !options.resourceId) {
|
|
3817
|
+
write(ctx.io.stderr, "comments list needs --resource-type and --resource-id.\n");
|
|
3818
|
+
return 2;
|
|
3819
|
+
}
|
|
3820
|
+
const q = `?resourceType=${encodeURIComponent(String(options.resourceType))}&resourceId=${encodeURIComponent(String(options.resourceId))}`;
|
|
3821
|
+
const res = await requestJson(ctx, `${base(org)}${q}`);
|
|
3822
|
+
if (ctx.json) {
|
|
3823
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3824
|
+
return 0;
|
|
3825
|
+
}
|
|
3826
|
+
const items = Array.isArray(res.body?.comments) ? res.body.comments : [];
|
|
3827
|
+
if (items.length === 0) {
|
|
3828
|
+
writeLine(ctx.io.stdout, "No comments.");
|
|
3829
|
+
return 0;
|
|
3830
|
+
}
|
|
3831
|
+
writeLine(ctx.io.stdout, formatTable(
|
|
3832
|
+
items.map((c) => ({ id: c.id ?? "", author: c.authorId ?? "", parent: c.parentId ?? "", body: (c.body ?? "").slice(0, 60) })),
|
|
3833
|
+
["id", "author", "parent", "body"]
|
|
3834
|
+
));
|
|
3835
|
+
return 0;
|
|
3836
|
+
}
|
|
3837
|
+
async function commentsCreate(ctx, argv) {
|
|
3838
|
+
const { options } = parseOptions(argv, { value: ["--org", "--resource-type", "--resource-id", "--body", "--parent"] });
|
|
3839
|
+
const org = requireOrg(options.org);
|
|
3840
|
+
if (!options.resourceType || !options.resourceId || !options.body) {
|
|
3841
|
+
write(ctx.io.stderr, "comments create needs --resource-type, --resource-id, and --body.\n");
|
|
3842
|
+
return 2;
|
|
3843
|
+
}
|
|
3844
|
+
const body = { resourceType: String(options.resourceType), resourceId: String(options.resourceId), body: String(options.body) };
|
|
3845
|
+
if (options.parent) body.parentId = String(options.parent);
|
|
3846
|
+
const res = await requestJson(ctx, base(org), { method: "POST", body });
|
|
3847
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
3848
|
+
else writeLine(ctx.io.stdout, `Added comment ${res.body?.id ?? ""}.`);
|
|
3849
|
+
return 0;
|
|
3850
|
+
}
|
|
3851
|
+
async function commentsUpdate(ctx, argv) {
|
|
3852
|
+
const { options, positionals } = parseOptions(argv, { value: ["--org", "--body"] });
|
|
3853
|
+
const org = requireOrg(options.org);
|
|
3854
|
+
if (positionals.length !== 1 || !options.body) {
|
|
3855
|
+
write(ctx.io.stderr, "Usage: openwop comments update <commentId> --org <orgId> --body <text> [--json]\n");
|
|
3856
|
+
return 2;
|
|
3857
|
+
}
|
|
3858
|
+
const res = await requestJson(ctx, `${base(org)}/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body: { body: String(options.body) } });
|
|
3859
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
3860
|
+
else writeLine(ctx.io.stdout, `Updated comment ${positionals[0]}.`);
|
|
3861
|
+
return 0;
|
|
3862
|
+
}
|
|
3863
|
+
async function commentsDelete(ctx, argv) {
|
|
3864
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--yes"], value: ["--org"] });
|
|
3865
|
+
const org = requireOrg(options.org);
|
|
3866
|
+
if (options.help || positionals.length !== 1) {
|
|
3867
|
+
write(ctx.io.stdout, "Usage: openwop comments delete <commentId> --org <orgId> [--yes]\n");
|
|
3868
|
+
return options.help ? 0 : 2;
|
|
3869
|
+
}
|
|
3870
|
+
if (!options.yes) throw new CliError(`Refusing to delete comment ${positionals[0]} without --yes.`, 2);
|
|
3871
|
+
await requestJson(ctx, `${base(org)}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
3872
|
+
writeLine(ctx.io.stdout, `Deleted comment ${positionals[0]}.`);
|
|
3873
|
+
return 0;
|
|
3874
|
+
}
|
|
3875
|
+
|
|
3876
|
+
// src/cli/sharing.ts
|
|
3877
|
+
var links = (org) => `/v1/host/sample/sharing/orgs/${encodeURIComponent(org)}/links`;
|
|
3878
|
+
var SHARING_HELP = `Usage:
|
|
3879
|
+
openwop sharing list --org <orgId> [--json]
|
|
3880
|
+
openwop sharing create --org <orgId> --resource-type <t> --resource-id <id> [--json]
|
|
3881
|
+
openwop sharing revoke <token> --org <orgId> [--yes]
|
|
3882
|
+
openwop sharing resolve <token> [--json]
|
|
3883
|
+
|
|
3884
|
+
Shareable links to a resource (host-extension, ADR 0013). \`list\`/\`create\`/\`revoke\`
|
|
3885
|
+
are org-scoped (need --org, RBAC-gated). \`resolve\` reads a public /shared/<token>
|
|
3886
|
+
link WITHOUT auth \u2014 the unguessable token IS the credential.
|
|
3887
|
+
`;
|
|
3888
|
+
async function runSharing(ctx, argv) {
|
|
3889
|
+
const sub = argv[0] ?? "list";
|
|
3890
|
+
if (sub === "--help" || sub === "-h") {
|
|
3891
|
+
write(ctx.io.stdout, SHARING_HELP);
|
|
3892
|
+
return 0;
|
|
3893
|
+
}
|
|
3894
|
+
const args = argv.slice(["list", "create", "revoke", "resolve"].includes(sub) ? 1 : 0);
|
|
3895
|
+
switch (sub) {
|
|
3896
|
+
case "list":
|
|
3897
|
+
return sharingList(ctx, args);
|
|
3898
|
+
case "create":
|
|
3899
|
+
return sharingCreate(ctx, args);
|
|
3900
|
+
case "revoke":
|
|
3901
|
+
return sharingRevoke(ctx, args);
|
|
3902
|
+
case "resolve":
|
|
3903
|
+
return sharingResolve(ctx, args);
|
|
3904
|
+
default:
|
|
3905
|
+
throw new CliError(`Unknown sharing command: ${sub}
|
|
3906
|
+
Run \`openwop sharing --help\` for usage.`);
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
async function sharingList(ctx, argv) {
|
|
3910
|
+
const { options } = parseOptions(argv, { bool: ["--help"], value: ["--org"] });
|
|
3911
|
+
if (options.help) {
|
|
3912
|
+
write(ctx.io.stdout, SHARING_HELP);
|
|
3913
|
+
return 0;
|
|
3914
|
+
}
|
|
3915
|
+
const org = requireOrg(options.org);
|
|
3916
|
+
const res = await requestJson(ctx, links(org));
|
|
3917
|
+
if (ctx.json) {
|
|
3918
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3919
|
+
return 0;
|
|
3920
|
+
}
|
|
3921
|
+
const items = Array.isArray(res.body?.links) ? res.body.links : [];
|
|
3922
|
+
if (items.length === 0) {
|
|
3923
|
+
writeLine(ctx.io.stdout, "No share links.");
|
|
3924
|
+
return 0;
|
|
3925
|
+
}
|
|
3926
|
+
writeLine(ctx.io.stdout, formatTable(
|
|
3927
|
+
items.map((l) => ({ token: l.token ?? "", resource: `${l.resourceType ?? ""}:${l.resourceId ?? ""}`, createdAt: l.createdAt ?? "" })),
|
|
3928
|
+
["token", "resource", "createdAt"]
|
|
3929
|
+
));
|
|
3930
|
+
return 0;
|
|
3931
|
+
}
|
|
3932
|
+
async function sharingCreate(ctx, argv) {
|
|
3933
|
+
const { options } = parseOptions(argv, { value: ["--org", "--resource-type", "--resource-id"] });
|
|
3934
|
+
const org = requireOrg(options.org);
|
|
3935
|
+
if (!options.resourceType || !options.resourceId) {
|
|
3936
|
+
write(ctx.io.stderr, "sharing create needs --resource-type and --resource-id.\n");
|
|
3937
|
+
return 2;
|
|
3938
|
+
}
|
|
3939
|
+
const body = { resourceType: String(options.resourceType), resourceId: String(options.resourceId) };
|
|
3940
|
+
const res = await requestJson(ctx, links(org), { method: "POST", body });
|
|
3941
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
3942
|
+
else writeLine(ctx.io.stdout, `Created share link ${res.body?.token ?? ""}${res.body?.url ? ` (${res.body.url})` : ""}.`);
|
|
3943
|
+
return 0;
|
|
3944
|
+
}
|
|
3945
|
+
async function sharingRevoke(ctx, argv) {
|
|
3946
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--yes"], value: ["--org"] });
|
|
3947
|
+
const org = requireOrg(options.org);
|
|
3948
|
+
if (options.help || positionals.length !== 1) {
|
|
3949
|
+
write(ctx.io.stdout, "Usage: openwop sharing revoke <token> --org <orgId> [--yes]\n");
|
|
3950
|
+
return options.help ? 0 : 2;
|
|
3951
|
+
}
|
|
3952
|
+
if (!options.yes) throw new CliError(`Refusing to revoke share link ${positionals[0]} without --yes.`, 2);
|
|
3953
|
+
await requestJson(ctx, `${links(org)}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
3954
|
+
writeLine(ctx.io.stdout, `Revoked share link ${positionals[0]}.`);
|
|
3955
|
+
return 0;
|
|
3956
|
+
}
|
|
3957
|
+
async function sharingResolve(ctx, argv) {
|
|
3958
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"] });
|
|
3959
|
+
if (options.help || positionals.length !== 1) {
|
|
3960
|
+
write(ctx.io.stdout, "Usage: openwop sharing resolve <token> [--json]\n");
|
|
3961
|
+
return options.help ? 0 : 2;
|
|
3962
|
+
}
|
|
3963
|
+
const res = await requestJson(ctx, `/v1/host/sample/shared/${encodeURIComponent(positionals[0])}`, { auth: false });
|
|
3964
|
+
writeJson(ctx.io.stdout, res.body);
|
|
3965
|
+
return 0;
|
|
3966
|
+
}
|
|
3967
|
+
|
|
3968
|
+
// src/cli/forms.ts
|
|
3969
|
+
var base2 = (org) => `/v1/host/sample/forms/orgs/${encodeURIComponent(org)}/forms`;
|
|
3970
|
+
var FORMS_HELP = `Usage:
|
|
3971
|
+
openwop forms list --org <orgId> [--json]
|
|
3972
|
+
openwop forms get <formId> --org <orgId> [--json]
|
|
3973
|
+
openwop forms create --org <orgId> --title <t> [--fields-json '[...]'] [--json]
|
|
3974
|
+
openwop forms update <formId> --org <orgId> [--title <t>] [--fields-json '[...]'] [--json]
|
|
3975
|
+
openwop forms status <formId> --org <orgId> --status <draft|published|closed> [--json]
|
|
3976
|
+
openwop forms delete <formId> --org <orgId> [--yes]
|
|
3977
|
+
openwop forms submissions <formId> --org <orgId> [--json]
|
|
3978
|
+
|
|
3979
|
+
Form builder + intake (host-extension, org-scoped). Every command needs --org.
|
|
3980
|
+
A form has a title + fields[]; \`status\` publishes/closes it; \`submissions\` reads
|
|
3981
|
+
the collected responses. The host is the authority; the CLI mirrors + relays.
|
|
3982
|
+
`;
|
|
3983
|
+
function parseFields(raw) {
|
|
3984
|
+
if (raw === void 0) return void 0;
|
|
3985
|
+
try {
|
|
3986
|
+
return JSON.parse(String(raw));
|
|
3987
|
+
} catch {
|
|
3988
|
+
throw new CliError("--fields-json must be valid JSON.", 2);
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
async function runForms(ctx, argv) {
|
|
3992
|
+
const sub = argv[0] ?? "list";
|
|
3993
|
+
if (sub === "--help" || sub === "-h") {
|
|
3994
|
+
write(ctx.io.stdout, FORMS_HELP);
|
|
3995
|
+
return 0;
|
|
3996
|
+
}
|
|
3997
|
+
const args = argv.slice(["list", "get", "create", "update", "status", "delete", "submissions"].includes(sub) ? 1 : 0);
|
|
3998
|
+
switch (sub) {
|
|
3999
|
+
case "list":
|
|
4000
|
+
return formsList(ctx, args);
|
|
4001
|
+
case "get":
|
|
4002
|
+
return formsGet(ctx, args);
|
|
4003
|
+
case "create":
|
|
4004
|
+
return formsCreate(ctx, args);
|
|
4005
|
+
case "update":
|
|
4006
|
+
return formsUpdate(ctx, args);
|
|
4007
|
+
case "status":
|
|
4008
|
+
return formsStatus(ctx, args);
|
|
4009
|
+
case "delete":
|
|
4010
|
+
return formsDelete(ctx, args);
|
|
4011
|
+
case "submissions":
|
|
4012
|
+
return formsSubmissions(ctx, args);
|
|
4013
|
+
default:
|
|
4014
|
+
throw new CliError(`Unknown forms command: ${sub}
|
|
4015
|
+
Run \`openwop forms --help\` for usage.`);
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
async function formsList(ctx, argv) {
|
|
4019
|
+
const { options } = parseOptions(argv, { bool: ["--help"], value: ["--org"] });
|
|
4020
|
+
if (options.help) {
|
|
4021
|
+
write(ctx.io.stdout, FORMS_HELP);
|
|
4022
|
+
return 0;
|
|
4023
|
+
}
|
|
4024
|
+
const org = requireOrg(options.org);
|
|
4025
|
+
const res = await requestJson(ctx, base2(org));
|
|
4026
|
+
if (ctx.json) {
|
|
4027
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4028
|
+
return 0;
|
|
4029
|
+
}
|
|
4030
|
+
const items = Array.isArray(res.body?.forms) ? res.body.forms : [];
|
|
4031
|
+
if (items.length === 0) {
|
|
4032
|
+
writeLine(ctx.io.stdout, "No forms.");
|
|
4033
|
+
return 0;
|
|
4034
|
+
}
|
|
4035
|
+
writeLine(ctx.io.stdout, formatTable(
|
|
4036
|
+
items.map((f) => ({ id: f.id ?? "", title: f.title ?? "", status: f.status ?? "", fields: Array.isArray(f.fields) ? f.fields.length : "" })),
|
|
4037
|
+
["id", "title", "status", "fields"]
|
|
4038
|
+
));
|
|
4039
|
+
return 0;
|
|
4040
|
+
}
|
|
4041
|
+
async function formsGet(ctx, argv) {
|
|
4042
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"], value: ["--org"] });
|
|
4043
|
+
const org = requireOrg(options.org);
|
|
4044
|
+
if (options.help || positionals.length !== 1) {
|
|
4045
|
+
write(ctx.io.stdout, "Usage: openwop forms get <formId> --org <orgId> [--json]\n");
|
|
4046
|
+
return options.help ? 0 : 2;
|
|
4047
|
+
}
|
|
4048
|
+
const res = await requestJson(ctx, `${base2(org)}/${encodeURIComponent(positionals[0])}`);
|
|
4049
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4050
|
+
return 0;
|
|
4051
|
+
}
|
|
4052
|
+
async function formsCreate(ctx, argv) {
|
|
4053
|
+
const { options } = parseOptions(argv, { value: ["--org", "--title", "--fields-json"] });
|
|
4054
|
+
const org = requireOrg(options.org);
|
|
4055
|
+
if (!options.title) {
|
|
4056
|
+
write(ctx.io.stderr, "Usage: openwop forms create --org <orgId> --title <t> [--fields-json '[...]'] [--json]\n");
|
|
4057
|
+
return 2;
|
|
4058
|
+
}
|
|
4059
|
+
const body = { title: String(options.title) };
|
|
4060
|
+
const fields = parseFields(options.fieldsJson);
|
|
4061
|
+
if (fields !== void 0) body.fields = fields;
|
|
4062
|
+
const res = await requestJson(ctx, base2(org), { method: "POST", body });
|
|
4063
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4064
|
+
else writeLine(ctx.io.stdout, `Created form ${res.body?.id ?? ""} (${String(options.title)}).`);
|
|
4065
|
+
return 0;
|
|
4066
|
+
}
|
|
4067
|
+
async function formsUpdate(ctx, argv) {
|
|
4068
|
+
const { options, positionals } = parseOptions(argv, { value: ["--org", "--title", "--fields-json"] });
|
|
4069
|
+
const org = requireOrg(options.org);
|
|
4070
|
+
if (positionals.length !== 1) {
|
|
4071
|
+
write(ctx.io.stderr, "Usage: openwop forms update <formId> --org <orgId> [--title <t>] [--fields-json ...] [--json]\n");
|
|
4072
|
+
return 2;
|
|
4073
|
+
}
|
|
4074
|
+
const body = {};
|
|
4075
|
+
if (options.title) body.title = String(options.title);
|
|
4076
|
+
const fields = parseFields(options.fieldsJson);
|
|
4077
|
+
if (fields !== void 0) body.fields = fields;
|
|
4078
|
+
const res = await requestJson(ctx, `${base2(org)}/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body });
|
|
4079
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4080
|
+
else writeLine(ctx.io.stdout, `Updated form ${positionals[0]}.`);
|
|
4081
|
+
return 0;
|
|
4082
|
+
}
|
|
4083
|
+
async function formsStatus(ctx, argv) {
|
|
4084
|
+
const { options, positionals } = parseOptions(argv, { value: ["--org", "--status"] });
|
|
4085
|
+
const org = requireOrg(options.org);
|
|
4086
|
+
if (positionals.length !== 1 || !options.status) {
|
|
4087
|
+
write(ctx.io.stderr, "Usage: openwop forms status <formId> --org <orgId> --status <draft|published|closed> [--json]\n");
|
|
4088
|
+
return 2;
|
|
4089
|
+
}
|
|
4090
|
+
const res = await requestJson(ctx, `${base2(org)}/${encodeURIComponent(positionals[0])}/status`, { method: "POST", body: { status: String(options.status) } });
|
|
4091
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4092
|
+
else writeLine(ctx.io.stdout, `Form ${positionals[0]} \u2192 status ${options.status}.`);
|
|
4093
|
+
return 0;
|
|
4094
|
+
}
|
|
4095
|
+
async function formsDelete(ctx, argv) {
|
|
4096
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--yes"], value: ["--org"] });
|
|
4097
|
+
const org = requireOrg(options.org);
|
|
4098
|
+
if (options.help || positionals.length !== 1) {
|
|
4099
|
+
write(ctx.io.stdout, "Usage: openwop forms delete <formId> --org <orgId> [--yes]\n");
|
|
4100
|
+
return options.help ? 0 : 2;
|
|
4101
|
+
}
|
|
4102
|
+
if (!options.yes) throw new CliError(`Refusing to delete form ${positionals[0]} without --yes.`, 2);
|
|
4103
|
+
await requestJson(ctx, `${base2(org)}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
4104
|
+
writeLine(ctx.io.stdout, `Deleted form ${positionals[0]}.`);
|
|
4105
|
+
return 0;
|
|
4106
|
+
}
|
|
4107
|
+
async function formsSubmissions(ctx, argv) {
|
|
4108
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"], value: ["--org"] });
|
|
4109
|
+
const org = requireOrg(options.org);
|
|
4110
|
+
if (options.help || positionals.length !== 1) {
|
|
4111
|
+
write(ctx.io.stdout, "Usage: openwop forms submissions <formId> --org <orgId> [--json]\n");
|
|
4112
|
+
return options.help ? 0 : 2;
|
|
4113
|
+
}
|
|
4114
|
+
const res = await requestJson(ctx, `${base2(org)}/${encodeURIComponent(positionals[0])}/submissions`);
|
|
4115
|
+
if (ctx.json) {
|
|
4116
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4117
|
+
return 0;
|
|
4118
|
+
}
|
|
4119
|
+
const subs = Array.isArray(res.body?.submissions) ? res.body.submissions : [];
|
|
4120
|
+
writeLine(ctx.io.stdout, `${subs.length} submission(s).`);
|
|
4121
|
+
if (subs.length) writeJson(ctx.io.stdout, subs);
|
|
4122
|
+
return 0;
|
|
4123
|
+
}
|
|
4124
|
+
|
|
4125
|
+
// src/cli/email.ts
|
|
4126
|
+
var base3 = (org) => `/v1/host/sample/email/orgs/${encodeURIComponent(org)}`;
|
|
4127
|
+
var EMAIL_HELP = `Usage:
|
|
4128
|
+
openwop email templates list --org <orgId> [--json]
|
|
4129
|
+
openwop email templates get <templateId> --org <orgId> [--json]
|
|
4130
|
+
openwop email templates create --org <orgId> --name <n> --subject <s> --body <b> [--json]
|
|
4131
|
+
openwop email templates update <templateId> --org <orgId> [--name n] [--subject s] [--body b] [--json]
|
|
4132
|
+
openwop email templates delete <templateId> --org <orgId> [--yes]
|
|
4133
|
+
openwop email campaigns list --org <orgId> [--json]
|
|
4134
|
+
openwop email campaigns get <campaignId> --org <orgId> [--json]
|
|
4135
|
+
openwop email campaigns create --org <orgId> --template <templateId> [--name <n>] [--json]
|
|
4136
|
+
openwop email campaigns delete <campaignId> --org <orgId> [--yes]
|
|
4137
|
+
openwop email campaigns send <campaignId> --org <orgId> [--yes] [--json]
|
|
4138
|
+
openwop email campaigns sends <campaignId> --org <orgId> [--json]
|
|
4139
|
+
|
|
4140
|
+
Outbound email (host-extension, org-scoped). Templates hold a name/subject/body;
|
|
4141
|
+
a campaign binds a template + audience; \`send\` dispatches it and \`sends\` reads the
|
|
4142
|
+
delivery log. Every command needs --org. The host is the authority; the CLI relays.
|
|
4143
|
+
`;
|
|
4144
|
+
async function runEmail(ctx, argv) {
|
|
4145
|
+
const group = argv[0];
|
|
4146
|
+
if (group === "--help" || group === "-h" || group === void 0) {
|
|
4147
|
+
write(ctx.io.stdout, EMAIL_HELP);
|
|
4148
|
+
return group === void 0 ? 2 : 0;
|
|
4149
|
+
}
|
|
4150
|
+
const rest = argv.slice(1);
|
|
4151
|
+
if (group === "templates") return emailTemplates(ctx, rest);
|
|
4152
|
+
if (group === "campaigns") return emailCampaigns(ctx, rest);
|
|
4153
|
+
throw new CliError(`Unknown email command: ${group}. Use 'templates' or 'campaigns'.`);
|
|
4154
|
+
}
|
|
4155
|
+
async function emailTemplates(ctx, argv) {
|
|
4156
|
+
const sub = argv[0] ?? "list";
|
|
4157
|
+
const args = argv.slice(["list", "get", "create", "update", "delete"].includes(sub) ? 1 : 0);
|
|
4158
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--name", "--subject", "--body"] });
|
|
4159
|
+
if (options.help) {
|
|
4160
|
+
write(ctx.io.stdout, EMAIL_HELP);
|
|
4161
|
+
return 0;
|
|
4162
|
+
}
|
|
4163
|
+
const org = requireOrg(options.org);
|
|
4164
|
+
const url = `${base3(org)}/templates`;
|
|
4165
|
+
switch (sub) {
|
|
4166
|
+
case "list": {
|
|
4167
|
+
const res = await requestJson(ctx, url);
|
|
4168
|
+
if (ctx.json) {
|
|
4169
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4170
|
+
return 0;
|
|
4171
|
+
}
|
|
4172
|
+
const items = Array.isArray(res.body?.templates) ? res.body.templates : [];
|
|
4173
|
+
if (items.length === 0) {
|
|
4174
|
+
writeLine(ctx.io.stdout, "No templates.");
|
|
4175
|
+
return 0;
|
|
4176
|
+
}
|
|
4177
|
+
writeLine(ctx.io.stdout, formatTable(items.map((t) => ({ id: t.id ?? "", name: t.name ?? "", subject: t.subject ?? "" })), ["id", "name", "subject"]));
|
|
4178
|
+
return 0;
|
|
4179
|
+
}
|
|
4180
|
+
case "get": {
|
|
4181
|
+
if (positionals.length !== 1) {
|
|
4182
|
+
write(ctx.io.stderr, "Usage: openwop email templates get <templateId> --org <orgId>\n");
|
|
4183
|
+
return 2;
|
|
4184
|
+
}
|
|
4185
|
+
const res = await requestJson(ctx, `${url}/${encodeURIComponent(positionals[0])}`);
|
|
4186
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4187
|
+
return 0;
|
|
4188
|
+
}
|
|
4189
|
+
case "create": {
|
|
4190
|
+
if (!options.name || !options.subject || !options.body) {
|
|
4191
|
+
write(ctx.io.stderr, "email templates create needs --name, --subject, --body.\n");
|
|
4192
|
+
return 2;
|
|
4193
|
+
}
|
|
4194
|
+
const res = await requestJson(ctx, url, { method: "POST", body: { name: String(options.name), subject: String(options.subject), body: String(options.body) } });
|
|
4195
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4196
|
+
else writeLine(ctx.io.stdout, `Created template ${res.body?.id ?? ""} (${String(options.name)}).`);
|
|
4197
|
+
return 0;
|
|
4198
|
+
}
|
|
4199
|
+
case "update": {
|
|
4200
|
+
if (positionals.length !== 1) {
|
|
4201
|
+
write(ctx.io.stderr, "Usage: openwop email templates update <templateId> --org <orgId> [--name n] [--subject s] [--body b]\n");
|
|
4202
|
+
return 2;
|
|
4203
|
+
}
|
|
4204
|
+
const patch = {};
|
|
4205
|
+
for (const k of ["name", "subject", "body"]) if (options[k]) patch[k] = String(options[k]);
|
|
4206
|
+
const res = await requestJson(ctx, `${url}/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body: patch });
|
|
4207
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4208
|
+
else writeLine(ctx.io.stdout, `Updated template ${positionals[0]}.`);
|
|
4209
|
+
return 0;
|
|
4210
|
+
}
|
|
4211
|
+
case "delete": {
|
|
4212
|
+
if (positionals.length !== 1) {
|
|
4213
|
+
write(ctx.io.stderr, "Usage: openwop email templates delete <templateId> --org <orgId> [--yes]\n");
|
|
4214
|
+
return 2;
|
|
4215
|
+
}
|
|
4216
|
+
if (!options.yes) throw new CliError(`Refusing to delete template ${positionals[0]} without --yes.`, 2);
|
|
4217
|
+
await requestJson(ctx, `${url}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
4218
|
+
writeLine(ctx.io.stdout, `Deleted template ${positionals[0]}.`);
|
|
4219
|
+
return 0;
|
|
4220
|
+
}
|
|
4221
|
+
default:
|
|
4222
|
+
throw new CliError(`Unknown email templates command: ${sub}`);
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
async function emailCampaigns(ctx, argv) {
|
|
4226
|
+
const sub = argv[0] ?? "list";
|
|
4227
|
+
const args = argv.slice(["list", "get", "create", "delete", "send", "sends"].includes(sub) ? 1 : 0);
|
|
4228
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--template", "--name"] });
|
|
4229
|
+
if (options.help) {
|
|
4230
|
+
write(ctx.io.stdout, EMAIL_HELP);
|
|
4231
|
+
return 0;
|
|
4232
|
+
}
|
|
4233
|
+
const org = requireOrg(options.org);
|
|
4234
|
+
const url = `${base3(org)}/campaigns`;
|
|
4235
|
+
switch (sub) {
|
|
4236
|
+
case "list": {
|
|
4237
|
+
const res = await requestJson(ctx, url);
|
|
4238
|
+
if (ctx.json) {
|
|
4239
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4240
|
+
return 0;
|
|
4241
|
+
}
|
|
4242
|
+
const items = Array.isArray(res.body?.campaigns) ? res.body.campaigns : [];
|
|
4243
|
+
if (items.length === 0) {
|
|
4244
|
+
writeLine(ctx.io.stdout, "No campaigns.");
|
|
4245
|
+
return 0;
|
|
4246
|
+
}
|
|
4247
|
+
writeLine(ctx.io.stdout, formatTable(items.map((c) => ({ id: c.id ?? "", name: c.name ?? "", status: c.status ?? "", template: c.templateId ?? "" })), ["id", "name", "status", "template"]));
|
|
4248
|
+
return 0;
|
|
4249
|
+
}
|
|
4250
|
+
case "get": {
|
|
4251
|
+
if (positionals.length !== 1) {
|
|
4252
|
+
write(ctx.io.stderr, "Usage: openwop email campaigns get <campaignId> --org <orgId>\n");
|
|
4253
|
+
return 2;
|
|
4254
|
+
}
|
|
4255
|
+
const res = await requestJson(ctx, `${url}/${encodeURIComponent(positionals[0])}`);
|
|
4256
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4257
|
+
return 0;
|
|
4258
|
+
}
|
|
4259
|
+
case "create": {
|
|
4260
|
+
if (!options.template) {
|
|
4261
|
+
write(ctx.io.stderr, "email campaigns create needs --template <templateId>.\n");
|
|
4262
|
+
return 2;
|
|
4263
|
+
}
|
|
4264
|
+
const body = { templateId: String(options.template) };
|
|
4265
|
+
if (options.name) body.name = String(options.name);
|
|
4266
|
+
const res = await requestJson(ctx, url, { method: "POST", body });
|
|
4267
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4268
|
+
else writeLine(ctx.io.stdout, `Created campaign ${res.body?.id ?? ""}.`);
|
|
4269
|
+
return 0;
|
|
4270
|
+
}
|
|
4271
|
+
case "delete": {
|
|
4272
|
+
if (positionals.length !== 1) {
|
|
4273
|
+
write(ctx.io.stderr, "Usage: openwop email campaigns delete <campaignId> --org <orgId> [--yes]\n");
|
|
4274
|
+
return 2;
|
|
4275
|
+
}
|
|
4276
|
+
if (!options.yes) throw new CliError(`Refusing to delete campaign ${positionals[0]} without --yes.`, 2);
|
|
4277
|
+
await requestJson(ctx, `${url}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
4278
|
+
writeLine(ctx.io.stdout, `Deleted campaign ${positionals[0]}.`);
|
|
4279
|
+
return 0;
|
|
4280
|
+
}
|
|
4281
|
+
case "send": {
|
|
4282
|
+
if (positionals.length !== 1) {
|
|
4283
|
+
write(ctx.io.stderr, "Usage: openwop email campaigns send <campaignId> --org <orgId> [--yes]\n");
|
|
4284
|
+
return 2;
|
|
4285
|
+
}
|
|
4286
|
+
if (!options.yes) throw new CliError(`Refusing to SEND campaign ${positionals[0]} without --yes (this dispatches real email).`, 2);
|
|
4287
|
+
const res = await requestJson(ctx, `${url}/${encodeURIComponent(positionals[0])}/send`, { method: "POST", body: {} });
|
|
4288
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4289
|
+
else writeLine(ctx.io.stdout, `Sent campaign ${positionals[0]}.`);
|
|
4290
|
+
return 0;
|
|
4291
|
+
}
|
|
4292
|
+
case "sends": {
|
|
4293
|
+
if (positionals.length !== 1) {
|
|
4294
|
+
write(ctx.io.stderr, "Usage: openwop email campaigns sends <campaignId> --org <orgId>\n");
|
|
4295
|
+
return 2;
|
|
4296
|
+
}
|
|
4297
|
+
const res = await requestJson(ctx, `${url}/${encodeURIComponent(positionals[0])}/sends`);
|
|
4298
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4299
|
+
return 0;
|
|
4300
|
+
}
|
|
4301
|
+
default:
|
|
4302
|
+
throw new CliError(`Unknown email campaigns command: ${sub}`);
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
|
|
4306
|
+
// src/cli/chatWidget.ts
|
|
4307
|
+
var base4 = (org) => `/v1/host/sample/chat-widget/orgs/${encodeURIComponent(org)}/widgets`;
|
|
4308
|
+
var CHAT_WIDGET_HELP = `Usage:
|
|
4309
|
+
openwop chat-widget list --org <orgId> [--json]
|
|
4310
|
+
openwop chat-widget get <widgetId> --org <orgId> [--json]
|
|
4311
|
+
openwop chat-widget create --org <orgId> [--name <n>] [--json]
|
|
4312
|
+
openwop chat-widget update <widgetId> --org <orgId> [--name <n>] [--json]
|
|
4313
|
+
openwop chat-widget delete <widgetId> --org <orgId> [--yes]
|
|
4314
|
+
openwop chat-widget rotate-token <widgetId> --org <orgId> [--json]
|
|
4315
|
+
|
|
4316
|
+
Embeddable chat widgets (host-extension, org-scoped). Each widget carries an embed
|
|
4317
|
+
token; \`rotate-token\` invalidates the old one. Every command needs --org.
|
|
4318
|
+
`;
|
|
4319
|
+
async function runChatWidget(ctx, argv) {
|
|
4320
|
+
const sub = argv[0] ?? "list";
|
|
4321
|
+
if (sub === "--help" || sub === "-h") {
|
|
4322
|
+
write(ctx.io.stdout, CHAT_WIDGET_HELP);
|
|
4323
|
+
return 0;
|
|
4324
|
+
}
|
|
4325
|
+
const args = argv.slice(["list", "get", "create", "update", "delete", "rotate-token"].includes(sub) ? 1 : 0);
|
|
4326
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--name"] });
|
|
4327
|
+
if (options.help) {
|
|
4328
|
+
write(ctx.io.stdout, CHAT_WIDGET_HELP);
|
|
4329
|
+
return 0;
|
|
4330
|
+
}
|
|
4331
|
+
const org = requireOrg(options.org);
|
|
4332
|
+
switch (sub) {
|
|
4333
|
+
case "list": {
|
|
4334
|
+
const res = await requestJson(ctx, base4(org));
|
|
4335
|
+
if (ctx.json) {
|
|
4336
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4337
|
+
return 0;
|
|
4338
|
+
}
|
|
4339
|
+
const items = Array.isArray(res.body?.widgets) ? res.body.widgets : [];
|
|
4340
|
+
if (items.length === 0) {
|
|
4341
|
+
writeLine(ctx.io.stdout, "No widgets.");
|
|
4342
|
+
return 0;
|
|
4343
|
+
}
|
|
4344
|
+
writeLine(ctx.io.stdout, formatTable(items.map((w) => ({ id: w.id ?? w.widgetId ?? "", name: w.name ?? "", createdAt: w.createdAt ?? "" })), ["id", "name", "createdAt"]));
|
|
4345
|
+
return 0;
|
|
4346
|
+
}
|
|
4347
|
+
case "get": {
|
|
4348
|
+
if (positionals.length !== 1) {
|
|
4349
|
+
write(ctx.io.stderr, "Usage: openwop chat-widget get <widgetId> --org <orgId>\n");
|
|
4350
|
+
return 2;
|
|
4351
|
+
}
|
|
4352
|
+
const res = await requestJson(ctx, `${base4(org)}/${encodeURIComponent(positionals[0])}`);
|
|
4353
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4354
|
+
return 0;
|
|
4355
|
+
}
|
|
4356
|
+
case "create": {
|
|
4357
|
+
const body = {};
|
|
4358
|
+
if (options.name) body.name = String(options.name);
|
|
4359
|
+
const res = await requestJson(ctx, base4(org), { method: "POST", body });
|
|
4360
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4361
|
+
else writeLine(ctx.io.stdout, `Created widget ${res.body?.widget?.id ?? res.body?.id ?? ""}.`);
|
|
4362
|
+
return 0;
|
|
4363
|
+
}
|
|
4364
|
+
case "update": {
|
|
4365
|
+
if (positionals.length !== 1) {
|
|
4366
|
+
write(ctx.io.stderr, "Usage: openwop chat-widget update <widgetId> --org <orgId> [--name n]\n");
|
|
4367
|
+
return 2;
|
|
4368
|
+
}
|
|
4369
|
+
const patch = {};
|
|
4370
|
+
if (options.name) patch.name = String(options.name);
|
|
4371
|
+
const res = await requestJson(ctx, `${base4(org)}/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body: patch });
|
|
4372
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4373
|
+
else writeLine(ctx.io.stdout, `Updated widget ${positionals[0]}.`);
|
|
4374
|
+
return 0;
|
|
4375
|
+
}
|
|
4376
|
+
case "delete": {
|
|
4377
|
+
if (positionals.length !== 1) {
|
|
4378
|
+
write(ctx.io.stderr, "Usage: openwop chat-widget delete <widgetId> --org <orgId> [--yes]\n");
|
|
4379
|
+
return 2;
|
|
4380
|
+
}
|
|
4381
|
+
if (!options.yes) throw new CliError(`Refusing to delete widget ${positionals[0]} without --yes.`, 2);
|
|
4382
|
+
await requestJson(ctx, `${base4(org)}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
4383
|
+
writeLine(ctx.io.stdout, `Deleted widget ${positionals[0]}.`);
|
|
4384
|
+
return 0;
|
|
4385
|
+
}
|
|
4386
|
+
case "rotate-token": {
|
|
4387
|
+
if (positionals.length !== 1) {
|
|
4388
|
+
write(ctx.io.stderr, "Usage: openwop chat-widget rotate-token <widgetId> --org <orgId>\n");
|
|
4389
|
+
return 2;
|
|
4390
|
+
}
|
|
4391
|
+
const res = await requestJson(ctx, `${base4(org)}/${encodeURIComponent(positionals[0])}/rotate-token`, { method: "POST", body: {} });
|
|
4392
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4393
|
+
else writeLine(ctx.io.stdout, `Rotated token for widget ${positionals[0]}.`);
|
|
4394
|
+
return 0;
|
|
4395
|
+
}
|
|
4396
|
+
default:
|
|
4397
|
+
throw new CliError(`Unknown chat-widget command: ${sub}
|
|
4398
|
+
Run \`openwop chat-widget --help\` for usage.`);
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
|
|
4402
|
+
// src/cli/marketplace.ts
|
|
4403
|
+
var MP = "/v1/host/sample/marketplace";
|
|
4404
|
+
var reviews = (org, pack) => `${MP}/orgs/${encodeURIComponent(org)}/listings/${encodeURIComponent(pack)}/reviews`;
|
|
4405
|
+
var MARKETPLACE_HELP = `Usage:
|
|
4406
|
+
openwop marketplace listings [--json]
|
|
4407
|
+
openwop marketplace install --pack <name> --version <v> [--json]
|
|
4408
|
+
openwop marketplace reviews <packName> --org <orgId> [--json]
|
|
4409
|
+
openwop marketplace review <packName> --org <orgId> --rating <1-5> [--comment <t>] [--json]
|
|
4410
|
+
openwop marketplace unreview <packName> --org <orgId> [--yes]
|
|
4411
|
+
|
|
4412
|
+
The pack marketplace (host-extension). \`listings\`/\`install\` browse + install shared
|
|
4413
|
+
packs; \`reviews\`/\`review\`/\`unreview\` are org-scoped (need --org). Distinct from the
|
|
4414
|
+
signed node-pack \`packs\` registry. The host is the authority; the CLI mirrors + relays.
|
|
4415
|
+
`;
|
|
4416
|
+
async function runMarketplace(ctx, argv) {
|
|
4417
|
+
const sub = argv[0] ?? "listings";
|
|
4418
|
+
if (sub === "--help" || sub === "-h") {
|
|
4419
|
+
write(ctx.io.stdout, MARKETPLACE_HELP);
|
|
4420
|
+
return 0;
|
|
4421
|
+
}
|
|
4422
|
+
const args = argv.slice(["listings", "install", "reviews", "review", "unreview"].includes(sub) ? 1 : 0);
|
|
4423
|
+
switch (sub) {
|
|
4424
|
+
case "listings":
|
|
4425
|
+
return mpListings(ctx, args);
|
|
4426
|
+
case "install":
|
|
4427
|
+
return mpInstall(ctx, args);
|
|
4428
|
+
case "reviews":
|
|
4429
|
+
return mpReviews(ctx, args);
|
|
4430
|
+
case "review":
|
|
4431
|
+
return mpReview(ctx, args);
|
|
4432
|
+
case "unreview":
|
|
4433
|
+
return mpUnreview(ctx, args);
|
|
4434
|
+
default:
|
|
4435
|
+
throw new CliError(`Unknown marketplace command: ${sub}
|
|
4436
|
+
Run \`openwop marketplace --help\` for usage.`);
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
async function mpListings(ctx, argv) {
|
|
4440
|
+
const { options } = parseOptions(argv, { bool: ["--help"] });
|
|
4441
|
+
if (options.help) {
|
|
4442
|
+
write(ctx.io.stdout, MARKETPLACE_HELP);
|
|
4443
|
+
return 0;
|
|
4444
|
+
}
|
|
4445
|
+
const res = await requestJson(ctx, `${MP}/listings`);
|
|
4446
|
+
if (ctx.json) {
|
|
4447
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4448
|
+
return 0;
|
|
4449
|
+
}
|
|
4450
|
+
const items = Array.isArray(res.body?.listings) ? res.body.listings : [];
|
|
4451
|
+
if (items.length === 0) {
|
|
4452
|
+
writeLine(ctx.io.stdout, "No listings.");
|
|
4453
|
+
return 0;
|
|
4454
|
+
}
|
|
4455
|
+
writeLine(ctx.io.stdout, formatTable(
|
|
4456
|
+
items.map((l) => ({ pack: l.packName ?? l.name ?? "", version: l.version ?? l.latestVersion ?? "", rating: l.rating ?? "", installs: l.installs ?? "" })),
|
|
4457
|
+
["pack", "version", "rating", "installs"]
|
|
4458
|
+
));
|
|
4459
|
+
return 0;
|
|
4460
|
+
}
|
|
4461
|
+
async function mpInstall(ctx, argv) {
|
|
4462
|
+
const { options } = parseOptions(argv, { value: ["--pack", "--version"] });
|
|
4463
|
+
if (!options.pack || !options.version) {
|
|
4464
|
+
write(ctx.io.stderr, "Usage: openwop marketplace install --pack <name> --version <v> [--json]\n");
|
|
4465
|
+
return 2;
|
|
4466
|
+
}
|
|
4467
|
+
const res = await requestJson(ctx, `${MP}/install`, { method: "POST", body: { packName: String(options.pack), version: String(options.version) } });
|
|
4468
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4469
|
+
else writeLine(ctx.io.stdout, `Installed ${String(options.pack)}@${String(options.version)}.`);
|
|
4470
|
+
return 0;
|
|
4471
|
+
}
|
|
4472
|
+
async function mpReviews(ctx, argv) {
|
|
4473
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"], value: ["--org"] });
|
|
4474
|
+
const org = requireOrg(options.org);
|
|
4475
|
+
if (options.help || positionals.length !== 1) {
|
|
4476
|
+
write(ctx.io.stdout, "Usage: openwop marketplace reviews <packName> --org <orgId> [--json]\n");
|
|
4477
|
+
return options.help ? 0 : 2;
|
|
4478
|
+
}
|
|
4479
|
+
const res = await requestJson(ctx, reviews(org, positionals[0]));
|
|
4480
|
+
if (ctx.json) {
|
|
4481
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4482
|
+
return 0;
|
|
4483
|
+
}
|
|
4484
|
+
const items = Array.isArray(res.body?.reviews) ? res.body.reviews : [];
|
|
4485
|
+
if (items.length === 0) {
|
|
4486
|
+
writeLine(ctx.io.stdout, "No reviews.");
|
|
4487
|
+
return 0;
|
|
4488
|
+
}
|
|
4489
|
+
writeLine(ctx.io.stdout, formatTable(items.map((r) => ({ author: r.authorId ?? "", rating: r.rating ?? "", comment: (r.comment ?? "").slice(0, 60) })), ["author", "rating", "comment"]));
|
|
4490
|
+
return 0;
|
|
4491
|
+
}
|
|
4492
|
+
async function mpReview(ctx, argv) {
|
|
4493
|
+
const { options, positionals } = parseOptions(argv, { value: ["--org", "--rating", "--comment"] });
|
|
4494
|
+
const org = requireOrg(options.org);
|
|
4495
|
+
if (positionals.length !== 1 || options.rating === void 0) {
|
|
4496
|
+
write(ctx.io.stderr, "Usage: openwop marketplace review <packName> --org <orgId> --rating <1-5> [--comment <t>] [--json]\n");
|
|
4497
|
+
return 2;
|
|
4498
|
+
}
|
|
4499
|
+
const body = { rating: Number(options.rating) };
|
|
4500
|
+
if (options.comment) body.comment = String(options.comment);
|
|
4501
|
+
const res = await requestJson(ctx, reviews(org, positionals[0]), { method: "POST", body });
|
|
4502
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4503
|
+
else writeLine(ctx.io.stdout, `Reviewed ${positionals[0]} (${Number(options.rating)}\u2605).`);
|
|
4504
|
+
return 0;
|
|
4505
|
+
}
|
|
4506
|
+
async function mpUnreview(ctx, argv) {
|
|
4507
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--yes"], value: ["--org"] });
|
|
4508
|
+
const org = requireOrg(options.org);
|
|
4509
|
+
if (options.help || positionals.length !== 1) {
|
|
4510
|
+
write(ctx.io.stdout, "Usage: openwop marketplace unreview <packName> --org <orgId> [--yes]\n");
|
|
4511
|
+
return options.help ? 0 : 2;
|
|
4512
|
+
}
|
|
4513
|
+
if (!options.yes) throw new CliError(`Refusing to remove your review of ${positionals[0]} without --yes.`, 2);
|
|
4514
|
+
await requestJson(ctx, reviews(org, positionals[0]), { method: "DELETE" });
|
|
4515
|
+
writeLine(ctx.io.stdout, `Removed review of ${positionals[0]}.`);
|
|
4516
|
+
return 0;
|
|
4517
|
+
}
|
|
4518
|
+
|
|
4519
|
+
// src/cli/kb.ts
|
|
4520
|
+
var base5 = (org) => `/v1/host/sample/kb/orgs/${encodeURIComponent(org)}`;
|
|
4521
|
+
var cols = (org) => `${base5(org)}/collections`;
|
|
4522
|
+
var KB_HELP = `Usage:
|
|
4523
|
+
openwop kb collections list --org <orgId> [--json]
|
|
4524
|
+
openwop kb collections get <collectionId> --org <orgId> [--json]
|
|
4525
|
+
openwop kb collections create --org <orgId> --name <n> [--json]
|
|
4526
|
+
openwop kb collections delete <collectionId> --org <orgId> [--yes]
|
|
4527
|
+
openwop kb docs list <collectionId> --org <orgId> [--json]
|
|
4528
|
+
openwop kb docs get <collectionId> <documentId> --org <orgId> [--json]
|
|
4529
|
+
openwop kb docs add <collectionId> --org <orgId> --title <t> --text <text> [--json]
|
|
4530
|
+
openwop kb docs delete <collectionId> <documentId> --org <orgId> [--yes]
|
|
4531
|
+
openwop kb search <collectionId> --org <orgId> --query <q> [--top-k <n>] [--json]
|
|
4532
|
+
openwop kb rag <collectionId> --org <orgId> --query <q> [--top-k <n>] [--json]
|
|
4533
|
+
|
|
4534
|
+
Knowledge base (host-extension, org-scoped). Collections hold documents; \`search\` runs
|
|
4535
|
+
retrieval and \`rag\` a retrieve-then-generate query. Every command needs --org. The host
|
|
4536
|
+
is the authority; the CLI mirrors + relays.
|
|
4537
|
+
`;
|
|
4538
|
+
async function runKb(ctx, argv) {
|
|
4539
|
+
const group = argv[0] ?? "collections";
|
|
4540
|
+
if (group === "--help" || group === "-h") {
|
|
4541
|
+
write(ctx.io.stdout, KB_HELP);
|
|
4542
|
+
return 0;
|
|
4543
|
+
}
|
|
4544
|
+
const rest = argv.slice(1);
|
|
4545
|
+
switch (group) {
|
|
4546
|
+
case "collections":
|
|
4547
|
+
return kbCollections(ctx, rest);
|
|
4548
|
+
case "docs":
|
|
4549
|
+
return kbDocs(ctx, rest);
|
|
4550
|
+
case "search":
|
|
4551
|
+
return kbSearch(ctx, rest, "search");
|
|
4552
|
+
case "rag":
|
|
4553
|
+
return kbSearch(ctx, rest, "rag");
|
|
4554
|
+
default:
|
|
4555
|
+
throw new CliError(`Unknown kb command: ${group}. Use collections|docs|search|rag.`);
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4558
|
+
async function kbCollections(ctx, argv) {
|
|
4559
|
+
const sub = argv[0] ?? "list";
|
|
4560
|
+
const args = argv.slice(["list", "get", "create", "delete"].includes(sub) ? 1 : 0);
|
|
4561
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--name"] });
|
|
4562
|
+
if (options.help) {
|
|
4563
|
+
write(ctx.io.stdout, KB_HELP);
|
|
4564
|
+
return 0;
|
|
4565
|
+
}
|
|
4566
|
+
const org = requireOrg(options.org);
|
|
4567
|
+
switch (sub) {
|
|
4568
|
+
case "list": {
|
|
4569
|
+
const res = await requestJson(ctx, cols(org));
|
|
4570
|
+
if (ctx.json) {
|
|
4571
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4572
|
+
return 0;
|
|
4573
|
+
}
|
|
4574
|
+
const items = Array.isArray(res.body?.collections) ? res.body.collections : [];
|
|
4575
|
+
if (items.length === 0) {
|
|
4576
|
+
writeLine(ctx.io.stdout, "No collections.");
|
|
4577
|
+
return 0;
|
|
4578
|
+
}
|
|
4579
|
+
writeLine(ctx.io.stdout, formatTable(items.map((c) => ({ id: c.id ?? c.collectionId ?? "", name: c.name ?? "", docs: c.documentCount ?? "" })), ["id", "name", "docs"]));
|
|
4580
|
+
return 0;
|
|
4581
|
+
}
|
|
4582
|
+
case "get": {
|
|
4583
|
+
if (positionals.length !== 1) {
|
|
4584
|
+
write(ctx.io.stderr, "Usage: openwop kb collections get <collectionId> --org <orgId>\n");
|
|
4585
|
+
return 2;
|
|
4586
|
+
}
|
|
4587
|
+
const res = await requestJson(ctx, `${cols(org)}/${encodeURIComponent(positionals[0])}`);
|
|
4588
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4589
|
+
return 0;
|
|
4590
|
+
}
|
|
4591
|
+
case "create": {
|
|
4592
|
+
if (!options.name) {
|
|
4593
|
+
write(ctx.io.stderr, "kb collections create needs --name.\n");
|
|
4594
|
+
return 2;
|
|
4595
|
+
}
|
|
4596
|
+
const res = await requestJson(ctx, cols(org), { method: "POST", body: { name: String(options.name) } });
|
|
4597
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4598
|
+
else writeLine(ctx.io.stdout, `Created collection ${res.body?.id ?? res.body?.collectionId ?? ""} (${String(options.name)}).`);
|
|
4599
|
+
return 0;
|
|
4600
|
+
}
|
|
4601
|
+
case "delete": {
|
|
4602
|
+
if (positionals.length !== 1) {
|
|
4603
|
+
write(ctx.io.stderr, "Usage: openwop kb collections delete <collectionId> --org <orgId> [--yes]\n");
|
|
4604
|
+
return 2;
|
|
4605
|
+
}
|
|
4606
|
+
if (!options.yes) throw new CliError(`Refusing to delete collection ${positionals[0]} without --yes.`, 2);
|
|
4607
|
+
await requestJson(ctx, `${cols(org)}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
4608
|
+
writeLine(ctx.io.stdout, `Deleted collection ${positionals[0]}.`);
|
|
4609
|
+
return 0;
|
|
4610
|
+
}
|
|
4611
|
+
default:
|
|
4612
|
+
throw new CliError(`Unknown kb collections command: ${sub}`);
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
async function kbDocs(ctx, argv) {
|
|
4616
|
+
const sub = argv[0] ?? "list";
|
|
4617
|
+
const args = argv.slice(["list", "get", "add", "delete"].includes(sub) ? 1 : 0);
|
|
4618
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--title", "--text"] });
|
|
4619
|
+
if (options.help) {
|
|
4620
|
+
write(ctx.io.stdout, KB_HELP);
|
|
4621
|
+
return 0;
|
|
4622
|
+
}
|
|
4623
|
+
const org = requireOrg(options.org);
|
|
4624
|
+
const collectionId = positionals[0];
|
|
4625
|
+
if (!collectionId) {
|
|
4626
|
+
write(ctx.io.stderr, "kb docs commands need a <collectionId>.\n");
|
|
4627
|
+
return 2;
|
|
4628
|
+
}
|
|
4629
|
+
const docsUrl = `${cols(org)}/${encodeURIComponent(collectionId)}/documents`;
|
|
4630
|
+
switch (sub) {
|
|
4631
|
+
case "list": {
|
|
4632
|
+
const res = await requestJson(ctx, docsUrl);
|
|
4633
|
+
if (ctx.json) {
|
|
4634
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4635
|
+
return 0;
|
|
4636
|
+
}
|
|
4637
|
+
const items = Array.isArray(res.body?.documents) ? res.body.documents : [];
|
|
4638
|
+
if (items.length === 0) {
|
|
4639
|
+
writeLine(ctx.io.stdout, "No documents.");
|
|
4640
|
+
return 0;
|
|
4641
|
+
}
|
|
4642
|
+
writeLine(ctx.io.stdout, formatTable(items.map((d) => ({ id: d.id ?? d.documentId ?? "", title: d.title ?? "" })), ["id", "title"]));
|
|
4643
|
+
return 0;
|
|
4644
|
+
}
|
|
4645
|
+
case "get": {
|
|
4646
|
+
if (positionals.length !== 2) {
|
|
4647
|
+
write(ctx.io.stderr, "Usage: openwop kb docs get <collectionId> <documentId> --org <orgId>\n");
|
|
4648
|
+
return 2;
|
|
4649
|
+
}
|
|
4650
|
+
const res = await requestJson(ctx, `${docsUrl}/${encodeURIComponent(positionals[1])}`);
|
|
4651
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4652
|
+
return 0;
|
|
4653
|
+
}
|
|
4654
|
+
case "add": {
|
|
4655
|
+
if (!options.title || !options.text) {
|
|
4656
|
+
write(ctx.io.stderr, "kb docs add needs --title and --text.\n");
|
|
4657
|
+
return 2;
|
|
4658
|
+
}
|
|
4659
|
+
const res = await requestJson(ctx, docsUrl, { method: "POST", body: { title: String(options.title), text: String(options.text) } });
|
|
4660
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4661
|
+
else writeLine(ctx.io.stdout, `Added document ${res.body?.id ?? ""} to ${collectionId}.`);
|
|
4662
|
+
return 0;
|
|
4663
|
+
}
|
|
4664
|
+
case "delete": {
|
|
4665
|
+
if (positionals.length !== 2) {
|
|
4666
|
+
write(ctx.io.stderr, "Usage: openwop kb docs delete <collectionId> <documentId> --org <orgId> [--yes]\n");
|
|
4667
|
+
return 2;
|
|
4668
|
+
}
|
|
4669
|
+
if (!options.yes) throw new CliError(`Refusing to delete document ${positionals[1]} without --yes.`, 2);
|
|
4670
|
+
await requestJson(ctx, `${docsUrl}/${encodeURIComponent(positionals[1])}`, { method: "DELETE" });
|
|
4671
|
+
writeLine(ctx.io.stdout, `Deleted document ${positionals[1]}.`);
|
|
4672
|
+
return 0;
|
|
4673
|
+
}
|
|
4674
|
+
default:
|
|
4675
|
+
throw new CliError(`Unknown kb docs command: ${sub}`);
|
|
4676
|
+
}
|
|
4677
|
+
}
|
|
4678
|
+
async function kbSearch(ctx, argv, kind) {
|
|
4679
|
+
const { options, positionals } = parseOptions(argv, { value: ["--org", "--query", "--top-k"] });
|
|
4680
|
+
const org = requireOrg(options.org);
|
|
4681
|
+
if (positionals.length !== 1 || !options.query) {
|
|
4682
|
+
write(ctx.io.stderr, `Usage: openwop kb ${kind} <collectionId> --org <orgId> --query <q> [--top-k <n>] [--json]
|
|
4683
|
+
`);
|
|
4684
|
+
return 2;
|
|
4685
|
+
}
|
|
4686
|
+
const body = { query: String(options.query) };
|
|
4687
|
+
if (options.topK !== void 0) body.topK = Number(options.topK);
|
|
4688
|
+
const res = await requestJson(ctx, `${cols(org)}/${encodeURIComponent(positionals[0])}/${kind}`, { method: "POST", body });
|
|
4689
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4690
|
+
return 0;
|
|
4691
|
+
}
|
|
4692
|
+
|
|
4693
|
+
// src/cli/cms.ts
|
|
4694
|
+
var pages = (org) => `/v1/host/sample/cms/orgs/${encodeURIComponent(org)}/pages`;
|
|
4695
|
+
var LIFECYCLE = ["submit", "approve", "reject", "publish", "unpublish", "archive"];
|
|
4696
|
+
var CMS_HELP = `Usage:
|
|
4697
|
+
openwop cms pages list --org <orgId> [--json]
|
|
4698
|
+
openwop cms pages get <pageId> --org <orgId> [--json]
|
|
4699
|
+
openwop cms pages by-slug <slug> --org <orgId> [--json]
|
|
4700
|
+
openwop cms pages create --org <orgId> --title <t> [--slug <s>] [--json]
|
|
4701
|
+
openwop cms pages update <pageId> --org <orgId> [--title <t>] [--slug <s>] [--json]
|
|
4702
|
+
openwop cms pages delete <pageId> --org <orgId> [--yes]
|
|
4703
|
+
openwop cms pages versions <pageId> --org <orgId> [--json]
|
|
4704
|
+
openwop cms <submit|approve|reject|publish|unpublish|archive> <pageId> --org <orgId> [--json]
|
|
4705
|
+
|
|
4706
|
+
CMS pages + the authoring lifecycle (host-extension, org-scoped, ADR 0027). A page
|
|
4707
|
+
moves draft \u2192 submit \u2192 approve \u2192 publish; \`versions\` lists its history. Every command
|
|
4708
|
+
needs --org. The host is the authority; the CLI mirrors + relays.
|
|
4709
|
+
`;
|
|
4710
|
+
async function runCms(ctx, argv) {
|
|
4711
|
+
const sub = argv[0] ?? "pages";
|
|
4712
|
+
if (sub === "--help" || sub === "-h") {
|
|
4713
|
+
write(ctx.io.stdout, CMS_HELP);
|
|
4714
|
+
return 0;
|
|
4715
|
+
}
|
|
4716
|
+
if (sub === "pages") return cmsPages(ctx, argv.slice(1));
|
|
4717
|
+
if (LIFECYCLE.includes(sub)) return cmsLifecycle(ctx, sub, argv.slice(1));
|
|
4718
|
+
throw new CliError(`Unknown cms command: ${sub}
|
|
4719
|
+
Run \`openwop cms --help\` for usage.`);
|
|
4720
|
+
}
|
|
4721
|
+
async function cmsPages(ctx, argv) {
|
|
4722
|
+
const sub = argv[0] ?? "list";
|
|
4723
|
+
const args = argv.slice(["list", "get", "by-slug", "create", "update", "delete", "versions"].includes(sub) ? 1 : 0);
|
|
4724
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--title", "--slug"] });
|
|
4725
|
+
if (options.help) {
|
|
4726
|
+
write(ctx.io.stdout, CMS_HELP);
|
|
4727
|
+
return 0;
|
|
4728
|
+
}
|
|
4729
|
+
const org = requireOrg(options.org);
|
|
4730
|
+
const url = pages(org);
|
|
4731
|
+
switch (sub) {
|
|
4732
|
+
case "list": {
|
|
4733
|
+
const res = await requestJson(ctx, url);
|
|
4734
|
+
if (ctx.json) {
|
|
4735
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4736
|
+
return 0;
|
|
4737
|
+
}
|
|
4738
|
+
const items = Array.isArray(res.body?.pages) ? res.body.pages : [];
|
|
4739
|
+
if (items.length === 0) {
|
|
4740
|
+
writeLine(ctx.io.stdout, "No pages.");
|
|
4741
|
+
return 0;
|
|
4742
|
+
}
|
|
4743
|
+
writeLine(ctx.io.stdout, formatTable(items.map((p) => ({ id: p.id ?? p.pageId ?? "", title: p.title ?? "", slug: p.slug ?? "", status: p.status ?? "" })), ["id", "title", "slug", "status"]));
|
|
4744
|
+
return 0;
|
|
4745
|
+
}
|
|
4746
|
+
case "get": {
|
|
4747
|
+
if (positionals.length !== 1) {
|
|
4748
|
+
write(ctx.io.stderr, "Usage: openwop cms pages get <pageId> --org <orgId>\n");
|
|
4749
|
+
return 2;
|
|
4750
|
+
}
|
|
4751
|
+
const res = await requestJson(ctx, `${url}/${encodeURIComponent(positionals[0])}`);
|
|
4752
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4753
|
+
return 0;
|
|
4754
|
+
}
|
|
4755
|
+
case "by-slug": {
|
|
4756
|
+
if (positionals.length !== 1) {
|
|
4757
|
+
write(ctx.io.stderr, "Usage: openwop cms pages by-slug <slug> --org <orgId>\n");
|
|
4758
|
+
return 2;
|
|
4759
|
+
}
|
|
4760
|
+
const res = await requestJson(ctx, `${url}/by-slug/${encodeURIComponent(positionals[0])}`);
|
|
4761
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4762
|
+
return 0;
|
|
4763
|
+
}
|
|
4764
|
+
case "create": {
|
|
4765
|
+
if (!options.title) {
|
|
4766
|
+
write(ctx.io.stderr, "cms pages create needs --title.\n");
|
|
4767
|
+
return 2;
|
|
4768
|
+
}
|
|
4769
|
+
const body = { title: String(options.title) };
|
|
4770
|
+
if (options.slug) body.slug = String(options.slug);
|
|
4771
|
+
const res = await requestJson(ctx, url, { method: "POST", body });
|
|
4772
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4773
|
+
else writeLine(ctx.io.stdout, `Created page ${res.body?.id ?? ""} (${String(options.title)}).`);
|
|
4774
|
+
return 0;
|
|
4775
|
+
}
|
|
4776
|
+
case "update": {
|
|
4777
|
+
if (positionals.length !== 1) {
|
|
4778
|
+
write(ctx.io.stderr, "Usage: openwop cms pages update <pageId> --org <orgId> [--title t] [--slug s]\n");
|
|
4779
|
+
return 2;
|
|
4780
|
+
}
|
|
4781
|
+
const patch = {};
|
|
4782
|
+
if (options.title) patch.title = String(options.title);
|
|
4783
|
+
if (options.slug) patch.slug = String(options.slug);
|
|
4784
|
+
const res = await requestJson(ctx, `${url}/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body: patch });
|
|
4785
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4786
|
+
else writeLine(ctx.io.stdout, `Updated page ${positionals[0]}.`);
|
|
4787
|
+
return 0;
|
|
4788
|
+
}
|
|
4789
|
+
case "delete": {
|
|
4790
|
+
if (positionals.length !== 1) {
|
|
4791
|
+
write(ctx.io.stderr, "Usage: openwop cms pages delete <pageId> --org <orgId> [--yes]\n");
|
|
4792
|
+
return 2;
|
|
4793
|
+
}
|
|
4794
|
+
if (!options.yes) throw new CliError(`Refusing to delete page ${positionals[0]} without --yes.`, 2);
|
|
4795
|
+
await requestJson(ctx, `${url}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
4796
|
+
writeLine(ctx.io.stdout, `Deleted page ${positionals[0]}.`);
|
|
4797
|
+
return 0;
|
|
4798
|
+
}
|
|
4799
|
+
case "versions": {
|
|
4800
|
+
if (positionals.length !== 1) {
|
|
4801
|
+
write(ctx.io.stderr, "Usage: openwop cms pages versions <pageId> --org <orgId>\n");
|
|
4802
|
+
return 2;
|
|
4803
|
+
}
|
|
4804
|
+
const res = await requestJson(ctx, `${url}/${encodeURIComponent(positionals[0])}/versions`);
|
|
4805
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4806
|
+
return 0;
|
|
4807
|
+
}
|
|
4808
|
+
default:
|
|
4809
|
+
throw new CliError(`Unknown cms pages command: ${sub}`);
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
async function cmsLifecycle(ctx, action, argv) {
|
|
4813
|
+
const { options, positionals } = parseOptions(argv, { value: ["--org"] });
|
|
4814
|
+
const org = requireOrg(options.org);
|
|
4815
|
+
if (positionals.length !== 1) {
|
|
4816
|
+
write(ctx.io.stderr, `Usage: openwop cms ${action} <pageId> --org <orgId> [--json]
|
|
4817
|
+
`);
|
|
4818
|
+
return 2;
|
|
4819
|
+
}
|
|
4820
|
+
const res = await requestJson(ctx, `${pages(org)}/${encodeURIComponent(positionals[0])}/${action}`, { method: "POST", body: {} });
|
|
4821
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4822
|
+
else writeLine(ctx.io.stdout, `Page ${positionals[0]} \u2192 ${action}.`);
|
|
4823
|
+
return 0;
|
|
4824
|
+
}
|
|
4825
|
+
|
|
4826
|
+
// src/cli/documents.ts
|
|
4827
|
+
var base6 = (org) => `/v1/host/sample/documents/orgs/${encodeURIComponent(org)}`;
|
|
4828
|
+
var DOCUMENTS_HELP = `Usage:
|
|
4829
|
+
openwop documents list --org <orgId> [--json]
|
|
4830
|
+
openwop documents get <documentId> --org <orgId> [--json]
|
|
4831
|
+
openwop documents create --org <orgId> --title <t> --kind <k> [--format <f>] [--json]
|
|
4832
|
+
openwop documents update <documentId> --org <orgId> [--title <t>] [--json]
|
|
4833
|
+
openwop documents delete <documentId> --org <orgId> [--yes]
|
|
4834
|
+
openwop documents versions <documentId> --org <orgId> [--json]
|
|
4835
|
+
openwop documents render <documentId> --org <orgId> [--json]
|
|
4836
|
+
openwop documents templates list --org <orgId> [--json]
|
|
4837
|
+
openwop documents templates get <templateId> --org <orgId> [--json]
|
|
4838
|
+
openwop documents templates create --org <orgId> --name <n> --kind <k> [--output-format <f>] [--json]
|
|
4839
|
+
openwop documents templates delete <templateId> --org <orgId> [--yes]
|
|
4840
|
+
|
|
4841
|
+
Document generation + templates (host-extension, org-scoped). A document has a
|
|
4842
|
+
title/kind/format and versioned content; \`render\` produces its output; templates
|
|
4843
|
+
drive assembly. Every command needs --org. The host is the authority; the CLI relays.
|
|
4844
|
+
`;
|
|
4845
|
+
async function runDocuments(ctx, argv) {
|
|
4846
|
+
const sub = argv[0] ?? "list";
|
|
4847
|
+
if (sub === "--help" || sub === "-h") {
|
|
4848
|
+
write(ctx.io.stdout, DOCUMENTS_HELP);
|
|
4849
|
+
return 0;
|
|
4850
|
+
}
|
|
4851
|
+
if (sub === "templates") return docTemplates(ctx, argv.slice(1));
|
|
4852
|
+
const args = argv.slice(["list", "get", "create", "update", "delete", "versions", "render"].includes(sub) ? 1 : 0);
|
|
4853
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--title", "--kind", "--format"] });
|
|
4854
|
+
if (options.help) {
|
|
4855
|
+
write(ctx.io.stdout, DOCUMENTS_HELP);
|
|
4856
|
+
return 0;
|
|
4857
|
+
}
|
|
4858
|
+
const org = requireOrg(options.org);
|
|
4859
|
+
const docs = `${base6(org)}/documents`;
|
|
4860
|
+
const id = positionals[0];
|
|
4861
|
+
switch (sub) {
|
|
4862
|
+
case "list": {
|
|
4863
|
+
const res = await requestJson(ctx, docs);
|
|
4864
|
+
if (ctx.json) {
|
|
4865
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4866
|
+
return 0;
|
|
4867
|
+
}
|
|
4868
|
+
const items = Array.isArray(res.body?.documents) ? res.body.documents : [];
|
|
4869
|
+
if (items.length === 0) {
|
|
4870
|
+
writeLine(ctx.io.stdout, "No documents.");
|
|
4871
|
+
return 0;
|
|
4872
|
+
}
|
|
4873
|
+
writeLine(ctx.io.stdout, formatTable(items.map((d) => ({ id: d.id ?? "", title: d.title ?? "", kind: d.kind ?? "", format: d.format ?? "" })), ["id", "title", "kind", "format"]));
|
|
4874
|
+
return 0;
|
|
4875
|
+
}
|
|
4876
|
+
case "get": {
|
|
4877
|
+
if (!id) {
|
|
4878
|
+
write(ctx.io.stderr, "Usage: openwop documents get <documentId> --org <orgId>\n");
|
|
4879
|
+
return 2;
|
|
4880
|
+
}
|
|
4881
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${docs}/${encodeURIComponent(id)}`)).body);
|
|
4882
|
+
return 0;
|
|
4883
|
+
}
|
|
4884
|
+
case "create": {
|
|
4885
|
+
if (!options.title || !options.kind) {
|
|
4886
|
+
write(ctx.io.stderr, "documents create needs --title and --kind.\n");
|
|
4887
|
+
return 2;
|
|
4888
|
+
}
|
|
4889
|
+
const body = { title: String(options.title), kind: String(options.kind) };
|
|
4890
|
+
if (options.format) body.format = String(options.format);
|
|
4891
|
+
const res = await requestJson(ctx, docs, { method: "POST", body });
|
|
4892
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4893
|
+
else writeLine(ctx.io.stdout, `Created document ${res.body?.id ?? ""} (${String(options.title)}).`);
|
|
4894
|
+
return 0;
|
|
4895
|
+
}
|
|
4896
|
+
case "update": {
|
|
4897
|
+
if (!id) {
|
|
4898
|
+
write(ctx.io.stderr, "Usage: openwop documents update <documentId> --org <orgId> [--title t]\n");
|
|
4899
|
+
return 2;
|
|
4900
|
+
}
|
|
4901
|
+
const patch = {};
|
|
4902
|
+
if (options.title) patch.title = String(options.title);
|
|
4903
|
+
const res = await requestJson(ctx, `${docs}/${encodeURIComponent(id)}`, { method: "PATCH", body: patch });
|
|
4904
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4905
|
+
else writeLine(ctx.io.stdout, `Updated document ${id}.`);
|
|
4906
|
+
return 0;
|
|
4907
|
+
}
|
|
4908
|
+
case "delete": {
|
|
4909
|
+
if (!id) {
|
|
4910
|
+
write(ctx.io.stderr, "Usage: openwop documents delete <documentId> --org <orgId> [--yes]\n");
|
|
4911
|
+
return 2;
|
|
4912
|
+
}
|
|
4913
|
+
if (!options.yes) throw new CliError(`Refusing to delete document ${id} without --yes.`, 2);
|
|
4914
|
+
await requestJson(ctx, `${docs}/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
4915
|
+
writeLine(ctx.io.stdout, `Deleted document ${id}.`);
|
|
4916
|
+
return 0;
|
|
4917
|
+
}
|
|
4918
|
+
case "versions": {
|
|
4919
|
+
if (!id) {
|
|
4920
|
+
write(ctx.io.stderr, "Usage: openwop documents versions <documentId> --org <orgId>\n");
|
|
4921
|
+
return 2;
|
|
4922
|
+
}
|
|
4923
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${docs}/${encodeURIComponent(id)}/versions`)).body);
|
|
4924
|
+
return 0;
|
|
4925
|
+
}
|
|
4926
|
+
case "render": {
|
|
4927
|
+
if (!id) {
|
|
4928
|
+
write(ctx.io.stderr, "Usage: openwop documents render <documentId> --org <orgId>\n");
|
|
4929
|
+
return 2;
|
|
4930
|
+
}
|
|
4931
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${docs}/${encodeURIComponent(id)}/render`, { method: "POST", body: {} })).body);
|
|
4932
|
+
return 0;
|
|
4933
|
+
}
|
|
4934
|
+
default:
|
|
4935
|
+
throw new CliError(`Unknown documents command: ${sub}`);
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
async function docTemplates(ctx, argv) {
|
|
4939
|
+
const sub = argv[0] ?? "list";
|
|
4940
|
+
const args = argv.slice(["list", "get", "create", "delete"].includes(sub) ? 1 : 0);
|
|
4941
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--name", "--kind", "--output-format"] });
|
|
4942
|
+
if (options.help) {
|
|
4943
|
+
write(ctx.io.stdout, DOCUMENTS_HELP);
|
|
4944
|
+
return 0;
|
|
4945
|
+
}
|
|
4946
|
+
const org = requireOrg(options.org);
|
|
4947
|
+
const url = `${base6(org)}/templates`;
|
|
4948
|
+
const id = positionals[0];
|
|
4949
|
+
switch (sub) {
|
|
4950
|
+
case "list": {
|
|
4951
|
+
const res = await requestJson(ctx, url);
|
|
4952
|
+
if (ctx.json) {
|
|
4953
|
+
writeJson(ctx.io.stdout, res.body);
|
|
4954
|
+
return 0;
|
|
4955
|
+
}
|
|
4956
|
+
const items = Array.isArray(res.body?.templates) ? res.body.templates : [];
|
|
4957
|
+
if (items.length === 0) {
|
|
4958
|
+
writeLine(ctx.io.stdout, "No templates.");
|
|
4959
|
+
return 0;
|
|
4960
|
+
}
|
|
4961
|
+
writeLine(ctx.io.stdout, formatTable(items.map((t) => ({ id: t.id ?? "", name: t.name ?? "", kind: t.kind ?? "" })), ["id", "name", "kind"]));
|
|
4962
|
+
return 0;
|
|
4963
|
+
}
|
|
4964
|
+
case "get": {
|
|
4965
|
+
if (!id) {
|
|
4966
|
+
write(ctx.io.stderr, "Usage: openwop documents templates get <templateId> --org <orgId>\n");
|
|
4967
|
+
return 2;
|
|
4968
|
+
}
|
|
4969
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${url}/${encodeURIComponent(id)}`)).body);
|
|
4970
|
+
return 0;
|
|
4971
|
+
}
|
|
4972
|
+
case "create": {
|
|
4973
|
+
if (!options.name || !options.kind) {
|
|
4974
|
+
write(ctx.io.stderr, "documents templates create needs --name and --kind.\n");
|
|
4975
|
+
return 2;
|
|
4976
|
+
}
|
|
4977
|
+
const body = { name: String(options.name), kind: String(options.kind) };
|
|
4978
|
+
if (options.outputFormat) body.outputFormat = String(options.outputFormat);
|
|
4979
|
+
const res = await requestJson(ctx, url, { method: "POST", body });
|
|
4980
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
4981
|
+
else writeLine(ctx.io.stdout, `Created template ${res.body?.id ?? ""} (${String(options.name)}).`);
|
|
4982
|
+
return 0;
|
|
4983
|
+
}
|
|
4984
|
+
case "delete": {
|
|
4985
|
+
if (!id) {
|
|
4986
|
+
write(ctx.io.stderr, "Usage: openwop documents templates delete <templateId> --org <orgId> [--yes]\n");
|
|
4987
|
+
return 2;
|
|
4988
|
+
}
|
|
4989
|
+
if (!options.yes) throw new CliError(`Refusing to delete template ${id} without --yes.`, 2);
|
|
4990
|
+
await requestJson(ctx, `${url}/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
4991
|
+
writeLine(ctx.io.stdout, `Deleted template ${id}.`);
|
|
4992
|
+
return 0;
|
|
4993
|
+
}
|
|
4994
|
+
default:
|
|
4995
|
+
throw new CliError(`Unknown documents templates command: ${sub}`);
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
4998
|
+
|
|
4999
|
+
// src/cli/projects.ts
|
|
5000
|
+
var BASE3 = "/v1/host/sample/projects";
|
|
5001
|
+
var PROJECTS_HELP = `Usage:
|
|
5002
|
+
openwop projects list [--json]
|
|
5003
|
+
openwop projects get <projectId> [--json]
|
|
5004
|
+
openwop projects create --org <orgId> --name <n> [--json]
|
|
5005
|
+
openwop projects update <projectId> [--name <n>] [--json]
|
|
5006
|
+
openwop projects delete <projectId> [--yes]
|
|
5007
|
+
openwop projects members list <projectId> [--json]
|
|
5008
|
+
openwop projects members add <projectId> --ref <subjectRef> [--json]
|
|
5009
|
+
openwop projects members remove <projectId> <ref> [--yes]
|
|
5010
|
+
|
|
5011
|
+
Project workspaces (host-extension). A project scopes members + knowledge + schedules;
|
|
5012
|
+
\`create\` needs the owning --org. The host is the authority; the CLI mirrors + relays.
|
|
5013
|
+
`;
|
|
5014
|
+
async function runProjects(ctx, argv) {
|
|
5015
|
+
const sub = argv[0] ?? "list";
|
|
5016
|
+
if (sub === "--help" || sub === "-h") {
|
|
5017
|
+
write(ctx.io.stdout, PROJECTS_HELP);
|
|
5018
|
+
return 0;
|
|
5019
|
+
}
|
|
5020
|
+
if (sub === "members") return projectMembers(ctx, argv.slice(1));
|
|
5021
|
+
const args = argv.slice(["list", "get", "create", "update", "delete"].includes(sub) ? 1 : 0);
|
|
5022
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--name"] });
|
|
5023
|
+
if (options.help) {
|
|
5024
|
+
write(ctx.io.stdout, PROJECTS_HELP);
|
|
5025
|
+
return 0;
|
|
5026
|
+
}
|
|
5027
|
+
const id = positionals[0];
|
|
5028
|
+
switch (sub) {
|
|
5029
|
+
case "list": {
|
|
5030
|
+
const res = await requestJson(ctx, BASE3);
|
|
5031
|
+
if (ctx.json) {
|
|
5032
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5033
|
+
return 0;
|
|
5034
|
+
}
|
|
5035
|
+
const items = Array.isArray(res.body?.projects) ? res.body.projects : [];
|
|
5036
|
+
if (items.length === 0) {
|
|
5037
|
+
writeLine(ctx.io.stdout, "No projects.");
|
|
5038
|
+
return 0;
|
|
5039
|
+
}
|
|
5040
|
+
writeLine(ctx.io.stdout, formatTable(items.map((p) => ({ id: p.id ?? "", name: p.name ?? "", org: p.orgId ?? "" })), ["id", "name", "org"]));
|
|
5041
|
+
return 0;
|
|
5042
|
+
}
|
|
5043
|
+
case "get": {
|
|
5044
|
+
if (!id) {
|
|
5045
|
+
write(ctx.io.stderr, "Usage: openwop projects get <projectId>\n");
|
|
5046
|
+
return 2;
|
|
5047
|
+
}
|
|
5048
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE3}/${encodeURIComponent(id)}`)).body);
|
|
5049
|
+
return 0;
|
|
5050
|
+
}
|
|
5051
|
+
case "create": {
|
|
5052
|
+
if (!options.org || !options.name) {
|
|
5053
|
+
write(ctx.io.stderr, "projects create needs --org and --name.\n");
|
|
5054
|
+
return 2;
|
|
5055
|
+
}
|
|
5056
|
+
const res = await requestJson(ctx, BASE3, { method: "POST", body: { orgId: String(options.org), name: String(options.name) } });
|
|
5057
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5058
|
+
else writeLine(ctx.io.stdout, `Created project ${res.body?.id ?? ""} (${String(options.name)}).`);
|
|
5059
|
+
return 0;
|
|
5060
|
+
}
|
|
5061
|
+
case "update": {
|
|
5062
|
+
if (!id) {
|
|
5063
|
+
write(ctx.io.stderr, "Usage: openwop projects update <projectId> [--name n]\n");
|
|
5064
|
+
return 2;
|
|
5065
|
+
}
|
|
5066
|
+
const patch = {};
|
|
5067
|
+
if (options.name) patch.name = String(options.name);
|
|
5068
|
+
const res = await requestJson(ctx, `${BASE3}/${encodeURIComponent(id)}`, { method: "PATCH", body: patch });
|
|
5069
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5070
|
+
else writeLine(ctx.io.stdout, `Updated project ${id}.`);
|
|
5071
|
+
return 0;
|
|
5072
|
+
}
|
|
5073
|
+
case "delete": {
|
|
5074
|
+
if (!id) {
|
|
5075
|
+
write(ctx.io.stderr, "Usage: openwop projects delete <projectId> [--yes]\n");
|
|
5076
|
+
return 2;
|
|
5077
|
+
}
|
|
5078
|
+
if (!options.yes) throw new CliError(`Refusing to delete project ${id} without --yes.`, 2);
|
|
5079
|
+
await requestJson(ctx, `${BASE3}/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
5080
|
+
writeLine(ctx.io.stdout, `Deleted project ${id}.`);
|
|
5081
|
+
return 0;
|
|
5082
|
+
}
|
|
5083
|
+
default:
|
|
5084
|
+
throw new CliError(`Unknown projects command: ${sub}`);
|
|
5085
|
+
}
|
|
5086
|
+
}
|
|
5087
|
+
async function projectMembers(ctx, argv) {
|
|
5088
|
+
const sub = argv[0] ?? "list";
|
|
5089
|
+
const args = argv.slice(["list", "add", "remove"].includes(sub) ? 1 : 0);
|
|
5090
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--ref"] });
|
|
5091
|
+
if (options.help) {
|
|
5092
|
+
write(ctx.io.stdout, PROJECTS_HELP);
|
|
5093
|
+
return 0;
|
|
5094
|
+
}
|
|
5095
|
+
const projectId = positionals[0];
|
|
5096
|
+
if (!projectId) {
|
|
5097
|
+
write(ctx.io.stderr, "projects members commands need a <projectId>.\n");
|
|
5098
|
+
return 2;
|
|
5099
|
+
}
|
|
5100
|
+
const url = `${BASE3}/${encodeURIComponent(projectId)}/members`;
|
|
5101
|
+
switch (sub) {
|
|
5102
|
+
case "list": {
|
|
5103
|
+
const res = await requestJson(ctx, url);
|
|
5104
|
+
if (ctx.json) {
|
|
5105
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5106
|
+
return 0;
|
|
5107
|
+
}
|
|
5108
|
+
const items = Array.isArray(res.body?.members) ? res.body.members : [];
|
|
5109
|
+
writeLine(ctx.io.stdout, items.length ? formatTable(items.map((m) => ({ ref: m.ref ?? m.subject ?? "", role: m.role ?? "" })), ["ref", "role"]) : "No members.");
|
|
5110
|
+
return 0;
|
|
5111
|
+
}
|
|
5112
|
+
case "add": {
|
|
5113
|
+
if (!options.ref) {
|
|
5114
|
+
write(ctx.io.stderr, "projects members add needs --ref <subjectRef>.\n");
|
|
5115
|
+
return 2;
|
|
5116
|
+
}
|
|
5117
|
+
const res = await requestJson(ctx, url, { method: "POST", body: { ref: String(options.ref) } });
|
|
5118
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5119
|
+
else writeLine(ctx.io.stdout, `Added member ${String(options.ref)} to ${projectId}.`);
|
|
5120
|
+
return 0;
|
|
5121
|
+
}
|
|
5122
|
+
case "remove": {
|
|
5123
|
+
if (positionals.length !== 2) {
|
|
5124
|
+
write(ctx.io.stderr, "Usage: openwop projects members remove <projectId> <ref> [--yes]\n");
|
|
5125
|
+
return 2;
|
|
5126
|
+
}
|
|
5127
|
+
if (!options.yes) throw new CliError(`Refusing to remove member ${positionals[1]} without --yes.`, 2);
|
|
5128
|
+
await requestJson(ctx, `${url}/${encodeURIComponent(positionals[1])}`, { method: "DELETE" });
|
|
5129
|
+
writeLine(ctx.io.stdout, `Removed member ${positionals[1]}.`);
|
|
5130
|
+
return 0;
|
|
5131
|
+
}
|
|
5132
|
+
default:
|
|
5133
|
+
throw new CliError(`Unknown projects members command: ${sub}`);
|
|
5134
|
+
}
|
|
5135
|
+
}
|
|
5136
|
+
|
|
5137
|
+
// src/cli/reviews.ts
|
|
5138
|
+
var BASE4 = "/v1/host/sample/reviews";
|
|
5139
|
+
var REVIEWS_HELP = `Usage:
|
|
5140
|
+
openwop reviews list [--status <s>] [--json]
|
|
5141
|
+
openwop reviews get <reviewId> [--json]
|
|
5142
|
+
openwop reviews action <reviewId> <action> [--note <t>] [--json]
|
|
5143
|
+
|
|
5144
|
+
The unified review inbox (ADR 0068 / RFC 0070). Reviewers act on a review via a named
|
|
5145
|
+
<action> (e.g. approve | reject | comment) that the host defines + quorums. The host is
|
|
5146
|
+
the authority; the CLI renders its resolved view and relays the action. Exit: 0 resolved
|
|
5147
|
+
\xB7 3 pending \xB7 1 rejected/error.`;
|
|
5148
|
+
async function runReviews(ctx, argv) {
|
|
5149
|
+
const sub = argv[0] ?? "list";
|
|
5150
|
+
if (sub === "--help" || sub === "-h") {
|
|
5151
|
+
write(ctx.io.stdout, REVIEWS_HELP);
|
|
5152
|
+
return 0;
|
|
5153
|
+
}
|
|
5154
|
+
const args = argv.slice(["list", "get", "action"].includes(sub) ? 1 : 0);
|
|
5155
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help"], value: ["--status", "--note"] });
|
|
5156
|
+
if (options.help) {
|
|
5157
|
+
write(ctx.io.stdout, REVIEWS_HELP);
|
|
5158
|
+
return 0;
|
|
5159
|
+
}
|
|
5160
|
+
switch (sub) {
|
|
5161
|
+
case "list": {
|
|
5162
|
+
const q = options.status ? `?status=${encodeURIComponent(String(options.status))}` : "";
|
|
5163
|
+
const res = await requestJson(ctx, `${BASE4}${q}`);
|
|
5164
|
+
if (ctx.json) {
|
|
5165
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5166
|
+
return 0;
|
|
5167
|
+
}
|
|
5168
|
+
const items = Array.isArray(res.body?.reviews) ? res.body.reviews : [];
|
|
5169
|
+
if (items.length === 0) {
|
|
5170
|
+
writeLine(ctx.io.stdout, "No reviews.");
|
|
5171
|
+
return 0;
|
|
5172
|
+
}
|
|
5173
|
+
writeLine(ctx.io.stdout, formatTable(items.map((r) => ({ id: r.reviewId ?? r.id ?? "", status: r.status ?? "", kind: r.kind ?? "", title: (r.title ?? "").slice(0, 50) })), ["id", "status", "kind", "title"]));
|
|
5174
|
+
return 0;
|
|
5175
|
+
}
|
|
5176
|
+
case "get": {
|
|
5177
|
+
if (positionals.length !== 1) {
|
|
5178
|
+
write(ctx.io.stderr, "Usage: openwop reviews get <reviewId>\n");
|
|
5179
|
+
return 2;
|
|
5180
|
+
}
|
|
5181
|
+
const res = await requestJson(ctx, `${BASE4}/${encodeURIComponent(positionals[0])}`);
|
|
5182
|
+
if (ctx.json) {
|
|
5183
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5184
|
+
return 0;
|
|
5185
|
+
}
|
|
5186
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5187
|
+
const st = res.body?.status;
|
|
5188
|
+
return st === "approved" || st === "resolved" ? 0 : st === "rejected" ? 1 : 3;
|
|
5189
|
+
}
|
|
5190
|
+
case "action": {
|
|
5191
|
+
if (positionals.length !== 2) {
|
|
5192
|
+
write(ctx.io.stderr, "Usage: openwop reviews action <reviewId> <action> [--note <t>] [--json]\n");
|
|
5193
|
+
return 2;
|
|
5194
|
+
}
|
|
5195
|
+
const body = options.note ? { note: String(options.note) } : {};
|
|
5196
|
+
const res = await requestJson(ctx, `${BASE4}/${encodeURIComponent(positionals[0])}/actions/${encodeURIComponent(positionals[1])}`, { method: "POST", body });
|
|
5197
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5198
|
+
else writeLine(ctx.io.stdout, `Applied action '${positionals[1]}' to review ${positionals[0]}.`);
|
|
5199
|
+
return 0;
|
|
5200
|
+
}
|
|
5201
|
+
default:
|
|
5202
|
+
throw new CliError(`Unknown reviews command: ${sub}
|
|
5203
|
+
Run \`openwop reviews --help\` for usage.`);
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
5206
|
+
|
|
5207
|
+
// src/cli/workspacesTenancy.ts
|
|
5208
|
+
var WORKSPACES_HELP = `Usage:
|
|
5209
|
+
openwop workspaces list [--json]
|
|
5210
|
+
openwop workspaces create --name <n> [--json]
|
|
5211
|
+
openwop workspaces switch <workspaceId> [--json]
|
|
5212
|
+
|
|
5213
|
+
Your B2B workspaces (each a tenant, ADR 0015). \`switch\` changes the active workspace.
|
|
5214
|
+
Distinct from \`workspace\` (singular) \u2014 the per-tenant agent FILE store.`;
|
|
5215
|
+
async function runWorkspaces(ctx, argv) {
|
|
5216
|
+
const sub = argv[0] ?? "list";
|
|
5217
|
+
if (sub === "--help" || sub === "-h") {
|
|
5218
|
+
write(ctx.io.stdout, WORKSPACES_HELP);
|
|
5219
|
+
return 0;
|
|
5220
|
+
}
|
|
5221
|
+
const args = argv.slice(["list", "create", "switch"].includes(sub) ? 1 : 0);
|
|
5222
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help"], value: ["--name"] });
|
|
5223
|
+
if (options.help) {
|
|
5224
|
+
write(ctx.io.stdout, WORKSPACES_HELP);
|
|
5225
|
+
return 0;
|
|
5226
|
+
}
|
|
5227
|
+
switch (sub) {
|
|
5228
|
+
case "list": {
|
|
5229
|
+
const res = await requestJson(ctx, "/v1/host/sample/me/workspaces");
|
|
5230
|
+
if (ctx.json) {
|
|
5231
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5232
|
+
return 0;
|
|
5233
|
+
}
|
|
5234
|
+
const items = Array.isArray(res.body?.workspaces) ? res.body.workspaces : [];
|
|
5235
|
+
if (items.length === 0) {
|
|
5236
|
+
writeLine(ctx.io.stdout, "No workspaces.");
|
|
5237
|
+
return 0;
|
|
5238
|
+
}
|
|
5239
|
+
writeLine(ctx.io.stdout, formatTable(items.map((w) => ({ id: w.id ?? w.workspaceId ?? "", name: w.name ?? "", active: w.active ? "yes" : "" })), ["id", "name", "active"]));
|
|
5240
|
+
return 0;
|
|
5241
|
+
}
|
|
5242
|
+
case "create": {
|
|
5243
|
+
if (!options.name) {
|
|
5244
|
+
write(ctx.io.stderr, "Usage: openwop workspaces create --name <n> [--json]\n");
|
|
5245
|
+
return 2;
|
|
5246
|
+
}
|
|
5247
|
+
const res = await requestJson(ctx, "/v1/host/sample/workspaces", { method: "POST", body: { name: String(options.name) } });
|
|
5248
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5249
|
+
else writeLine(ctx.io.stdout, `Created workspace ${res.body?.id ?? ""} (${String(options.name)}).`);
|
|
5250
|
+
return 0;
|
|
5251
|
+
}
|
|
5252
|
+
case "switch": {
|
|
5253
|
+
if (positionals.length !== 1) {
|
|
5254
|
+
write(ctx.io.stderr, "Usage: openwop workspaces switch <workspaceId> [--json]\n");
|
|
5255
|
+
return 2;
|
|
5256
|
+
}
|
|
5257
|
+
const res = await requestJson(ctx, `/v1/host/sample/workspaces/${encodeURIComponent(positionals[0])}/switch`, { method: "POST", body: {} });
|
|
5258
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5259
|
+
else writeLine(ctx.io.stdout, `Switched to workspace ${positionals[0]}.`);
|
|
5260
|
+
return 0;
|
|
5261
|
+
}
|
|
5262
|
+
default:
|
|
5263
|
+
throw new CliError(`Unknown workspaces command: ${sub}
|
|
5264
|
+
Run \`openwop workspaces --help\` for usage.`);
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
|
|
5268
|
+
// src/cli/agentProfile.ts
|
|
5269
|
+
var prof = (id) => `/v1/host/sample/agents/${encodeURIComponent(id)}/profile`;
|
|
5270
|
+
var AGENT_PROFILE_HELP = `Usage:
|
|
5271
|
+
openwop agent-profile get <agentId> [--json]
|
|
5272
|
+
openwop agent-profile set <agentId> --profile-json '{...}' [--json]
|
|
5273
|
+
openwop agent-profile readiness <agentId> [--json]
|
|
5274
|
+
|
|
5275
|
+
An agent's rich profile + its connector readiness (ADR 0031). \`set\` PUTs the full
|
|
5276
|
+
profile object; \`readiness\` reports whether the connectors the agent needs are wired.`;
|
|
5277
|
+
async function runAgentProfile(ctx, argv) {
|
|
5278
|
+
const sub = argv[0] ?? "get";
|
|
5279
|
+
if (sub === "--help" || sub === "-h") {
|
|
5280
|
+
write(ctx.io.stdout, AGENT_PROFILE_HELP);
|
|
5281
|
+
return 0;
|
|
5282
|
+
}
|
|
5283
|
+
const args = argv.slice(["get", "set", "readiness"].includes(sub) ? 1 : 0);
|
|
5284
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help"], value: ["--profile-json"] });
|
|
5285
|
+
if (options.help) {
|
|
5286
|
+
write(ctx.io.stdout, AGENT_PROFILE_HELP);
|
|
5287
|
+
return 0;
|
|
5288
|
+
}
|
|
5289
|
+
if (positionals.length !== 1) {
|
|
5290
|
+
write(ctx.io.stderr, `Usage: openwop agent-profile ${sub} <agentId>
|
|
5291
|
+
`);
|
|
5292
|
+
return 2;
|
|
5293
|
+
}
|
|
5294
|
+
const agentId = positionals[0];
|
|
5295
|
+
switch (sub) {
|
|
5296
|
+
case "get":
|
|
5297
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, prof(agentId))).body);
|
|
5298
|
+
return 0;
|
|
5299
|
+
case "readiness":
|
|
5300
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `/v1/host/sample/agents/${encodeURIComponent(agentId)}/connection-readiness`)).body);
|
|
5301
|
+
return 0;
|
|
5302
|
+
case "set": {
|
|
5303
|
+
if (!options.profileJson) {
|
|
5304
|
+
write(ctx.io.stderr, "agent-profile set needs --profile-json '{...}'.\n");
|
|
5305
|
+
return 2;
|
|
5306
|
+
}
|
|
5307
|
+
let profile;
|
|
5308
|
+
try {
|
|
5309
|
+
profile = JSON.parse(String(options.profileJson));
|
|
5310
|
+
} catch {
|
|
5311
|
+
throw new CliError("--profile-json must be valid JSON.", 2);
|
|
5312
|
+
}
|
|
5313
|
+
const res = await requestJson(ctx, prof(agentId), { method: "PUT", body: profile });
|
|
5314
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5315
|
+
else writeLine(ctx.io.stdout, `Updated profile for agent ${agentId}.`);
|
|
5316
|
+
return 0;
|
|
5317
|
+
}
|
|
5318
|
+
default:
|
|
5319
|
+
throw new CliError(`Unknown agent-profile command: ${sub}`);
|
|
5320
|
+
}
|
|
5321
|
+
}
|
|
5322
|
+
|
|
5323
|
+
// src/cli/agentAllowlists.ts
|
|
5324
|
+
var BASE5 = "/v1/host/sample/agent-allowlists/admin/agents";
|
|
5325
|
+
var AGENT_ALLOWLISTS_HELP = `Usage:
|
|
5326
|
+
openwop agent-allowlists list [--json]
|
|
5327
|
+
openwop agent-allowlists get <agentId> [--json]
|
|
5328
|
+
openwop agent-allowlists set <agentId> --allowlist-json '[...]' [--json]
|
|
5329
|
+
openwop agent-allowlists clear <agentId> [--yes]
|
|
5330
|
+
|
|
5331
|
+
Super-admin per-agent tool-allowlist overrides (ADR 0104). \`set\` PUTs the override
|
|
5332
|
+
(an array of tool ids); \`clear\` removes it. Requires super-admin (OPENWOP_SUPERADMIN_TENANTS).`;
|
|
5333
|
+
async function req(ctx, path, opts, action) {
|
|
5334
|
+
try {
|
|
5335
|
+
return await requestJson(ctx, path, opts);
|
|
5336
|
+
} catch (err) {
|
|
5337
|
+
if (err instanceof HttpError && (err.status === 401 || err.status === 403)) {
|
|
5338
|
+
throw new CliError(`${action} requires super-admin on this host (OPENWOP_SUPERADMIN_TENANTS).`, 4);
|
|
5339
|
+
}
|
|
5340
|
+
throw err;
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
async function runAgentAllowlists(ctx, argv) {
|
|
5344
|
+
const sub = argv[0] ?? "list";
|
|
5345
|
+
if (sub === "--help" || sub === "-h") {
|
|
5346
|
+
write(ctx.io.stdout, AGENT_ALLOWLISTS_HELP);
|
|
5347
|
+
return 0;
|
|
5348
|
+
}
|
|
5349
|
+
const args = argv.slice(["list", "get", "set", "clear"].includes(sub) ? 1 : 0);
|
|
5350
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--allowlist-json"] });
|
|
5351
|
+
if (options.help) {
|
|
5352
|
+
write(ctx.io.stdout, AGENT_ALLOWLISTS_HELP);
|
|
5353
|
+
return 0;
|
|
5354
|
+
}
|
|
5355
|
+
switch (sub) {
|
|
5356
|
+
case "list": {
|
|
5357
|
+
const res = await req(ctx, BASE5, {}, "Reading agent allowlists");
|
|
5358
|
+
if (ctx.json) {
|
|
5359
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5360
|
+
return 0;
|
|
5361
|
+
}
|
|
5362
|
+
const items = Array.isArray(res.body?.agents) ? res.body.agents : [];
|
|
5363
|
+
writeLine(ctx.io.stdout, items.length ? formatTable(items.map((a) => ({ agentId: a.agentId ?? a.id ?? "", tools: Array.isArray(a.allowlist) ? a.allowlist.length : "" })), ["agentId", "tools"]) : "No allowlist overrides.");
|
|
5364
|
+
return 0;
|
|
5365
|
+
}
|
|
5366
|
+
case "get": {
|
|
5367
|
+
if (positionals.length !== 1) {
|
|
5368
|
+
write(ctx.io.stderr, "Usage: openwop agent-allowlists get <agentId>\n");
|
|
5369
|
+
return 2;
|
|
5370
|
+
}
|
|
5371
|
+
writeJson(ctx.io.stdout, (await req(ctx, `${BASE5}/${encodeURIComponent(positionals[0])}`, {}, "Reading the agent allowlist")).body);
|
|
5372
|
+
return 0;
|
|
5373
|
+
}
|
|
5374
|
+
case "set": {
|
|
5375
|
+
if (positionals.length !== 1 || !options.allowlistJson) {
|
|
5376
|
+
write(ctx.io.stderr, "Usage: openwop agent-allowlists set <agentId> --allowlist-json '[...]'\n");
|
|
5377
|
+
return 2;
|
|
5378
|
+
}
|
|
5379
|
+
let allowlist;
|
|
5380
|
+
try {
|
|
5381
|
+
allowlist = JSON.parse(String(options.allowlistJson));
|
|
5382
|
+
} catch {
|
|
5383
|
+
throw new CliError("--allowlist-json must be valid JSON.", 2);
|
|
5384
|
+
}
|
|
5385
|
+
const res = await req(ctx, `${BASE5}/${encodeURIComponent(positionals[0])}`, { method: "PUT", body: { allowlist } }, "Setting the agent allowlist");
|
|
5386
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5387
|
+
else writeLine(ctx.io.stdout, `Set allowlist override for ${positionals[0]}.`);
|
|
5388
|
+
return 0;
|
|
5389
|
+
}
|
|
5390
|
+
case "clear": {
|
|
5391
|
+
if (positionals.length !== 1) {
|
|
5392
|
+
write(ctx.io.stderr, "Usage: openwop agent-allowlists clear <agentId> [--yes]\n");
|
|
5393
|
+
return 2;
|
|
5394
|
+
}
|
|
5395
|
+
if (!options.yes) throw new CliError(`Refusing to clear the allowlist override for ${positionals[0]} without --yes.`, 2);
|
|
5396
|
+
await req(ctx, `${BASE5}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" }, "Clearing the agent allowlist");
|
|
5397
|
+
writeLine(ctx.io.stdout, `Cleared allowlist override for ${positionals[0]}.`);
|
|
5398
|
+
return 0;
|
|
5399
|
+
}
|
|
5400
|
+
default:
|
|
5401
|
+
throw new CliError(`Unknown agent-allowlists command: ${sub}`);
|
|
5402
|
+
}
|
|
5403
|
+
}
|
|
5404
|
+
|
|
5405
|
+
// src/cli/agentPacks.ts
|
|
5406
|
+
var BASE6 = "/v1/host/sample/registry/agent-packs";
|
|
5407
|
+
var AGENT_PACKS_HELP = `Usage:
|
|
5408
|
+
openwop agent-packs list [--json]
|
|
5409
|
+
openwop agent-packs install --name <core.openwop.agents.*> [--version <v>] [--json]
|
|
5410
|
+
|
|
5411
|
+
The agent (persona) pack registry. \`install\` provisions a signed agent pack (its name
|
|
5412
|
+
MUST start with 'core.openwop.agents.'). Distinct from the node-pack 'packs' registry.`;
|
|
5413
|
+
async function runAgentPacks(ctx, argv) {
|
|
5414
|
+
const sub = argv[0] ?? "list";
|
|
5415
|
+
if (sub === "--help" || sub === "-h") {
|
|
5416
|
+
write(ctx.io.stdout, AGENT_PACKS_HELP);
|
|
5417
|
+
return 0;
|
|
5418
|
+
}
|
|
5419
|
+
const args = argv.slice(["list", "install"].includes(sub) ? 1 : 0);
|
|
5420
|
+
const { options } = parseOptions(args, { bool: ["--help"], value: ["--name", "--version"] });
|
|
5421
|
+
if (options.help) {
|
|
5422
|
+
write(ctx.io.stdout, AGENT_PACKS_HELP);
|
|
5423
|
+
return 0;
|
|
5424
|
+
}
|
|
5425
|
+
switch (sub) {
|
|
5426
|
+
case "list": {
|
|
5427
|
+
const res = await requestJson(ctx, BASE6);
|
|
5428
|
+
if (ctx.json) {
|
|
5429
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5430
|
+
return 0;
|
|
5431
|
+
}
|
|
5432
|
+
const items = Array.isArray(res.body?.packs) ? res.body.packs : Array.isArray(res.body) ? res.body : [];
|
|
5433
|
+
writeLine(ctx.io.stdout, items.length ? formatTable(items.map((p) => ({ name: p.name ?? "", version: p.version ?? p.latestVersion ?? "", agents: Array.isArray(p.agents) ? p.agents.length : "" })), ["name", "version", "agents"]) : "No agent packs.");
|
|
5434
|
+
return 0;
|
|
5435
|
+
}
|
|
5436
|
+
case "install": {
|
|
5437
|
+
if (!options.name) {
|
|
5438
|
+
write(ctx.io.stderr, "Usage: openwop agent-packs install --name <core.openwop.agents.*> [--version <v>]\n");
|
|
5439
|
+
return 2;
|
|
5440
|
+
}
|
|
5441
|
+
const body = { name: String(options.name) };
|
|
5442
|
+
if (options.version) body.version = String(options.version);
|
|
5443
|
+
const res = await requestJson(ctx, `${BASE6}/install`, { method: "POST", body });
|
|
5444
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5445
|
+
else writeLine(ctx.io.stdout, `Installed agent pack ${String(options.name)}.`);
|
|
5446
|
+
return 0;
|
|
5447
|
+
}
|
|
5448
|
+
default:
|
|
5449
|
+
throw new CliError(`Unknown agent-packs command: ${sub}`);
|
|
5450
|
+
}
|
|
5451
|
+
}
|
|
5452
|
+
|
|
5453
|
+
// src/cli/agentOps.ts
|
|
5454
|
+
var H = "/v1/host/sample";
|
|
5455
|
+
var AGENT_OPS_HELP = `Usage:
|
|
5456
|
+
openwop agent-ops seed [--heal] [--json]
|
|
5457
|
+
openwop agent-ops status [--json]
|
|
5458
|
+
openwop agent-ops run [--step <id>]... [--dry-run] [--json]
|
|
5459
|
+
openwop agent-ops clear [--yes]
|
|
5460
|
+
openwop agent-ops roster-check <rosterId> [--json]
|
|
5461
|
+
openwop agent-ops roster-activity <rosterId> [--json]
|
|
5462
|
+
openwop agent-ops fleet-activity [--json]
|
|
5463
|
+
|
|
5464
|
+
Demo/operations helpers: seed/run/clear the example dataset + read roster & fleet
|
|
5465
|
+
activity. \`seed --heal\` repairs a partial seed; \`run --dry-run\` previews without writing.`;
|
|
5466
|
+
async function runAgentOps(ctx, argv) {
|
|
5467
|
+
const sub = argv[0] ?? "status";
|
|
5468
|
+
if (sub === "--help" || sub === "-h") {
|
|
5469
|
+
write(ctx.io.stdout, AGENT_OPS_HELP);
|
|
5470
|
+
return 0;
|
|
5471
|
+
}
|
|
5472
|
+
const args = argv.slice(["seed", "status", "run", "clear", "roster-check", "roster-activity", "fleet-activity"].includes(sub) ? 1 : 0);
|
|
5473
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--heal", "--dry-run", "--yes"], multi: ["--step"] });
|
|
5474
|
+
if (options.help) {
|
|
5475
|
+
write(ctx.io.stdout, AGENT_OPS_HELP);
|
|
5476
|
+
return 0;
|
|
5477
|
+
}
|
|
5478
|
+
const rid = positionals[0];
|
|
5479
|
+
switch (sub) {
|
|
5480
|
+
case "seed": {
|
|
5481
|
+
const res = await requestJson(ctx, `${H}/example-data/seed`, { method: "POST", body: options.heal ? { heal: true } : {} });
|
|
5482
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5483
|
+
else writeLine(ctx.io.stdout, "Seeded example data.");
|
|
5484
|
+
return 0;
|
|
5485
|
+
}
|
|
5486
|
+
case "status":
|
|
5487
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${H}/example-data/status`)).body);
|
|
5488
|
+
return 0;
|
|
5489
|
+
case "run": {
|
|
5490
|
+
const body = {};
|
|
5491
|
+
if (Array.isArray(options.step) && options.step.length) body.steps = options.step;
|
|
5492
|
+
if (options.dryRun) body.dryRun = true;
|
|
5493
|
+
const res = await requestJson(ctx, `${H}/example-data/run`, { method: "POST", body });
|
|
5494
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5495
|
+
else writeLine(ctx.io.stdout, options.dryRun ? "Dry-run complete." : "Ran example-data steps.");
|
|
5496
|
+
return 0;
|
|
5497
|
+
}
|
|
5498
|
+
case "clear": {
|
|
5499
|
+
if (!options.yes) throw new CliError("Refusing to clear example data without --yes.", 2);
|
|
5500
|
+
await requestJson(ctx, `${H}/example-data/clear`, { method: "POST", body: {} });
|
|
5501
|
+
writeLine(ctx.io.stdout, "Cleared example data.");
|
|
5502
|
+
return 0;
|
|
5503
|
+
}
|
|
5504
|
+
case "roster-check": {
|
|
5505
|
+
if (!rid) {
|
|
5506
|
+
write(ctx.io.stderr, "Usage: openwop agent-ops roster-check <rosterId>\n");
|
|
5507
|
+
return 2;
|
|
5508
|
+
}
|
|
5509
|
+
const res = await requestJson(ctx, `${H}/roster/${encodeURIComponent(rid)}/check`, { method: "POST", body: {} });
|
|
5510
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5511
|
+
return 0;
|
|
5512
|
+
}
|
|
5513
|
+
case "roster-activity": {
|
|
5514
|
+
if (!rid) {
|
|
5515
|
+
write(ctx.io.stderr, "Usage: openwop agent-ops roster-activity <rosterId>\n");
|
|
5516
|
+
return 2;
|
|
5517
|
+
}
|
|
5518
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${H}/roster/${encodeURIComponent(rid)}/activity`)).body);
|
|
5519
|
+
return 0;
|
|
5520
|
+
}
|
|
5521
|
+
case "fleet-activity":
|
|
5522
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${H}/fleet/activity`)).body);
|
|
5523
|
+
return 0;
|
|
5524
|
+
default:
|
|
5525
|
+
throw new CliError(`Unknown agent-ops command: ${sub}`);
|
|
5526
|
+
}
|
|
5527
|
+
}
|
|
5528
|
+
|
|
5529
|
+
// src/cli/evals.ts
|
|
5530
|
+
var base7 = (org) => `/v1/host/sample/evals/orgs/${encodeURIComponent(org)}`;
|
|
5531
|
+
var EVALS_HELP = `Usage:
|
|
5532
|
+
openwop evals leaderboard --org <orgId> [--json]
|
|
5533
|
+
openwop evals rating --org <orgId> [--json]
|
|
5534
|
+
openwop evals match --org <orgId> --model-a <a> --model-b <b> --winner <a|b|tie> [--json]
|
|
5535
|
+
|
|
5536
|
+
Model evals (host-extension, org-scoped). \`leaderboard\`/\`rating\` read the standings;
|
|
5537
|
+
\`match\` records an arena head-to-head outcome. Every command needs --org.`;
|
|
5538
|
+
async function runEvals(ctx, argv) {
|
|
5539
|
+
const sub = argv[0] ?? "leaderboard";
|
|
5540
|
+
if (sub === "--help" || sub === "-h") {
|
|
5541
|
+
write(ctx.io.stdout, EVALS_HELP);
|
|
5542
|
+
return 0;
|
|
5543
|
+
}
|
|
5544
|
+
const args = argv.slice(["leaderboard", "rating", "match"].includes(sub) ? 1 : 0);
|
|
5545
|
+
const { options } = parseOptions(args, { bool: ["--help"], value: ["--org", "--model-a", "--model-b", "--winner"] });
|
|
5546
|
+
if (options.help) {
|
|
5547
|
+
write(ctx.io.stdout, EVALS_HELP);
|
|
5548
|
+
return 0;
|
|
5549
|
+
}
|
|
5550
|
+
const org = requireOrg(options.org);
|
|
5551
|
+
switch (sub) {
|
|
5552
|
+
case "leaderboard":
|
|
5553
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${base7(org)}/leaderboard`)).body);
|
|
5554
|
+
return 0;
|
|
5555
|
+
case "rating":
|
|
5556
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${base7(org)}/arena/rating`)).body);
|
|
5557
|
+
return 0;
|
|
5558
|
+
case "match": {
|
|
5559
|
+
if (!options.modelA || !options.modelB || !options.winner) {
|
|
5560
|
+
write(ctx.io.stderr, "evals match needs --model-a, --model-b, --winner.\n");
|
|
5561
|
+
return 2;
|
|
5562
|
+
}
|
|
5563
|
+
const res = await requestJson(ctx, `${base7(org)}/arena/match`, { method: "POST", body: { modelA: String(options.modelA), modelB: String(options.modelB), winner: String(options.winner) } });
|
|
5564
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5565
|
+
else writeLine(ctx.io.stdout, `Recorded match (${String(options.modelA)} vs ${String(options.modelB)} \u2192 ${String(options.winner)}).`);
|
|
5566
|
+
return 0;
|
|
5567
|
+
}
|
|
5568
|
+
default:
|
|
5569
|
+
throw new CliError(`Unknown evals command: ${sub}`);
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
|
|
5573
|
+
// src/cli/twin.ts
|
|
5574
|
+
var twinPath = (id) => `/v1/host/sample/agents/${encodeURIComponent(id)}/twin`;
|
|
5575
|
+
var GRANTS = "/v1/host/sample/profiles/me/twin-grants";
|
|
5576
|
+
var TWIN_HELP = `Usage:
|
|
5577
|
+
openwop twin get <agentId> [--json]
|
|
5578
|
+
openwop twin set <agentId> --scopes <a,b,...> [--json]
|
|
5579
|
+
openwop twin clear <agentId> [--yes]
|
|
5580
|
+
openwop twin grants [--json]
|
|
5581
|
+
openwop twin grant --agent <agentId> --scopes <a,b,...> [--json]
|
|
5582
|
+
openwop twin revoke <agentId> [--yes]
|
|
5583
|
+
|
|
5584
|
+
An agent's digital-twin config + your twin GRANTS (which agents may act as your twin,
|
|
5585
|
+
with which scopes). \`set\` configures an agent's twin; \`grant\`/\`revoke\` manage grants.`;
|
|
5586
|
+
async function runTwin(ctx, argv) {
|
|
5587
|
+
const sub = argv[0] ?? "get";
|
|
5588
|
+
if (sub === "--help" || sub === "-h") {
|
|
5589
|
+
write(ctx.io.stdout, TWIN_HELP);
|
|
5590
|
+
return 0;
|
|
5591
|
+
}
|
|
5592
|
+
const args = argv.slice(["get", "set", "clear", "grants", "grant", "revoke"].includes(sub) ? 1 : 0);
|
|
5593
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--scopes", "--agent"] });
|
|
5594
|
+
if (options.help) {
|
|
5595
|
+
write(ctx.io.stdout, TWIN_HELP);
|
|
5596
|
+
return 0;
|
|
5597
|
+
}
|
|
5598
|
+
const scopes = options.scopes ? String(options.scopes).split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
5599
|
+
switch (sub) {
|
|
5600
|
+
case "get": {
|
|
5601
|
+
if (!positionals[0]) {
|
|
5602
|
+
write(ctx.io.stderr, "Usage: openwop twin get <agentId>\n");
|
|
5603
|
+
return 2;
|
|
5604
|
+
}
|
|
5605
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, twinPath(positionals[0]))).body);
|
|
5606
|
+
return 0;
|
|
5607
|
+
}
|
|
5608
|
+
case "set": {
|
|
5609
|
+
if (!positionals[0] || !scopes) {
|
|
5610
|
+
write(ctx.io.stderr, "Usage: openwop twin set <agentId> --scopes <a,b,...>\n");
|
|
5611
|
+
return 2;
|
|
5612
|
+
}
|
|
5613
|
+
const res = await requestJson(ctx, twinPath(positionals[0]), { method: "PUT", body: { scopes } });
|
|
5614
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5615
|
+
else writeLine(ctx.io.stdout, `Configured twin for agent ${positionals[0]}.`);
|
|
5616
|
+
return 0;
|
|
5617
|
+
}
|
|
5618
|
+
case "clear": {
|
|
5619
|
+
if (!positionals[0]) {
|
|
5620
|
+
write(ctx.io.stderr, "Usage: openwop twin clear <agentId> [--yes]\n");
|
|
5621
|
+
return 2;
|
|
5622
|
+
}
|
|
5623
|
+
if (!options.yes) throw new CliError(`Refusing to clear the twin for ${positionals[0]} without --yes.`, 2);
|
|
5624
|
+
await requestJson(ctx, twinPath(positionals[0]), { method: "DELETE" });
|
|
5625
|
+
writeLine(ctx.io.stdout, `Cleared twin for ${positionals[0]}.`);
|
|
5626
|
+
return 0;
|
|
5627
|
+
}
|
|
5628
|
+
case "grants": {
|
|
5629
|
+
const res = await requestJson(ctx, GRANTS);
|
|
5630
|
+
if (ctx.json) {
|
|
5631
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5632
|
+
return 0;
|
|
5633
|
+
}
|
|
5634
|
+
const items = Array.isArray(res.body?.grants) ? res.body.grants : [];
|
|
5635
|
+
writeLine(ctx.io.stdout, items.length ? formatTable(items.map((g) => ({ agentId: g.agentId ?? "", scopes: Array.isArray(g.scopes) ? g.scopes.join(",") : "" })), ["agentId", "scopes"]) : "No twin grants.");
|
|
5636
|
+
return 0;
|
|
5637
|
+
}
|
|
5638
|
+
case "grant": {
|
|
5639
|
+
if (!options.agent || !scopes) {
|
|
5640
|
+
write(ctx.io.stderr, "Usage: openwop twin grant --agent <agentId> --scopes <a,b,...>\n");
|
|
5641
|
+
return 2;
|
|
5642
|
+
}
|
|
5643
|
+
const res = await requestJson(ctx, GRANTS, { method: "POST", body: { agentId: String(options.agent), scopes } });
|
|
5644
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5645
|
+
else writeLine(ctx.io.stdout, `Granted twin scope to ${String(options.agent)}.`);
|
|
5646
|
+
return 0;
|
|
5647
|
+
}
|
|
5648
|
+
case "revoke": {
|
|
5649
|
+
if (!positionals[0]) {
|
|
5650
|
+
write(ctx.io.stderr, "Usage: openwop twin revoke <agentId> [--yes]\n");
|
|
5651
|
+
return 2;
|
|
5652
|
+
}
|
|
5653
|
+
if (!options.yes) throw new CliError(`Refusing to revoke the twin grant for ${positionals[0]} without --yes.`, 2);
|
|
5654
|
+
await requestJson(ctx, `${GRANTS}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
5655
|
+
writeLine(ctx.io.stdout, `Revoked twin grant for ${positionals[0]}.`);
|
|
5656
|
+
return 0;
|
|
5657
|
+
}
|
|
5658
|
+
default:
|
|
5659
|
+
throw new CliError(`Unknown twin command: ${sub}`);
|
|
5660
|
+
}
|
|
5661
|
+
}
|
|
5662
|
+
|
|
5663
|
+
// src/cli/strategy.ts
|
|
5664
|
+
var BASE7 = "/v1/host/sample/strategy";
|
|
5665
|
+
var STRATEGY_HELP = `Usage:
|
|
5666
|
+
openwop strategy list [--json]
|
|
5667
|
+
openwop strategy get <strategyId> [--json]
|
|
5668
|
+
openwop strategy create --org <orgId> --title <t> [--json]
|
|
5669
|
+
openwop strategy update <strategyId> [--title <t>] [--json]
|
|
5670
|
+
openwop strategy delete <strategyId> [--yes]
|
|
5671
|
+
openwop strategy context [--json]
|
|
5672
|
+
openwop strategy health [--json]
|
|
5673
|
+
|
|
5674
|
+
Strategy documents (host-extension). \`context\`/\`health\` read the strategy context +
|
|
5675
|
+
its health. \`create\` needs the owning --org. The host is the authority; the CLI relays.`;
|
|
5676
|
+
async function runStrategy(ctx, argv) {
|
|
5677
|
+
const sub = argv[0] ?? "list";
|
|
5678
|
+
if (sub === "--help" || sub === "-h") {
|
|
5679
|
+
write(ctx.io.stdout, STRATEGY_HELP);
|
|
5680
|
+
return 0;
|
|
5681
|
+
}
|
|
5682
|
+
const args = argv.slice(["list", "get", "create", "update", "delete", "context", "health"].includes(sub) ? 1 : 0);
|
|
5683
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--title"] });
|
|
5684
|
+
if (options.help) {
|
|
5685
|
+
write(ctx.io.stdout, STRATEGY_HELP);
|
|
5686
|
+
return 0;
|
|
5687
|
+
}
|
|
5688
|
+
const id = positionals[0];
|
|
5689
|
+
switch (sub) {
|
|
5690
|
+
case "list": {
|
|
5691
|
+
const res = await requestJson(ctx, BASE7);
|
|
5692
|
+
if (ctx.json) {
|
|
5693
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5694
|
+
return 0;
|
|
5695
|
+
}
|
|
5696
|
+
const items = Array.isArray(res.body?.strategies) ? res.body.strategies : [];
|
|
5697
|
+
writeLine(ctx.io.stdout, items.length ? formatTable(items.map((s) => ({ id: s.id ?? "", title: s.title ?? "", org: s.orgId ?? "" })), ["id", "title", "org"]) : "No strategies.");
|
|
5698
|
+
return 0;
|
|
5699
|
+
}
|
|
5700
|
+
case "get": {
|
|
5701
|
+
if (!id) {
|
|
5702
|
+
write(ctx.io.stderr, "Usage: openwop strategy get <strategyId>\n");
|
|
5703
|
+
return 2;
|
|
5704
|
+
}
|
|
5705
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE7}/${encodeURIComponent(id)}`)).body);
|
|
5706
|
+
return 0;
|
|
5707
|
+
}
|
|
5708
|
+
case "context":
|
|
5709
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE7}/context`)).body);
|
|
5710
|
+
return 0;
|
|
5711
|
+
case "health":
|
|
5712
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE7}/health`)).body);
|
|
5713
|
+
return 0;
|
|
5714
|
+
case "create": {
|
|
5715
|
+
if (!options.org || !options.title) {
|
|
5716
|
+
write(ctx.io.stderr, "strategy create needs --org and --title.\n");
|
|
5717
|
+
return 2;
|
|
5718
|
+
}
|
|
5719
|
+
const res = await requestJson(ctx, BASE7, { method: "POST", body: { orgId: String(options.org), title: String(options.title) } });
|
|
5720
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5721
|
+
else writeLine(ctx.io.stdout, `Created strategy ${res.body?.id ?? ""} (${String(options.title)}).`);
|
|
5722
|
+
return 0;
|
|
5723
|
+
}
|
|
5724
|
+
case "update": {
|
|
5725
|
+
if (!id) {
|
|
5726
|
+
write(ctx.io.stderr, "Usage: openwop strategy update <strategyId> [--title t]\n");
|
|
5727
|
+
return 2;
|
|
5728
|
+
}
|
|
5729
|
+
const patch = {};
|
|
5730
|
+
if (options.title) patch.title = String(options.title);
|
|
5731
|
+
const res = await requestJson(ctx, `${BASE7}/${encodeURIComponent(id)}`, { method: "PATCH", body: patch });
|
|
5732
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5733
|
+
else writeLine(ctx.io.stdout, `Updated strategy ${id}.`);
|
|
5734
|
+
return 0;
|
|
5735
|
+
}
|
|
5736
|
+
case "delete": {
|
|
5737
|
+
if (!id) {
|
|
5738
|
+
write(ctx.io.stderr, "Usage: openwop strategy delete <strategyId> [--yes]\n");
|
|
5739
|
+
return 2;
|
|
5740
|
+
}
|
|
5741
|
+
if (!options.yes) throw new CliError(`Refusing to delete strategy ${id} without --yes.`, 2);
|
|
5742
|
+
await requestJson(ctx, `${BASE7}/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
5743
|
+
writeLine(ctx.io.stdout, `Deleted strategy ${id}.`);
|
|
5744
|
+
return 0;
|
|
5745
|
+
}
|
|
5746
|
+
default:
|
|
5747
|
+
throw new CliError(`Unknown strategy command: ${sub}`);
|
|
5748
|
+
}
|
|
5749
|
+
}
|
|
5750
|
+
|
|
5751
|
+
// src/cli/advisoryBoard.ts
|
|
5752
|
+
var BOARDS = "/v1/host/sample/advisors/boards";
|
|
5753
|
+
var ADVISORS_HELP = `Usage:
|
|
5754
|
+
openwop advisors list [--json]
|
|
5755
|
+
openwop advisors get <boardId> [--json]
|
|
5756
|
+
openwop advisors by-handle <handle> [--json]
|
|
5757
|
+
openwop advisors create --org <orgId> --name <n> [--handle <h>] [--json]
|
|
5758
|
+
openwop advisors delete <boardId> [--yes]
|
|
5759
|
+
|
|
5760
|
+
Advisory boards (host-extension) \u2014 a panel of advisor agents. \`create\` needs the
|
|
5761
|
+
owning --org. The host is the authority; the CLI mirrors + relays.`;
|
|
5762
|
+
async function runAdvisors(ctx, argv) {
|
|
5763
|
+
const sub = argv[0] ?? "list";
|
|
5764
|
+
if (sub === "--help" || sub === "-h") {
|
|
5765
|
+
write(ctx.io.stdout, ADVISORS_HELP);
|
|
5766
|
+
return 0;
|
|
5767
|
+
}
|
|
5768
|
+
const args = argv.slice(["list", "get", "by-handle", "create", "delete"].includes(sub) ? 1 : 0);
|
|
5769
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--name", "--handle"] });
|
|
5770
|
+
if (options.help) {
|
|
5771
|
+
write(ctx.io.stdout, ADVISORS_HELP);
|
|
5772
|
+
return 0;
|
|
5773
|
+
}
|
|
5774
|
+
const id = positionals[0];
|
|
5775
|
+
switch (sub) {
|
|
5776
|
+
case "list": {
|
|
5777
|
+
const res = await requestJson(ctx, BOARDS);
|
|
5778
|
+
if (ctx.json) {
|
|
5779
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5780
|
+
return 0;
|
|
5781
|
+
}
|
|
5782
|
+
const items = Array.isArray(res.body?.boards) ? res.body.boards : [];
|
|
5783
|
+
writeLine(ctx.io.stdout, items.length ? formatTable(items.map((b) => ({ id: b.id ?? b.boardId ?? "", name: b.name ?? "", handle: b.handle ?? "" })), ["id", "name", "handle"]) : "No advisory boards.");
|
|
5784
|
+
return 0;
|
|
5785
|
+
}
|
|
5786
|
+
case "get": {
|
|
5787
|
+
if (!id) {
|
|
5788
|
+
write(ctx.io.stderr, "Usage: openwop advisors get <boardId>\n");
|
|
5789
|
+
return 2;
|
|
5790
|
+
}
|
|
5791
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${BOARDS}/${encodeURIComponent(id)}`)).body);
|
|
5792
|
+
return 0;
|
|
5793
|
+
}
|
|
5794
|
+
case "by-handle": {
|
|
5795
|
+
if (!id) {
|
|
5796
|
+
write(ctx.io.stderr, "Usage: openwop advisors by-handle <handle>\n");
|
|
5797
|
+
return 2;
|
|
5798
|
+
}
|
|
5799
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${BOARDS}/by-handle/${encodeURIComponent(id)}`)).body);
|
|
5800
|
+
return 0;
|
|
5801
|
+
}
|
|
5802
|
+
case "create": {
|
|
5803
|
+
if (!options.org || !options.name) {
|
|
5804
|
+
write(ctx.io.stderr, "advisors create needs --org and --name.\n");
|
|
5805
|
+
return 2;
|
|
5806
|
+
}
|
|
5807
|
+
const body = { orgId: String(options.org), name: String(options.name) };
|
|
5808
|
+
if (options.handle) body.handle = String(options.handle);
|
|
5809
|
+
const res = await requestJson(ctx, BOARDS, { method: "POST", body });
|
|
5810
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5811
|
+
else writeLine(ctx.io.stdout, `Created advisory board ${res.body?.id ?? ""} (${String(options.name)}).`);
|
|
5812
|
+
return 0;
|
|
5813
|
+
}
|
|
5814
|
+
case "delete": {
|
|
5815
|
+
if (!id) {
|
|
5816
|
+
write(ctx.io.stderr, "Usage: openwop advisors delete <boardId> [--yes]\n");
|
|
5817
|
+
return 2;
|
|
5818
|
+
}
|
|
5819
|
+
if (!options.yes) throw new CliError(`Refusing to delete advisory board ${id} without --yes.`, 2);
|
|
5820
|
+
await requestJson(ctx, `${BOARDS}/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
5821
|
+
writeLine(ctx.io.stdout, `Deleted advisory board ${id}.`);
|
|
5822
|
+
return 0;
|
|
5823
|
+
}
|
|
5824
|
+
default:
|
|
5825
|
+
throw new CliError(`Unknown advisors command: ${sub}`);
|
|
5826
|
+
}
|
|
5827
|
+
}
|
|
5828
|
+
|
|
5829
|
+
// src/cli/campaignOrchestration.ts
|
|
5830
|
+
var BASE8 = "/v1/host/sample/campaign-orchestration/campaigns";
|
|
5831
|
+
var CAMPAIGNS_ORCH_HELP = `Usage:
|
|
5832
|
+
openwop campaigns-orchestration list [--json]
|
|
5833
|
+
openwop campaigns-orchestration get <campaignId> [--json]
|
|
5834
|
+
openwop campaigns-orchestration create --name <n> [--json]
|
|
5835
|
+
openwop campaigns-orchestration update <campaignId> [--name <n>] [--json]
|
|
5836
|
+
openwop campaigns-orchestration delete <campaignId> [--yes]
|
|
5837
|
+
openwop campaigns-orchestration finalize <campaignId> [--yes] [--json]
|
|
5838
|
+
|
|
5839
|
+
Campaign orchestration (host-extension). \`finalize\` locks a campaign for execution.
|
|
5840
|
+
The host is the authority; the CLI mirrors + relays.`;
|
|
5841
|
+
async function runCampaignsOrch(ctx, argv) {
|
|
5842
|
+
const sub = argv[0] ?? "list";
|
|
5843
|
+
if (sub === "--help" || sub === "-h") {
|
|
5844
|
+
write(ctx.io.stdout, CAMPAIGNS_ORCH_HELP);
|
|
5845
|
+
return 0;
|
|
5846
|
+
}
|
|
5847
|
+
const args = argv.slice(["list", "get", "create", "update", "delete", "finalize"].includes(sub) ? 1 : 0);
|
|
5848
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--name"] });
|
|
5849
|
+
if (options.help) {
|
|
5850
|
+
write(ctx.io.stdout, CAMPAIGNS_ORCH_HELP);
|
|
5851
|
+
return 0;
|
|
5852
|
+
}
|
|
5853
|
+
const id = positionals[0];
|
|
5854
|
+
switch (sub) {
|
|
5855
|
+
case "list": {
|
|
5856
|
+
const res = await requestJson(ctx, BASE8);
|
|
5857
|
+
if (ctx.json) {
|
|
5858
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5859
|
+
return 0;
|
|
5860
|
+
}
|
|
5861
|
+
const items = Array.isArray(res.body?.campaigns) ? res.body.campaigns : [];
|
|
5862
|
+
writeLine(ctx.io.stdout, items.length ? formatTable(items.map((c) => ({ id: c.id ?? "", name: c.name ?? "", status: c.status ?? "" })), ["id", "name", "status"]) : "No campaigns.");
|
|
5863
|
+
return 0;
|
|
5864
|
+
}
|
|
5865
|
+
case "get": {
|
|
5866
|
+
if (!id) {
|
|
5867
|
+
write(ctx.io.stderr, "Usage: openwop campaigns-orchestration get <campaignId>\n");
|
|
5868
|
+
return 2;
|
|
5869
|
+
}
|
|
5870
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE8}/${encodeURIComponent(id)}`)).body);
|
|
5871
|
+
return 0;
|
|
5872
|
+
}
|
|
5873
|
+
case "create": {
|
|
5874
|
+
if (!options.name) {
|
|
5875
|
+
write(ctx.io.stderr, "campaigns-orchestration create needs --name.\n");
|
|
5876
|
+
return 2;
|
|
5877
|
+
}
|
|
5878
|
+
const res = await requestJson(ctx, BASE8, { method: "POST", body: { name: String(options.name) } });
|
|
5879
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5880
|
+
else writeLine(ctx.io.stdout, `Created campaign ${res.body?.id ?? ""} (${String(options.name)}).`);
|
|
5881
|
+
return 0;
|
|
5882
|
+
}
|
|
5883
|
+
case "update": {
|
|
5884
|
+
if (!id) {
|
|
5885
|
+
write(ctx.io.stderr, "Usage: openwop campaigns-orchestration update <campaignId> [--name n]\n");
|
|
5886
|
+
return 2;
|
|
5887
|
+
}
|
|
5888
|
+
const patch = {};
|
|
5889
|
+
if (options.name) patch.name = String(options.name);
|
|
5890
|
+
const res = await requestJson(ctx, `${BASE8}/${encodeURIComponent(id)}`, { method: "PATCH", body: patch });
|
|
5891
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5892
|
+
else writeLine(ctx.io.stdout, `Updated campaign ${id}.`);
|
|
5893
|
+
return 0;
|
|
5894
|
+
}
|
|
5895
|
+
case "delete": {
|
|
5896
|
+
if (!id) {
|
|
5897
|
+
write(ctx.io.stderr, "Usage: openwop campaigns-orchestration delete <campaignId> [--yes]\n");
|
|
5898
|
+
return 2;
|
|
5899
|
+
}
|
|
5900
|
+
if (!options.yes) throw new CliError(`Refusing to delete campaign ${id} without --yes.`, 2);
|
|
5901
|
+
await requestJson(ctx, `${BASE8}/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
5902
|
+
writeLine(ctx.io.stdout, `Deleted campaign ${id}.`);
|
|
5903
|
+
return 0;
|
|
5904
|
+
}
|
|
5905
|
+
case "finalize": {
|
|
5906
|
+
if (!id) {
|
|
5907
|
+
write(ctx.io.stderr, "Usage: openwop campaigns-orchestration finalize <campaignId> [--yes]\n");
|
|
5908
|
+
return 2;
|
|
5909
|
+
}
|
|
5910
|
+
if (!options.yes) throw new CliError(`Refusing to finalize campaign ${id} without --yes.`, 2);
|
|
5911
|
+
const res = await requestJson(ctx, `/v1/host/sample/campaign-orchestration/finalize`, { method: "POST", body: { campaignId: id } });
|
|
5912
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5913
|
+
else writeLine(ctx.io.stdout, `Finalized campaign ${id}.`);
|
|
5914
|
+
return 0;
|
|
5915
|
+
}
|
|
5916
|
+
default:
|
|
5917
|
+
throw new CliError(`Unknown campaigns-orchestration command: ${sub}`);
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5920
|
+
|
|
5921
|
+
// src/cli/notebooks.ts
|
|
5922
|
+
var BASE9 = "/v1/host/sample/notebooks";
|
|
5923
|
+
var NOTEBOOKS_HELP = `Usage:
|
|
5924
|
+
openwop notebooks list [--json]
|
|
5925
|
+
openwop notebooks get <notebookId> [--json]
|
|
5926
|
+
openwop notebooks create --org <orgId> --name <n> [--json]
|
|
5927
|
+
openwop notebooks delete <notebookId> [--yes]
|
|
5928
|
+
openwop notebooks notes <notebookId> [--json]
|
|
5929
|
+
|
|
5930
|
+
NotebookLM-style notebooks (host-extension). A notebook holds sources + notes + a chat.
|
|
5931
|
+
\`create\` needs the owning --org. The host is the authority; the CLI mirrors + relays.`;
|
|
5932
|
+
async function runNotebooks(ctx, argv) {
|
|
5933
|
+
const sub = argv[0] ?? "list";
|
|
5934
|
+
if (sub === "--help" || sub === "-h") {
|
|
5935
|
+
write(ctx.io.stdout, NOTEBOOKS_HELP);
|
|
5936
|
+
return 0;
|
|
5937
|
+
}
|
|
5938
|
+
const args = argv.slice(["list", "get", "create", "delete", "notes"].includes(sub) ? 1 : 0);
|
|
5939
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--name"] });
|
|
5940
|
+
if (options.help) {
|
|
5941
|
+
write(ctx.io.stdout, NOTEBOOKS_HELP);
|
|
3086
5942
|
return 0;
|
|
3087
5943
|
}
|
|
3088
|
-
|
|
3089
|
-
|
|
5944
|
+
const id = positionals[0];
|
|
5945
|
+
switch (sub) {
|
|
5946
|
+
case "list": {
|
|
5947
|
+
const res = await requestJson(ctx, BASE9);
|
|
5948
|
+
if (ctx.json) {
|
|
5949
|
+
writeJson(ctx.io.stdout, res.body);
|
|
5950
|
+
return 0;
|
|
5951
|
+
}
|
|
5952
|
+
const items = Array.isArray(res.body?.notebooks) ? res.body.notebooks : [];
|
|
5953
|
+
writeLine(ctx.io.stdout, items.length ? formatTable(items.map((n) => ({ id: n.id ?? "", title: n.title ?? "", sources: n.sourceCount ?? "" })), ["id", "title", "sources"]) : "No notebooks.");
|
|
5954
|
+
return 0;
|
|
5955
|
+
}
|
|
5956
|
+
case "get": {
|
|
5957
|
+
if (!id) {
|
|
5958
|
+
write(ctx.io.stderr, "Usage: openwop notebooks get <notebookId>\n");
|
|
5959
|
+
return 2;
|
|
5960
|
+
}
|
|
5961
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE9}/${encodeURIComponent(id)}`)).body);
|
|
5962
|
+
return 0;
|
|
5963
|
+
}
|
|
5964
|
+
case "notes": {
|
|
5965
|
+
if (!id) {
|
|
5966
|
+
write(ctx.io.stderr, "Usage: openwop notebooks notes <notebookId>\n");
|
|
5967
|
+
return 2;
|
|
5968
|
+
}
|
|
5969
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${BASE9}/${encodeURIComponent(id)}/notes`)).body);
|
|
5970
|
+
return 0;
|
|
5971
|
+
}
|
|
5972
|
+
case "create": {
|
|
5973
|
+
if (!options.org || !options.name) {
|
|
5974
|
+
write(ctx.io.stderr, "notebooks create needs --org and --name.\n");
|
|
5975
|
+
return 2;
|
|
5976
|
+
}
|
|
5977
|
+
const res = await requestJson(ctx, BASE9, { method: "POST", body: { orgId: String(options.org), name: String(options.name) } });
|
|
5978
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
5979
|
+
else writeLine(ctx.io.stdout, `Created notebook ${res.body?.id ?? ""} (${String(options.name)}).`);
|
|
5980
|
+
return 0;
|
|
5981
|
+
}
|
|
5982
|
+
case "delete": {
|
|
5983
|
+
if (!id) {
|
|
5984
|
+
write(ctx.io.stderr, "Usage: openwop notebooks delete <notebookId> [--yes]\n");
|
|
5985
|
+
return 2;
|
|
5986
|
+
}
|
|
5987
|
+
if (!options.yes) throw new CliError(`Refusing to delete notebook ${id} without --yes.`, 2);
|
|
5988
|
+
await requestJson(ctx, `${BASE9}/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
5989
|
+
writeLine(ctx.io.stdout, `Deleted notebook ${id}.`);
|
|
5990
|
+
return 0;
|
|
5991
|
+
}
|
|
5992
|
+
default:
|
|
5993
|
+
throw new CliError(`Unknown notebooks command: ${sub}`);
|
|
5994
|
+
}
|
|
3090
5995
|
}
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
5996
|
+
|
|
5997
|
+
// src/cli/podcasts.ts
|
|
5998
|
+
var EP = "/v1/host/sample/podcasts/episodes";
|
|
5999
|
+
var PODCASTS_HELP = `Usage:
|
|
6000
|
+
openwop podcasts list [--json]
|
|
6001
|
+
openwop podcasts get <episodeId> [--json]
|
|
6002
|
+
openwop podcasts create --org <orgId> --notebook <notebookId> --episode-profile <profileId> [--title <t>] [--json]
|
|
6003
|
+
openwop podcasts delete <episodeId> [--yes]
|
|
6004
|
+
openwop podcasts retry <episodeId> [--json]
|
|
6005
|
+
|
|
6006
|
+
Podcast episodes (host-extension). \`create\` starts an episode; \`retry\` re-runs a
|
|
6007
|
+
failed generation. The host is the authority; the CLI mirrors + relays.`;
|
|
6008
|
+
async function runPodcasts(ctx, argv) {
|
|
6009
|
+
const sub = argv[0] ?? "list";
|
|
6010
|
+
if (sub === "--help" || sub === "-h") {
|
|
6011
|
+
write(ctx.io.stdout, PODCASTS_HELP);
|
|
6012
|
+
return 0;
|
|
3096
6013
|
}
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
6014
|
+
const args = argv.slice(["list", "get", "create", "delete", "retry"].includes(sub) ? 1 : 0);
|
|
6015
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--title", "--org", "--notebook", "--episode-profile"] });
|
|
6016
|
+
if (options.help) {
|
|
6017
|
+
write(ctx.io.stdout, PODCASTS_HELP);
|
|
6018
|
+
return 0;
|
|
6019
|
+
}
|
|
6020
|
+
const id = positionals[0];
|
|
6021
|
+
switch (sub) {
|
|
6022
|
+
case "list": {
|
|
6023
|
+
const res = await requestJson(ctx, EP);
|
|
6024
|
+
if (ctx.json) {
|
|
6025
|
+
writeJson(ctx.io.stdout, res.body);
|
|
6026
|
+
return 0;
|
|
6027
|
+
}
|
|
6028
|
+
const items = Array.isArray(res.body?.episodes) ? res.body.episodes : [];
|
|
6029
|
+
writeLine(ctx.io.stdout, items.length ? formatTable(items.map((e) => ({ id: e.id ?? "", title: e.title ?? "", status: e.status ?? "" })), ["id", "title", "status"]) : "No episodes.");
|
|
6030
|
+
return 0;
|
|
6031
|
+
}
|
|
6032
|
+
case "get": {
|
|
6033
|
+
if (!id) {
|
|
6034
|
+
write(ctx.io.stderr, "Usage: openwop podcasts get <episodeId>\n");
|
|
6035
|
+
return 2;
|
|
6036
|
+
}
|
|
6037
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${EP}/${encodeURIComponent(id)}`)).body);
|
|
6038
|
+
return 0;
|
|
6039
|
+
}
|
|
6040
|
+
case "create": {
|
|
6041
|
+
if (!options.org || !options.notebook || !options.episodeProfile) {
|
|
6042
|
+
write(ctx.io.stderr, "podcasts create needs --org, --notebook, and --episode-profile.\n");
|
|
6043
|
+
return 2;
|
|
6044
|
+
}
|
|
6045
|
+
const body = { orgId: String(options.org), notebookId: String(options.notebook), episodeProfileId: String(options.episodeProfile) };
|
|
6046
|
+
if (options.title) body.title = String(options.title);
|
|
6047
|
+
const res = await requestJson(ctx, EP, { method: "POST", body });
|
|
6048
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
6049
|
+
else writeLine(ctx.io.stdout, `Created episode ${res.body?.id ?? ""}.`);
|
|
6050
|
+
return 0;
|
|
6051
|
+
}
|
|
6052
|
+
case "delete": {
|
|
6053
|
+
if (!id) {
|
|
6054
|
+
write(ctx.io.stderr, "Usage: openwop podcasts delete <episodeId> [--yes]\n");
|
|
6055
|
+
return 2;
|
|
6056
|
+
}
|
|
6057
|
+
if (!options.yes) throw new CliError(`Refusing to delete episode ${id} without --yes.`, 2);
|
|
6058
|
+
await requestJson(ctx, `${EP}/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
6059
|
+
writeLine(ctx.io.stdout, `Deleted episode ${id}.`);
|
|
6060
|
+
return 0;
|
|
6061
|
+
}
|
|
6062
|
+
case "retry": {
|
|
6063
|
+
if (!id) {
|
|
6064
|
+
write(ctx.io.stderr, "Usage: openwop podcasts retry <episodeId>\n");
|
|
6065
|
+
return 2;
|
|
6066
|
+
}
|
|
6067
|
+
const res = await requestJson(ctx, `${EP}/${encodeURIComponent(id)}/retry`, { method: "POST", body: {} });
|
|
6068
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
6069
|
+
else writeLine(ctx.io.stdout, `Retrying episode ${id}.`);
|
|
6070
|
+
return 0;
|
|
6071
|
+
}
|
|
6072
|
+
default:
|
|
6073
|
+
throw new CliError(`Unknown podcasts command: ${sub}`);
|
|
3100
6074
|
}
|
|
3101
|
-
await requestJson(ctx, `/v1/host/sample/kanban/cards/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
3102
|
-
writeLine(ctx.io.stdout, `Deleted card ${positionals[0]}.`);
|
|
3103
|
-
return 0;
|
|
3104
6075
|
}
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
6076
|
+
|
|
6077
|
+
// src/cli/priorityMatrix.ts
|
|
6078
|
+
var LISTS = "/v1/host/sample/priority-matrix/lists";
|
|
6079
|
+
var PRIORITY_MATRIX_HELP = `Usage:
|
|
6080
|
+
openwop priority-matrix lists [--json]
|
|
6081
|
+
openwop priority-matrix get <listId> [--json]
|
|
6082
|
+
openwop priority-matrix create --org <orgId> --name <n> [--json]
|
|
6083
|
+
openwop priority-matrix delete <listId> [--yes]
|
|
6084
|
+
openwop priority-matrix ideas <listId> [--json]
|
|
6085
|
+
|
|
6086
|
+
Prioritization boards (host-extension). A list holds scored/voted ideas; \`ideas\` reads
|
|
6087
|
+
a list's ideas. \`create\` needs the owning --org. The host is the authority.`;
|
|
6088
|
+
async function runPriorityMatrix(ctx, argv) {
|
|
6089
|
+
const sub = argv[0] ?? "lists";
|
|
6090
|
+
if (sub === "--help" || sub === "-h") {
|
|
6091
|
+
write(ctx.io.stdout, PRIORITY_MATRIX_HELP);
|
|
6092
|
+
return 0;
|
|
3110
6093
|
}
|
|
3111
|
-
const
|
|
3112
|
-
const
|
|
3113
|
-
if (
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
throw new CliError(`Could not open the board event stream (HTTP ${res.status}).`, 1);
|
|
6094
|
+
const args = argv.slice(["lists", "get", "create", "delete", "ideas"].includes(sub) ? 1 : 0);
|
|
6095
|
+
const { options, positionals } = parseOptions(args, { bool: ["--help", "--yes"], value: ["--org", "--name"] });
|
|
6096
|
+
if (options.help) {
|
|
6097
|
+
write(ctx.io.stdout, PRIORITY_MATRIX_HELP);
|
|
6098
|
+
return 0;
|
|
3117
6099
|
}
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
6100
|
+
const id = positionals[0];
|
|
6101
|
+
switch (sub) {
|
|
6102
|
+
case "lists": {
|
|
6103
|
+
const res = await requestJson(ctx, LISTS);
|
|
6104
|
+
if (ctx.json) {
|
|
6105
|
+
writeJson(ctx.io.stdout, res.body);
|
|
6106
|
+
return 0;
|
|
6107
|
+
}
|
|
6108
|
+
const items = Array.isArray(res.body?.lists) ? res.body.lists : [];
|
|
6109
|
+
writeLine(ctx.io.stdout, items.length ? formatTable(items.map((l) => ({ id: l.id ?? l.listId ?? "", name: l.name ?? "", ideas: l.ideaCount ?? "" })), ["id", "name", "ideas"]) : "No lists.");
|
|
6110
|
+
return 0;
|
|
3123
6111
|
}
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
6112
|
+
case "get": {
|
|
6113
|
+
if (!id) {
|
|
6114
|
+
write(ctx.io.stderr, "Usage: openwop priority-matrix get <listId>\n");
|
|
6115
|
+
return 2;
|
|
6116
|
+
}
|
|
6117
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${LISTS}/${encodeURIComponent(id)}`)).body);
|
|
6118
|
+
return 0;
|
|
6119
|
+
}
|
|
6120
|
+
case "ideas": {
|
|
6121
|
+
if (!id) {
|
|
6122
|
+
write(ctx.io.stderr, "Usage: openwop priority-matrix ideas <listId>\n");
|
|
6123
|
+
return 2;
|
|
6124
|
+
}
|
|
6125
|
+
writeJson(ctx.io.stdout, (await requestJson(ctx, `${LISTS}/${encodeURIComponent(id)}/ideas`)).body);
|
|
6126
|
+
return 0;
|
|
6127
|
+
}
|
|
6128
|
+
case "create": {
|
|
6129
|
+
if (!options.org || !options.name) {
|
|
6130
|
+
write(ctx.io.stderr, "priority-matrix create needs --org and --name.\n");
|
|
6131
|
+
return 2;
|
|
6132
|
+
}
|
|
6133
|
+
const res = await requestJson(ctx, LISTS, { method: "POST", body: { orgId: String(options.org), name: String(options.name) } });
|
|
6134
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
6135
|
+
else writeLine(ctx.io.stdout, `Created list ${res.body?.id ?? ""} (${String(options.name)}).`);
|
|
6136
|
+
return 0;
|
|
6137
|
+
}
|
|
6138
|
+
case "delete": {
|
|
6139
|
+
if (!id) {
|
|
6140
|
+
write(ctx.io.stderr, "Usage: openwop priority-matrix delete <listId> [--yes]\n");
|
|
6141
|
+
return 2;
|
|
6142
|
+
}
|
|
6143
|
+
if (!options.yes) throw new CliError(`Refusing to delete list ${id} without --yes.`, 2);
|
|
6144
|
+
await requestJson(ctx, `${LISTS}/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
6145
|
+
writeLine(ctx.io.stdout, `Deleted list ${id}.`);
|
|
6146
|
+
return 0;
|
|
6147
|
+
}
|
|
6148
|
+
default:
|
|
6149
|
+
throw new CliError(`Unknown priority-matrix command: ${sub}`);
|
|
6150
|
+
}
|
|
3128
6151
|
}
|
|
3129
6152
|
|
|
3130
6153
|
// src/cli/accessControl.ts
|
|
@@ -3313,10 +6336,10 @@ async function runEntity(ctx, entity, argv) {
|
|
|
3313
6336
|
return orgId ? 0 : 2;
|
|
3314
6337
|
}
|
|
3315
6338
|
const rest = argv.slice(2);
|
|
3316
|
-
const
|
|
6339
|
+
const base8 = `/v1/host/sample/orgs/${encodeURIComponent(orgId)}/${entity}`;
|
|
3317
6340
|
switch (verb) {
|
|
3318
6341
|
case "list": {
|
|
3319
|
-
const res = await requestJson(ctx,
|
|
6342
|
+
const res = await requestJson(ctx, base8);
|
|
3320
6343
|
if (ctx.json) {
|
|
3321
6344
|
writeJson(ctx.io.stdout, res.body);
|
|
3322
6345
|
return 0;
|
|
@@ -3332,7 +6355,7 @@ async function runEntity(ctx, entity, argv) {
|
|
|
3332
6355
|
case "create": {
|
|
3333
6356
|
const { body, ok: ok3 } = entityBody(ctx, entity, rest, true);
|
|
3334
6357
|
if (!ok3) return 2;
|
|
3335
|
-
const res = await requestJson(ctx,
|
|
6358
|
+
const res = await requestJson(ctx, base8, { method: "POST", body });
|
|
3336
6359
|
if (ctx.json) {
|
|
3337
6360
|
writeJson(ctx.io.stdout, res.body);
|
|
3338
6361
|
return 0;
|
|
@@ -3348,7 +6371,7 @@ async function runEntity(ctx, entity, argv) {
|
|
|
3348
6371
|
}
|
|
3349
6372
|
const { body, ok: ok3 } = entityBody(ctx, entity, rest, false);
|
|
3350
6373
|
if (!ok3) return 2;
|
|
3351
|
-
const res = await requestJson(ctx, `${
|
|
6374
|
+
const res = await requestJson(ctx, `${base8}/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body });
|
|
3352
6375
|
if (ctx.json) {
|
|
3353
6376
|
writeJson(ctx.io.stdout, res.body);
|
|
3354
6377
|
return 0;
|
|
@@ -3366,7 +6389,7 @@ async function runEntity(ctx, entity, argv) {
|
|
|
3366
6389
|
writeLine(ctx.io.stderr, `Refusing to delete ${singular(entity)} ${positionals[0]} without --yes.`);
|
|
3367
6390
|
return 2;
|
|
3368
6391
|
}
|
|
3369
|
-
await requestJson(ctx, `${
|
|
6392
|
+
await requestJson(ctx, `${base8}/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
3370
6393
|
writeLine(ctx.io.stdout, `Deleted ${singular(entity)} ${positionals[0]}.`);
|
|
3371
6394
|
return 0;
|
|
3372
6395
|
}
|
|
@@ -3673,6 +6696,9 @@ var BYOK_HELP = `Usage:
|
|
|
3673
6696
|
openwop byok list [--json]
|
|
3674
6697
|
openwop byok set --ref <credentialRef> [--value <secret>] [--json]
|
|
3675
6698
|
openwop byok delete <credentialRef> [--yes]
|
|
6699
|
+
openwop byok ai-default [get] [--json]
|
|
6700
|
+
openwop byok ai-default set <credentialRef> [--json]
|
|
6701
|
+
openwop byok ai-default clear [--yes]
|
|
3676
6702
|
|
|
3677
6703
|
Bring-your-own-key secret store (host-side). The host holds the secret; the
|
|
3678
6704
|
credential-payload-redaction invariant (RFC 0046) means a value is NEVER
|
|
@@ -3696,7 +6722,7 @@ async function runByok(ctx, argv) {
|
|
|
3696
6722
|
write(ctx.io.stdout, BYOK_HELP);
|
|
3697
6723
|
return 0;
|
|
3698
6724
|
}
|
|
3699
|
-
const args = argv.slice(["list", "set", "delete"].includes(sub) ? 1 : 0);
|
|
6725
|
+
const args = argv.slice(["list", "set", "delete", "ai-default"].includes(sub) ? 1 : 0);
|
|
3700
6726
|
switch (sub) {
|
|
3701
6727
|
case "list":
|
|
3702
6728
|
return await byokList(ctx, args);
|
|
@@ -3704,6 +6730,8 @@ async function runByok(ctx, argv) {
|
|
|
3704
6730
|
return await byokSet(ctx, args);
|
|
3705
6731
|
case "delete":
|
|
3706
6732
|
return await byokDelete(ctx, args);
|
|
6733
|
+
case "ai-default":
|
|
6734
|
+
return await byokAiDefault(ctx, args);
|
|
3707
6735
|
default:
|
|
3708
6736
|
throw new CliError(`Unknown byok command: ${sub}
|
|
3709
6737
|
Run \`openwop byok --help\` for usage.`);
|
|
@@ -3766,6 +6794,39 @@ async function byokDelete(ctx, argv) {
|
|
|
3766
6794
|
writeLine(ctx.io.stdout, `Deleted secret ref ${positionals[0]}.`);
|
|
3767
6795
|
return 0;
|
|
3768
6796
|
}
|
|
6797
|
+
async function byokAiDefault(ctx, argv) {
|
|
6798
|
+
const action = argv[0] && !argv[0].startsWith("-") ? argv[0] : "get";
|
|
6799
|
+
const rest = ["get", "set", "clear"].includes(action) ? argv.slice(1) : argv;
|
|
6800
|
+
const { options, positionals } = parseOptions(rest, { bool: ["--help", "--yes"] });
|
|
6801
|
+
if (options.help) {
|
|
6802
|
+
write(ctx.io.stdout, BYOK_HELP);
|
|
6803
|
+
return 0;
|
|
6804
|
+
}
|
|
6805
|
+
const path = "/v1/host/sample/byok/ai-default";
|
|
6806
|
+
if (action === "set") {
|
|
6807
|
+
if (positionals.length !== 1) {
|
|
6808
|
+
write(ctx.io.stderr, "Usage: openwop byok ai-default set <credentialRef> [--json]\n");
|
|
6809
|
+
return 2;
|
|
6810
|
+
}
|
|
6811
|
+
const res2 = await requestJson(ctx, path, { method: "PUT", body: { credentialRef: positionals[0] } });
|
|
6812
|
+
if (ctx.json) writeJson(ctx.io.stdout, res2.body);
|
|
6813
|
+
else writeLine(ctx.io.stdout, `AI-default credential set to ${positionals[0]}.`);
|
|
6814
|
+
return 0;
|
|
6815
|
+
}
|
|
6816
|
+
if (action === "clear") {
|
|
6817
|
+
if (!options.yes) {
|
|
6818
|
+
writeLine(ctx.io.stderr, "Refusing to clear the AI-default binding without --yes.");
|
|
6819
|
+
return 2;
|
|
6820
|
+
}
|
|
6821
|
+
await requestJson(ctx, path, { method: "DELETE" });
|
|
6822
|
+
writeLine(ctx.io.stdout, "AI-default credential cleared.");
|
|
6823
|
+
return 0;
|
|
6824
|
+
}
|
|
6825
|
+
const res = await requestJson(ctx, path);
|
|
6826
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
6827
|
+
else writeLine(ctx.io.stdout, `AI-default credentialRef: ${res.body?.credentialRef ?? "(none)"}`);
|
|
6828
|
+
return 0;
|
|
6829
|
+
}
|
|
3769
6830
|
|
|
3770
6831
|
// src/cli/conformance.ts
|
|
3771
6832
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
@@ -3893,6 +6954,10 @@ var RUNS_HELP = `Usage:
|
|
|
3893
6954
|
openwop runs annotations <runId> [--json]
|
|
3894
6955
|
openwop runs annotate <runId> (--rating 1-5 | --label t | --correction t | --flag) [--note t] [--event-id id] [--node-id id]
|
|
3895
6956
|
openwop runs debug-bundle <runId> [--max-events n] [--out file] [--json]
|
|
6957
|
+
openwop runs fork <runId> [--mode replay|branch] [--from-sequence n] [--json]
|
|
6958
|
+
openwop runs diff <runId> --against <otherRunId> [--json]
|
|
6959
|
+
openwop runs delete <runId> [--yes]
|
|
6960
|
+
openwop runs bulk-cancel <runId...> [--reason text] [--json]
|
|
3896
6961
|
|
|
3897
6962
|
\`runs events\` polls GET /v1/runs/{runId}/events/poll (JSON, not SSE); --since N
|
|
3898
6963
|
returns events with sequence > N. \`runs annotate\` posts a review signal
|
|
@@ -3917,7 +6982,7 @@ Input parsing for \`runs create\`:
|
|
|
3917
6982
|
`;
|
|
3918
6983
|
async function runRuns(ctx, argv) {
|
|
3919
6984
|
const sub = argv[0] ?? "list";
|
|
3920
|
-
const args = argv.slice(["list", "create", "get", "cancel", "ancestry", "events", "annotations", "annotate", "debug-bundle"].includes(sub) ? 1 : 0);
|
|
6985
|
+
const args = argv.slice(["list", "create", "get", "cancel", "ancestry", "events", "annotations", "annotate", "debug-bundle", "fork", "diff", "delete", "bulk-cancel"].includes(sub) ? 1 : 0);
|
|
3921
6986
|
if (sub === "--help" || sub === "-h") {
|
|
3922
6987
|
write(ctx.io.stdout, RUNS_HELP);
|
|
3923
6988
|
return 0;
|
|
@@ -3941,6 +7006,14 @@ async function runRuns(ctx, argv) {
|
|
|
3941
7006
|
return runRunsAnnotate(ctx, args);
|
|
3942
7007
|
case "debug-bundle":
|
|
3943
7008
|
return runRunsDebugBundle(ctx, args);
|
|
7009
|
+
case "fork":
|
|
7010
|
+
return runRunsFork(ctx, args);
|
|
7011
|
+
case "diff":
|
|
7012
|
+
return runRunsDiff(ctx, args);
|
|
7013
|
+
case "delete":
|
|
7014
|
+
return runRunsDelete(ctx, args);
|
|
7015
|
+
case "bulk-cancel":
|
|
7016
|
+
return runRunsBulkCancel(ctx, args);
|
|
3944
7017
|
default:
|
|
3945
7018
|
throw new CliError(`Unknown runs command: ${sub}`);
|
|
3946
7019
|
}
|
|
@@ -4033,6 +7106,57 @@ async function runRunsCancel(ctx, argv) {
|
|
|
4033
7106
|
else writeLine(ctx.io.stdout, `Cancelled ${positionals[0]}`);
|
|
4034
7107
|
return 0;
|
|
4035
7108
|
}
|
|
7109
|
+
async function runRunsFork(ctx, argv) {
|
|
7110
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"], value: ["--mode", "--from-sequence"] });
|
|
7111
|
+
if (options.help || positionals.length !== 1) {
|
|
7112
|
+
write(ctx.io.stdout, "Usage: openwop runs fork <runId> [--mode replay|branch] [--from-sequence n] [--json]\n");
|
|
7113
|
+
return options.help ? 0 : 2;
|
|
7114
|
+
}
|
|
7115
|
+
const body = {};
|
|
7116
|
+
if (options.mode) body.mode = String(options.mode);
|
|
7117
|
+
if (options.fromSequence !== void 0) body.fromSeq = Number(options.fromSequence);
|
|
7118
|
+
const res = await requestJson(ctx, `/v1/runs/${encodeURIComponent(positionals[0])}:fork`, { method: "POST", body });
|
|
7119
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
7120
|
+
else writeLine(ctx.io.stdout, `Forked ${positionals[0]} \u2192 ${res.body?.runId ?? "(see --json)"}`);
|
|
7121
|
+
return 0;
|
|
7122
|
+
}
|
|
7123
|
+
async function runRunsDiff(ctx, argv) {
|
|
7124
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"], value: ["--against"] });
|
|
7125
|
+
if (options.help || positionals.length !== 1 || !options.against) {
|
|
7126
|
+
write(ctx.io.stdout, "Usage: openwop runs diff <runId> --against <otherRunId> [--json]\n");
|
|
7127
|
+
return options.help ? 0 : 2;
|
|
7128
|
+
}
|
|
7129
|
+
const res = await requestJson(
|
|
7130
|
+
ctx,
|
|
7131
|
+
`/v1/runs/${encodeURIComponent(positionals[0])}:diff?against=${encodeURIComponent(String(options.against))}`
|
|
7132
|
+
);
|
|
7133
|
+
writeJson(ctx.io.stdout, res.body);
|
|
7134
|
+
return 0;
|
|
7135
|
+
}
|
|
7136
|
+
async function runRunsDelete(ctx, argv) {
|
|
7137
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--yes"] });
|
|
7138
|
+
if (options.help || positionals.length !== 1) {
|
|
7139
|
+
write(ctx.io.stdout, "Usage: openwop runs delete <runId> [--yes]\n");
|
|
7140
|
+
return options.help ? 0 : 2;
|
|
7141
|
+
}
|
|
7142
|
+
if (!options.yes) throw new CliError(`Refusing to delete run ${positionals[0]} without --yes (this removes the run and its event log).`, 2);
|
|
7143
|
+
await requestJson(ctx, `/v1/runs/${encodeURIComponent(positionals[0])}`, { method: "DELETE" });
|
|
7144
|
+
writeLine(ctx.io.stdout, `Deleted ${positionals[0]}`);
|
|
7145
|
+
return 0;
|
|
7146
|
+
}
|
|
7147
|
+
async function runRunsBulkCancel(ctx, argv) {
|
|
7148
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help"], value: ["--reason"] });
|
|
7149
|
+
if (options.help || positionals.length === 0) {
|
|
7150
|
+
write(ctx.io.stdout, "Usage: openwop runs bulk-cancel <runId...> [--reason text] [--json]\n");
|
|
7151
|
+
return options.help ? 0 : 2;
|
|
7152
|
+
}
|
|
7153
|
+
const body = { runIds: positionals };
|
|
7154
|
+
if (options.reason) body.reason = options.reason;
|
|
7155
|
+
const res = await requestJson(ctx, "/v1/runs:bulk-cancel", { method: "POST", body });
|
|
7156
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
7157
|
+
else writeLine(ctx.io.stdout, `Requested cancel of ${positionals.length} run(s)`);
|
|
7158
|
+
return 0;
|
|
7159
|
+
}
|
|
4036
7160
|
async function runRunsEvents(ctx, argv) {
|
|
4037
7161
|
const { options, positionals } = parseOptions(argv, { bool: ["--help"], value: ["--since", "--limit"] });
|
|
4038
7162
|
if (options.help || positionals.length !== 1) {
|
|
@@ -5951,10 +9075,10 @@ async function runPacksPublish(ctx, argv) {
|
|
|
5951
9075
|
const sha = createHash("sha256").update(tgz).digest("hex");
|
|
5952
9076
|
const outDir = options.out ? resolvePath4(ctx.cwd, options.out) : join6(ctx.cwd, "dist", "packs");
|
|
5953
9077
|
mkdirSync5(outDir, { recursive: true });
|
|
5954
|
-
const
|
|
5955
|
-
const tgzPath = join6(outDir, `${
|
|
5956
|
-
const sigPath = join6(outDir, `${
|
|
5957
|
-
const manifestOut = join6(outDir, `${
|
|
9078
|
+
const base8 = `${name}-${version}`;
|
|
9079
|
+
const tgzPath = join6(outDir, `${base8}.tgz`);
|
|
9080
|
+
const sigPath = join6(outDir, `${base8}.sig`);
|
|
9081
|
+
const manifestOut = join6(outDir, `${base8}.manifest.json`);
|
|
5958
9082
|
writeFileSync7(tgzPath, tgz);
|
|
5959
9083
|
writeFileSync7(sigPath, sig);
|
|
5960
9084
|
writeFileSync7(manifestOut, JSON.stringify(signedManifest, null, 2) + "\n", "utf8");
|
|
@@ -6039,8 +9163,8 @@ function canonicalJsonStringify(value) {
|
|
|
6039
9163
|
return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalJsonStringify(value[k])).join(",") + "}";
|
|
6040
9164
|
}
|
|
6041
9165
|
function configHomeDir(env = process.env) {
|
|
6042
|
-
const
|
|
6043
|
-
return join6(
|
|
9166
|
+
const base8 = env.OPENWOP_CONFIG_HOME ? env.OPENWOP_CONFIG_HOME : homedir3();
|
|
9167
|
+
return join6(base8, ".openwop");
|
|
6044
9168
|
}
|
|
6045
9169
|
function extractPackJsonBytes(tarballBytes) {
|
|
6046
9170
|
const decompressed = gunzipSync(tarballBytes);
|
|
@@ -6221,6 +9345,8 @@ var GOVERNANCE_HELP = `Usage:
|
|
|
6221
9345
|
openwop governance policy [get] [--json]
|
|
6222
9346
|
openwop governance policy set [--provider-allowlist <a,b,...>] [--action <kind=policy>]... [--retention-graph-days <n>] [--retention-source-days <n>] [--json]
|
|
6223
9347
|
openwop governance audit [--prefix <p>] [--limit <n>] [--since <iso>] [--json]
|
|
9348
|
+
openwop governance media-budget [get] [--json]
|
|
9349
|
+
openwop governance media-budget set [--tts-chars <n>] [--stt-bytes <n>] [--json]
|
|
6224
9350
|
|
|
6225
9351
|
Tenant governance administration (ADR 0028, host extension \u2014 superadmin-gated).
|
|
6226
9352
|
Endpoints under ${GOV_BASE}:
|
|
@@ -6279,6 +9405,8 @@ async function runGovernance(ctx, argv) {
|
|
|
6279
9405
|
return await runGovernancePolicy(ctx, argv.slice(1));
|
|
6280
9406
|
case "audit":
|
|
6281
9407
|
return await runGovernanceAudit(ctx, argv.slice(1));
|
|
9408
|
+
case "media-budget":
|
|
9409
|
+
return await runGovernanceMediaBudget(ctx, argv.slice(1));
|
|
6282
9410
|
// `get`/`set` at the top level are policy ops — lets the `policy` group alias
|
|
6283
9411
|
// read naturally (`openwop policy set ...`) without a redundant `policy policy`.
|
|
6284
9412
|
case "get":
|
|
@@ -6428,6 +9556,30 @@ async function runGovernanceAudit(ctx, argv) {
|
|
|
6428
9556
|
writeLine(ctx.io.stdout, formatTable(rows, ["timestamp", "principal", "action", "resource", "outcome"]));
|
|
6429
9557
|
return 0;
|
|
6430
9558
|
}
|
|
9559
|
+
async function runGovernanceMediaBudget(ctx, argv) {
|
|
9560
|
+
const action = argv[0] === "set" ? "set" : "get";
|
|
9561
|
+
const rest = argv[0] === "get" || argv[0] === "set" ? argv.slice(1) : argv;
|
|
9562
|
+
const path = `${GOV_BASE}/media-budget`;
|
|
9563
|
+
if (action === "set") {
|
|
9564
|
+
const { options } = parseOptions(rest, { value: ["--tts-chars", "--stt-bytes"] });
|
|
9565
|
+
const body = {};
|
|
9566
|
+
if (options.ttsChars !== void 0) body.ttsChars = Number(options.ttsChars);
|
|
9567
|
+
if (options.sttBytes !== void 0) body.sttBytes = Number(options.sttBytes);
|
|
9568
|
+
const res2 = await requestJson(ctx, path, { method: "PUT", body });
|
|
9569
|
+
if (ctx.json) writeJson(ctx.io.stdout, res2.body);
|
|
9570
|
+
else writeLine(ctx.io.stdout, "Media budget updated.");
|
|
9571
|
+
return 0;
|
|
9572
|
+
}
|
|
9573
|
+
const res = await requestJson(ctx, path);
|
|
9574
|
+
if (ctx.json) {
|
|
9575
|
+
writeJson(ctx.io.stdout, res.body);
|
|
9576
|
+
return 0;
|
|
9577
|
+
}
|
|
9578
|
+
const b = res.body ?? {};
|
|
9579
|
+
writeLine(ctx.io.stdout, `override: ${b.override ? JSON.stringify(b.override) : "(none \u2014 using host defaults)"}`);
|
|
9580
|
+
writeLine(ctx.io.stdout, `effective: tts ${b.budgets?.ttsChars ?? "?"} chars/day, stt ${b.budgets?.sttBytes ?? "?"} bytes/day`);
|
|
9581
|
+
return 0;
|
|
9582
|
+
}
|
|
6431
9583
|
|
|
6432
9584
|
// src/cli/demo.ts
|
|
6433
9585
|
import { spawn as spawn3, spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -8001,7 +11153,7 @@ Examples:
|
|
|
8001
11153
|
openwop profiles pin r_123
|
|
8002
11154
|
openwop profiles activity --limit 10 --status failed
|
|
8003
11155
|
`;
|
|
8004
|
-
var
|
|
11156
|
+
var BASE10 = "/v1/host/sample/profiles";
|
|
8005
11157
|
async function profilesRequest(ctx, path, options, notFoundMsg) {
|
|
8006
11158
|
try {
|
|
8007
11159
|
return await requestJson(ctx, path, options);
|
|
@@ -8096,7 +11248,7 @@ async function runList2(ctx, argv) {
|
|
|
8096
11248
|
write(ctx.io.stdout, "Usage: openwop profiles list [--json]\n");
|
|
8097
11249
|
return 0;
|
|
8098
11250
|
}
|
|
8099
|
-
const res = await profilesRequest(ctx,
|
|
11251
|
+
const res = await profilesRequest(ctx, BASE10);
|
|
8100
11252
|
if (ctx.json) {
|
|
8101
11253
|
writeJson(ctx.io.stdout, res.body);
|
|
8102
11254
|
return 0;
|
|
@@ -8122,7 +11274,7 @@ async function runGet3(ctx, argv) {
|
|
|
8122
11274
|
write(ctx.io.stdout, "Usage: openwop profiles get [<userId>] [--json]\n");
|
|
8123
11275
|
return options.help ? 0 : 2;
|
|
8124
11276
|
}
|
|
8125
|
-
const path = positionals.length === 1 ? `${
|
|
11277
|
+
const path = positionals.length === 1 ? `${BASE10}/${encodeURIComponent(positionals[0])}` : `${BASE10}/me`;
|
|
8126
11278
|
const res = await profilesRequest(ctx, path, void 0, `Profile not found: ${positionals[0] ?? "me"}`);
|
|
8127
11279
|
if (ctx.json) {
|
|
8128
11280
|
writeJson(ctx.io.stdout, res.body);
|
|
@@ -8141,7 +11293,7 @@ async function runActivity(ctx, argv) {
|
|
|
8141
11293
|
if (options.limit !== void 0) qs.set("limit", String(options.limit));
|
|
8142
11294
|
if (options.status !== void 0) qs.set("status", String(options.status));
|
|
8143
11295
|
const q = qs.toString();
|
|
8144
|
-
const res = await profilesRequest(ctx, `${
|
|
11296
|
+
const res = await profilesRequest(ctx, `${BASE10}/me/activity${q ? `?${q}` : ""}`);
|
|
8145
11297
|
if (ctx.json) {
|
|
8146
11298
|
writeJson(ctx.io.stdout, res.body);
|
|
8147
11299
|
return 0;
|
|
@@ -8193,7 +11345,7 @@ async function runEdit(ctx, argv) {
|
|
|
8193
11345
|
}
|
|
8194
11346
|
if (Object.keys(avail).length) body.availability = avail;
|
|
8195
11347
|
if (Object.keys(body).length === 0) throw new CliError("Nothing to edit \u2014 pass at least one field.", 2);
|
|
8196
|
-
const res = await profilesRequest(ctx, `${
|
|
11348
|
+
const res = await profilesRequest(ctx, `${BASE10}/me`, { method: "PATCH", body });
|
|
8197
11349
|
if (ctx.json) {
|
|
8198
11350
|
writeJson(ctx.io.stdout, res.body);
|
|
8199
11351
|
return 0;
|
|
@@ -8214,12 +11366,12 @@ async function runSkillsSet(ctx, argv) {
|
|
|
8214
11366
|
const eq = e.lastIndexOf("=");
|
|
8215
11367
|
if (eq < 0) throw new CliError(`--skill must be name=proficiency (got '${e}')`, 2);
|
|
8216
11368
|
const name = e.slice(0, eq).trim();
|
|
8217
|
-
const
|
|
11369
|
+
const prof2 = Number(e.slice(eq + 1));
|
|
8218
11370
|
if (!name) throw new CliError(`--skill name is required (got '${e}')`, 2);
|
|
8219
|
-
if (!Number.isFinite(
|
|
8220
|
-
return { name, proficiency:
|
|
11371
|
+
if (!Number.isFinite(prof2) || prof2 < 1 || prof2 > 5) throw new CliError(`--skill proficiency must be 1..5 (got '${e}')`, 2);
|
|
11372
|
+
return { name, proficiency: prof2 };
|
|
8221
11373
|
});
|
|
8222
|
-
const res = await profilesRequest(ctx, `${
|
|
11374
|
+
const res = await profilesRequest(ctx, `${BASE10}/me/skills`, { method: "PUT", body: { skills } });
|
|
8223
11375
|
if (ctx.json) {
|
|
8224
11376
|
writeJson(ctx.io.stdout, res.body);
|
|
8225
11377
|
return 0;
|
|
@@ -8234,7 +11386,7 @@ async function runPortfolioAdd(ctx, argv) {
|
|
|
8234
11386
|
write(ctx.io.stdout, "Usage: openwop profiles portfolio add --token <mediaToken> [--json]\n");
|
|
8235
11387
|
return options.help ? 0 : 2;
|
|
8236
11388
|
}
|
|
8237
|
-
const res = await profilesRequest(ctx, `${
|
|
11389
|
+
const res = await profilesRequest(ctx, `${BASE10}/me/portfolio`, { method: "POST", body: { token: options.token } });
|
|
8238
11390
|
if (ctx.json) {
|
|
8239
11391
|
writeJson(ctx.io.stdout, res.body);
|
|
8240
11392
|
return 0;
|
|
@@ -8248,7 +11400,7 @@ async function runPortfolioRemove(ctx, argv) {
|
|
|
8248
11400
|
write(ctx.io.stdout, "Usage: openwop profiles portfolio remove <mediaToken> [--json]\n");
|
|
8249
11401
|
return options.help ? 0 : 2;
|
|
8250
11402
|
}
|
|
8251
|
-
const res = await profilesRequest(ctx, `${
|
|
11403
|
+
const res = await profilesRequest(ctx, `${BASE10}/me/portfolio/${encodeURIComponent(positionals[0])}`, { method: "DELETE" }, `Portfolio asset not found: ${positionals[0]}`);
|
|
8252
11404
|
if (ctx.json) {
|
|
8253
11405
|
writeJson(ctx.io.stdout, res.body);
|
|
8254
11406
|
return 0;
|
|
@@ -8263,7 +11415,7 @@ async function runPin(ctx, argv, pinned) {
|
|
|
8263
11415
|
`);
|
|
8264
11416
|
return options.help ? 0 : 2;
|
|
8265
11417
|
}
|
|
8266
|
-
const path = `${
|
|
11418
|
+
const path = `${BASE10}/me/pinned-agents/${encodeURIComponent(positionals[0])}`;
|
|
8267
11419
|
const res = await profilesRequest(ctx, path, { method: pinned ? "PUT" : "DELETE" }, `Agent not found: ${positionals[0]}`);
|
|
8268
11420
|
if (ctx.json) {
|
|
8269
11421
|
writeJson(ctx.io.stdout, res.body);
|
|
@@ -8280,7 +11432,7 @@ async function runEndorse(ctx, argv, add) {
|
|
|
8280
11432
|
return options.help ? 0 : 2;
|
|
8281
11433
|
}
|
|
8282
11434
|
const [userId, skill] = positionals;
|
|
8283
|
-
const path = `${
|
|
11435
|
+
const path = `${BASE10}/${encodeURIComponent(userId)}/skills/${encodeURIComponent(skill)}/endorse`;
|
|
8284
11436
|
const res = await profilesRequest(ctx, path, { method: add ? "POST" : "DELETE" }, `Profile or skill not found: ${userId} / ${skill}`);
|
|
8285
11437
|
if (ctx.json) {
|
|
8286
11438
|
writeJson(ctx.io.stdout, res.body);
|
|
@@ -8290,6 +11442,115 @@ async function runEndorse(ctx, argv, add) {
|
|
|
8290
11442
|
return 0;
|
|
8291
11443
|
}
|
|
8292
11444
|
|
|
11445
|
+
// src/cli/completion.ts
|
|
11446
|
+
var COMPLETION_HELP = `Usage:
|
|
11447
|
+
openwop completion <bash|zsh|fish>
|
|
11448
|
+
|
|
11449
|
+
Emit a shell-completion script for openwop's top-level commands. Install it:
|
|
11450
|
+
bash: openwop completion bash > \${BASH_COMPLETION_USER_DIR:-~/.local/share/bash-completion}/completions/openwop
|
|
11451
|
+
zsh: openwop completion zsh > "\${fpath[1]}/_openwop" # then: autoload -U compinit && compinit
|
|
11452
|
+
fish: openwop completion fish > ~/.config/fish/completions/openwop.fish
|
|
11453
|
+
`;
|
|
11454
|
+
function bashScript(cmds) {
|
|
11455
|
+
return `# openwop bash completion \u2014 source this or drop it in your bash-completion dir.
|
|
11456
|
+
_openwop() {
|
|
11457
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
11458
|
+
if [ "\${COMP_CWORD}" -eq 1 ]; then
|
|
11459
|
+
COMPREPLY=( $(compgen -W "${cmds}" -- "\${cur}") )
|
|
11460
|
+
return 0
|
|
11461
|
+
fi
|
|
11462
|
+
}
|
|
11463
|
+
complete -F _openwop openwop
|
|
11464
|
+
`;
|
|
11465
|
+
}
|
|
11466
|
+
function zshScript(cmds) {
|
|
11467
|
+
return `#compdef openwop
|
|
11468
|
+
# openwop zsh completion \u2014 install as _openwop on your fpath.
|
|
11469
|
+
_openwop() {
|
|
11470
|
+
local -a _cmds
|
|
11471
|
+
_cmds=(${cmds})
|
|
11472
|
+
_arguments '1: :(\${_cmds})' '*::arg:->args'
|
|
11473
|
+
}
|
|
11474
|
+
_openwop "$@"
|
|
11475
|
+
`;
|
|
11476
|
+
}
|
|
11477
|
+
function fishScript(commands) {
|
|
11478
|
+
const lines = commands.map((c) => `complete -c openwop -n __fish_use_subcommand -a ${c}`);
|
|
11479
|
+
return `# openwop fish completion \u2014 install as ~/.config/fish/completions/openwop.fish
|
|
11480
|
+
${lines.join("\n")}
|
|
11481
|
+
`;
|
|
11482
|
+
}
|
|
11483
|
+
function runCompletion(ctx, argv, commands) {
|
|
11484
|
+
const shell = argv[0];
|
|
11485
|
+
if (shell === "--help" || shell === "-h") {
|
|
11486
|
+
write(ctx.io.stdout, COMPLETION_HELP);
|
|
11487
|
+
return 0;
|
|
11488
|
+
}
|
|
11489
|
+
if (!shell) {
|
|
11490
|
+
write(ctx.io.stderr, COMPLETION_HELP);
|
|
11491
|
+
return 2;
|
|
11492
|
+
}
|
|
11493
|
+
const cmds = commands.join(" ");
|
|
11494
|
+
switch (shell) {
|
|
11495
|
+
case "bash":
|
|
11496
|
+
write(ctx.io.stdout, bashScript(cmds));
|
|
11497
|
+
return 0;
|
|
11498
|
+
case "zsh":
|
|
11499
|
+
write(ctx.io.stdout, zshScript(cmds));
|
|
11500
|
+
return 0;
|
|
11501
|
+
case "fish":
|
|
11502
|
+
write(ctx.io.stdout, fishScript(commands));
|
|
11503
|
+
return 0;
|
|
11504
|
+
default:
|
|
11505
|
+
throw new CliError(`Unsupported shell: ${shell}. Use one of: bash | zsh | fish.`, 2);
|
|
11506
|
+
}
|
|
11507
|
+
}
|
|
11508
|
+
|
|
11509
|
+
// src/cli/upgrade.ts
|
|
11510
|
+
var REGISTRY = "https://registry.npmjs.org/@openwop/cli/latest";
|
|
11511
|
+
var UPGRADE_HELP = `Usage:
|
|
11512
|
+
openwop upgrade [--json]
|
|
11513
|
+
|
|
11514
|
+
Check npm for a newer @openwop/cli and print how to update. Report-only \u2014 it never
|
|
11515
|
+
installs. Update the global CLI yourself with:
|
|
11516
|
+
npm install -g @openwop/cli@latest
|
|
11517
|
+
`;
|
|
11518
|
+
function semverGt(a, b) {
|
|
11519
|
+
const pa = a.split(".").map((n) => Number(n) || 0);
|
|
11520
|
+
const pb = b.split(".").map((n) => Number(n) || 0);
|
|
11521
|
+
for (let i = 0; i < 3; i++) {
|
|
11522
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
11523
|
+
if (d !== 0) return d > 0;
|
|
11524
|
+
}
|
|
11525
|
+
return false;
|
|
11526
|
+
}
|
|
11527
|
+
async function runUpgrade(ctx, argv) {
|
|
11528
|
+
if (argv[0] === "--help" || argv[0] === "-h") {
|
|
11529
|
+
write(ctx.io.stdout, UPGRADE_HELP);
|
|
11530
|
+
return 0;
|
|
11531
|
+
}
|
|
11532
|
+
let latest = "";
|
|
11533
|
+
try {
|
|
11534
|
+
const res = await ctx.fetchImpl(REGISTRY, { headers: { accept: "application/json" } });
|
|
11535
|
+
if (!res.ok) throw new Error(`registry HTTP ${res.status}`);
|
|
11536
|
+
latest = (await res.json()).version ?? "";
|
|
11537
|
+
} catch (err) {
|
|
11538
|
+
throw new CliError(`Could not reach the npm registry to check for updates: ${err.message}`, 1);
|
|
11539
|
+
}
|
|
11540
|
+
const behind = latest !== "" && semverGt(latest, VERSION);
|
|
11541
|
+
if (ctx.json) {
|
|
11542
|
+
writeJson(ctx.io.stdout, { current: VERSION, latest, upToDate: !behind });
|
|
11543
|
+
return 0;
|
|
11544
|
+
}
|
|
11545
|
+
if (!behind) {
|
|
11546
|
+
writeLine(ctx.io.stdout, `@openwop/cli is up to date (${VERSION}).`);
|
|
11547
|
+
return 0;
|
|
11548
|
+
}
|
|
11549
|
+
writeLine(ctx.io.stdout, `A newer @openwop/cli is available: ${VERSION} \u2192 ${latest}`);
|
|
11550
|
+
writeLine(ctx.io.stdout, "Update with: npm install -g @openwop/cli@latest");
|
|
11551
|
+
return 0;
|
|
11552
|
+
}
|
|
11553
|
+
|
|
8293
11554
|
// src/cli/toggles.ts
|
|
8294
11555
|
var TOGGLES_BASE = "/v1/host/sample/feature-toggles";
|
|
8295
11556
|
var TOGGLES_HELP = `Usage:
|
|
@@ -8997,7 +12258,7 @@ Examples:
|
|
|
8997
12258
|
openwop auth saml validate --idp-url http://localhost:9100/idp --variant valid
|
|
8998
12259
|
openwop auth scim provision --op create-user --user-name jo@acme.com --email jo@acme.com
|
|
8999
12260
|
`;
|
|
9000
|
-
var
|
|
12261
|
+
var BASE11 = "/v1/host/sample/auth";
|
|
9001
12262
|
var SECRET_KEY2 = /secret|token|password|private[-_]?key|client[-_]?secret|api[-_]?key|credential|bearer|assertion|certificate|cert|pem/i;
|
|
9002
12263
|
function redact2(value) {
|
|
9003
12264
|
if (Array.isArray(value)) return value.map(redact2);
|
|
@@ -9055,7 +12316,7 @@ async function runStatus(ctx, argv) {
|
|
|
9055
12316
|
const profiles = disc.ok && Array.isArray(disc.body?.capabilities?.auth?.profiles) ? disc.body.capabilities.auth.profiles : [];
|
|
9056
12317
|
const samlAdvertised = profiles.includes("openwop-auth-saml");
|
|
9057
12318
|
const scimAdvertised = profiles.includes("openwop-auth-scim");
|
|
9058
|
-
const metaReachable = samlAdvertised ? (await probeEndpoint(ctx, `${
|
|
12319
|
+
const metaReachable = samlAdvertised ? (await probeEndpoint(ctx, `${BASE11}/saml/sso/metadata`)).ok : false;
|
|
9059
12320
|
if (ctx.json) {
|
|
9060
12321
|
writeJson(ctx.io.stdout, {
|
|
9061
12322
|
authProfilesAdvertised: profiles,
|
|
@@ -9080,7 +12341,7 @@ async function runSamlMetadata(ctx, argv) {
|
|
|
9080
12341
|
write(ctx.io.stdout, "Usage: openwop auth saml metadata [--json]\n");
|
|
9081
12342
|
return 0;
|
|
9082
12343
|
}
|
|
9083
|
-
const res = await authRequest(ctx, `${
|
|
12344
|
+
const res = await authRequest(ctx, `${BASE11}/saml/sso/metadata`, { auth: false }, "SAML SSO is not configured on this host (set OPENWOP_SAML_*).");
|
|
9084
12345
|
const xml = typeof res.body?.raw === "string" ? res.body.raw : typeof res.body === "string" ? res.body : JSON.stringify(res.body);
|
|
9085
12346
|
if (ctx.json) {
|
|
9086
12347
|
writeJson(ctx.io.stdout, { metadata: xml });
|
|
@@ -9097,8 +12358,8 @@ async function runSamlLoginUrl(ctx, argv) {
|
|
|
9097
12358
|
return 0;
|
|
9098
12359
|
}
|
|
9099
12360
|
const returnTo = options.returnTo ?? "/";
|
|
9100
|
-
const
|
|
9101
|
-
const url = new URL(`${
|
|
12361
|
+
const base8 = ctx.baseUrl.endsWith("/") ? ctx.baseUrl : `${ctx.baseUrl}/`;
|
|
12362
|
+
const url = new URL(`${BASE11}/saml/sso/login?returnTo=${encodeURIComponent(returnTo)}`.replace(/^\//, ""), base8);
|
|
9102
12363
|
const res = await ctx.fetchImpl(url, { method: "GET", redirect: "manual" });
|
|
9103
12364
|
if (res.status === 404) {
|
|
9104
12365
|
throw new CliError("SAML SSO is not configured on this host (set OPENWOP_SAML_*).", 1);
|
|
@@ -9123,7 +12384,7 @@ async function runSamlValidate(ctx, argv) {
|
|
|
9123
12384
|
const body = { idpUrl: options.idpUrl, variant: options.variant };
|
|
9124
12385
|
let res;
|
|
9125
12386
|
try {
|
|
9126
|
-
res = await requestJson(ctx, `${
|
|
12387
|
+
res = await requestJson(ctx, `${BASE11}/saml/validate`, { method: "POST", body });
|
|
9127
12388
|
} catch (err) {
|
|
9128
12389
|
if (err instanceof HttpError && err.status === 404) {
|
|
9129
12390
|
throw new CliError("SAML validation seam not configured on this host (set OPENWOP_TEST_SAML_IDP_URL).", 1);
|
|
@@ -9166,7 +12427,7 @@ async function runScimProvision(ctx, argv) {
|
|
|
9166
12427
|
if (options.displayName !== void 0) user.displayName = options.displayName;
|
|
9167
12428
|
const body = { op: options.op, user };
|
|
9168
12429
|
if (options.group !== void 0) body.group = options.group;
|
|
9169
|
-
const res = await authRequest(ctx, `${
|
|
12430
|
+
const res = await authRequest(ctx, `${BASE11}/scim/provision`, { method: "POST", body }, "SCIM provisioning seam not configured on this host (set OPENWOP_TEST_SCIM_URL).");
|
|
9170
12431
|
const safe = redact2(res.body ?? {});
|
|
9171
12432
|
if (ctx.json) {
|
|
9172
12433
|
writeJson(ctx.io.stdout, safe);
|
|
@@ -10171,6 +13432,8 @@ var TRIGGERS_HELP = `Usage:
|
|
|
10171
13432
|
openwop triggers register --source <webhook|email|form> --workflow <id> [--dedup] [--verification <mode>] [--json]
|
|
10172
13433
|
openwop triggers list [--state <s>] [--source <s>] [--json]
|
|
10173
13434
|
openwop triggers get <subscriptionId> [--json]
|
|
13435
|
+
openwop triggers update <subscriptionId> --state <active|paused> [--json]
|
|
13436
|
+
openwop triggers ingest <subscriptionId> [--event-json '{...}'] [--json]
|
|
10174
13437
|
|
|
10175
13438
|
External-event trigger subscriptions (RFC 0099). A subscription binds an external
|
|
10176
13439
|
SOURCE (webhook / email / form) to a WORKFLOW the caller can start; a verified
|
|
@@ -10209,7 +13472,7 @@ Examples:
|
|
|
10209
13472
|
openwop triggers list --state active
|
|
10210
13473
|
openwop triggers get sub_123 --json
|
|
10211
13474
|
`;
|
|
10212
|
-
var SUBCOMMANDS5 = ["register", "list", "get"];
|
|
13475
|
+
var SUBCOMMANDS5 = ["register", "list", "get", "update", "ingest"];
|
|
10213
13476
|
async function runTriggers(ctx, argv) {
|
|
10214
13477
|
const sub = argv[0] ?? "list";
|
|
10215
13478
|
if (sub === "--help" || sub === "-h") {
|
|
@@ -10224,11 +13487,45 @@ async function runTriggers(ctx, argv) {
|
|
|
10224
13487
|
return await runList3(ctx, args);
|
|
10225
13488
|
case "get":
|
|
10226
13489
|
return await runGet4(ctx, args);
|
|
13490
|
+
case "update":
|
|
13491
|
+
return await runTriggersUpdate(ctx, args);
|
|
13492
|
+
case "ingest":
|
|
13493
|
+
return await runTriggersIngest(ctx, args);
|
|
10227
13494
|
default:
|
|
10228
13495
|
throw new CliError(`Unknown triggers command: ${sub}
|
|
10229
13496
|
Run \`openwop triggers --help\` for usage.`);
|
|
10230
13497
|
}
|
|
10231
13498
|
}
|
|
13499
|
+
async function runTriggersUpdate(ctx, argv) {
|
|
13500
|
+
const { options, positionals } = parseOptions(argv, { value: ["--state"] });
|
|
13501
|
+
if (positionals.length !== 1 || !options.state) {
|
|
13502
|
+
write(ctx.io.stderr, "Usage: openwop triggers update <subscriptionId> --state <active|paused> [--json]\n");
|
|
13503
|
+
return 2;
|
|
13504
|
+
}
|
|
13505
|
+
const res = await requestJson(ctx, `/v1/trigger-subscriptions/${encodeURIComponent(positionals[0])}`, { method: "PATCH", body: { state: options.state } });
|
|
13506
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
13507
|
+
else writeLine(ctx.io.stdout, `Updated ${positionals[0]} \u2192 state ${options.state}.`);
|
|
13508
|
+
return 0;
|
|
13509
|
+
}
|
|
13510
|
+
async function runTriggersIngest(ctx, argv) {
|
|
13511
|
+
const { options, positionals } = parseOptions(argv, { value: ["--event-json"] });
|
|
13512
|
+
if (positionals.length !== 1) {
|
|
13513
|
+
write(ctx.io.stderr, "Usage: openwop triggers ingest <subscriptionId> [--event-json '{...}'] [--json]\n");
|
|
13514
|
+
return 2;
|
|
13515
|
+
}
|
|
13516
|
+
let event = {};
|
|
13517
|
+
if (options.eventJson) {
|
|
13518
|
+
try {
|
|
13519
|
+
event = JSON.parse(String(options.eventJson));
|
|
13520
|
+
} catch {
|
|
13521
|
+
throw new CliError("--event-json must be valid JSON.", 2);
|
|
13522
|
+
}
|
|
13523
|
+
}
|
|
13524
|
+
const res = await requestJson(ctx, `/v1/trigger-subscriptions/${encodeURIComponent(positionals[0])}/ingest`, { method: "POST", body: event });
|
|
13525
|
+
if (ctx.json) writeJson(ctx.io.stdout, res.body);
|
|
13526
|
+
else writeLine(ctx.io.stdout, `Ingested event for ${positionals[0]}.`);
|
|
13527
|
+
return 0;
|
|
13528
|
+
}
|
|
10232
13529
|
function exitForState3(state) {
|
|
10233
13530
|
if (state === "active") return 0;
|
|
10234
13531
|
if (state === "paused") return 3;
|
|
@@ -10549,13 +13846,18 @@ async function runCli(argv, options = {}) {
|
|
|
10549
13846
|
throw new CliError("This CLI requires a Node runtime with global fetch (Node 20+).");
|
|
10550
13847
|
}
|
|
10551
13848
|
const parsed = extractGlobalOptions(argv, env);
|
|
13849
|
+
const profile = parsed.globals.profile ?? env.OPENWOP_PROFILE;
|
|
13850
|
+
const savedConfig = readConfigSafe(configPathFor(profile, env)) ?? {};
|
|
10552
13851
|
const ctx = {
|
|
10553
13852
|
cwd,
|
|
10554
13853
|
env,
|
|
10555
13854
|
io,
|
|
10556
13855
|
fetchImpl,
|
|
10557
|
-
|
|
10558
|
-
|
|
13856
|
+
profile,
|
|
13857
|
+
baseUrl: normalizeBaseUrl(
|
|
13858
|
+
parsed.globals.baseUrl ?? env.OPENWOP_BASE_URL ?? savedConfig?.host?.baseUrl ?? DEFAULT_BASE_URL
|
|
13859
|
+
),
|
|
13860
|
+
apiKey: parsed.globals.apiKey ?? env.OPENWOP_API_KEY ?? savedConfig?.host?.apiKey,
|
|
10559
13861
|
json: parsed.globals.json,
|
|
10560
13862
|
quiet: parsed.globals.quiet,
|
|
10561
13863
|
verbose: parsed.globals.verbose,
|
|
@@ -10631,6 +13933,60 @@ async function runCli(argv, options = {}) {
|
|
|
10631
13933
|
return await runOrgs(ctx, commandArgs);
|
|
10632
13934
|
case "workspace":
|
|
10633
13935
|
return await runWorkspace(ctx, commandArgs);
|
|
13936
|
+
case "brand":
|
|
13937
|
+
return await runBrand(ctx, commandArgs);
|
|
13938
|
+
case "crm":
|
|
13939
|
+
return await runCrm(ctx, commandArgs);
|
|
13940
|
+
case "csm":
|
|
13941
|
+
return await runCsm(ctx, commandArgs);
|
|
13942
|
+
case "comments":
|
|
13943
|
+
return await runComments(ctx, commandArgs);
|
|
13944
|
+
case "sharing":
|
|
13945
|
+
return await runSharing(ctx, commandArgs);
|
|
13946
|
+
case "forms":
|
|
13947
|
+
return await runForms(ctx, commandArgs);
|
|
13948
|
+
case "email":
|
|
13949
|
+
return await runEmail(ctx, commandArgs);
|
|
13950
|
+
case "chat-widget":
|
|
13951
|
+
return await runChatWidget(ctx, commandArgs);
|
|
13952
|
+
case "marketplace":
|
|
13953
|
+
return await runMarketplace(ctx, commandArgs);
|
|
13954
|
+
case "kb":
|
|
13955
|
+
return await runKb(ctx, commandArgs);
|
|
13956
|
+
case "cms":
|
|
13957
|
+
return await runCms(ctx, commandArgs);
|
|
13958
|
+
case "documents":
|
|
13959
|
+
return await runDocuments(ctx, commandArgs);
|
|
13960
|
+
case "projects":
|
|
13961
|
+
return await runProjects(ctx, commandArgs);
|
|
13962
|
+
case "reviews":
|
|
13963
|
+
return await runReviews(ctx, commandArgs);
|
|
13964
|
+
case "workspaces":
|
|
13965
|
+
return await runWorkspaces(ctx, commandArgs);
|
|
13966
|
+
case "agent-profile":
|
|
13967
|
+
return await runAgentProfile(ctx, commandArgs);
|
|
13968
|
+
case "agent-allowlists":
|
|
13969
|
+
return await runAgentAllowlists(ctx, commandArgs);
|
|
13970
|
+
case "agent-packs":
|
|
13971
|
+
return await runAgentPacks(ctx, commandArgs);
|
|
13972
|
+
case "agent-ops":
|
|
13973
|
+
return await runAgentOps(ctx, commandArgs);
|
|
13974
|
+
case "evals":
|
|
13975
|
+
return await runEvals(ctx, commandArgs);
|
|
13976
|
+
case "twin":
|
|
13977
|
+
return await runTwin(ctx, commandArgs);
|
|
13978
|
+
case "strategy":
|
|
13979
|
+
return await runStrategy(ctx, commandArgs);
|
|
13980
|
+
case "advisors":
|
|
13981
|
+
return await runAdvisors(ctx, commandArgs);
|
|
13982
|
+
case "campaigns-orchestration":
|
|
13983
|
+
return await runCampaignsOrch(ctx, commandArgs);
|
|
13984
|
+
case "notebooks":
|
|
13985
|
+
return await runNotebooks(ctx, commandArgs);
|
|
13986
|
+
case "podcasts":
|
|
13987
|
+
return await runPodcasts(ctx, commandArgs);
|
|
13988
|
+
case "priority-matrix":
|
|
13989
|
+
return await runPriorityMatrix(ctx, commandArgs);
|
|
10634
13990
|
case "byok":
|
|
10635
13991
|
return await runByok(ctx, commandArgs);
|
|
10636
13992
|
case "config":
|
|
@@ -10699,6 +14055,10 @@ async function runCli(argv, options = {}) {
|
|
|
10699
14055
|
return await runTriggers(ctx, commandArgs);
|
|
10700
14056
|
case "a2a":
|
|
10701
14057
|
return await runA2a(ctx, commandArgs);
|
|
14058
|
+
case "completion":
|
|
14059
|
+
return runCompletion(ctx, commandArgs, COMMAND_NAMES);
|
|
14060
|
+
case "upgrade":
|
|
14061
|
+
return await runUpgrade(ctx, commandArgs);
|
|
10702
14062
|
default:
|
|
10703
14063
|
throw new CliError(`Unknown command: ${command}
|
|
10704
14064
|
Run \`openwop --help\` for usage.`);
|
|
@@ -10712,6 +14072,7 @@ Run \`openwop --help\` for usage.`);
|
|
|
10712
14072
|
const bodyMessage = err.body && typeof err.body === "object" && typeof err.body.message === "string" ? `: ${err.body.message}` : "";
|
|
10713
14073
|
writeLine(io.stderr, `openwop: HTTP ${err.status}${bodyMessage}`);
|
|
10714
14074
|
if (options.debugErrors) writeLine(io.stderr, String(err.stack ?? err));
|
|
14075
|
+
if (err.status === 401 || err.status === 403) return 4;
|
|
10715
14076
|
return err.status >= 500 ? 1 : 2;
|
|
10716
14077
|
}
|
|
10717
14078
|
writeLine(io.stderr, `openwop: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -10719,78 +14080,108 @@ Run \`openwop --help\` for usage.`);
|
|
|
10719
14080
|
return 1;
|
|
10720
14081
|
}
|
|
10721
14082
|
}
|
|
14083
|
+
var HELP_MAP = {
|
|
14084
|
+
demo: DEMO_HELP,
|
|
14085
|
+
runs: RUNS_HELP,
|
|
14086
|
+
run: RUNS_HELP,
|
|
14087
|
+
chat: CHAT_HELP,
|
|
14088
|
+
workflows: WORKFLOWS_HELP,
|
|
14089
|
+
workflow: WORKFLOWS_HELP,
|
|
14090
|
+
catalog: CATALOG_HELP,
|
|
14091
|
+
packs: PACKS_HELP,
|
|
14092
|
+
pack: PACKS_HELP,
|
|
14093
|
+
onboard: ONBOARD_HELP,
|
|
14094
|
+
providers: PROVIDERS_HELP,
|
|
14095
|
+
provider: PROVIDERS_HELP,
|
|
14096
|
+
agents: AGENTS_HELP,
|
|
14097
|
+
agent: AGENTS_HELP,
|
|
14098
|
+
roster: ROSTER_HELP,
|
|
14099
|
+
"org-chart": ORG_CHART_HELP,
|
|
14100
|
+
orgchart: ORG_CHART_HELP,
|
|
14101
|
+
kanban: KANBAN_HELP,
|
|
14102
|
+
boards: KANBAN_HELP,
|
|
14103
|
+
orgs: ORGS_HELP,
|
|
14104
|
+
org: ORGS_HELP,
|
|
14105
|
+
workspace: WORKSPACE_HELP,
|
|
14106
|
+
byok: BYOK_HELP,
|
|
14107
|
+
brand: BRAND_HELP,
|
|
14108
|
+
crm: CRM_HELP,
|
|
14109
|
+
csm: CSM_HELP,
|
|
14110
|
+
comments: COMMENTS_HELP,
|
|
14111
|
+
sharing: SHARING_HELP,
|
|
14112
|
+
forms: FORMS_HELP,
|
|
14113
|
+
email: EMAIL_HELP,
|
|
14114
|
+
"chat-widget": CHAT_WIDGET_HELP,
|
|
14115
|
+
marketplace: MARKETPLACE_HELP,
|
|
14116
|
+
kb: KB_HELP,
|
|
14117
|
+
cms: CMS_HELP,
|
|
14118
|
+
documents: DOCUMENTS_HELP,
|
|
14119
|
+
projects: PROJECTS_HELP,
|
|
14120
|
+
reviews: REVIEWS_HELP,
|
|
14121
|
+
workspaces: WORKSPACES_HELP,
|
|
14122
|
+
"agent-profile": AGENT_PROFILE_HELP,
|
|
14123
|
+
"agent-allowlists": AGENT_ALLOWLISTS_HELP,
|
|
14124
|
+
"agent-packs": AGENT_PACKS_HELP,
|
|
14125
|
+
"agent-ops": AGENT_OPS_HELP,
|
|
14126
|
+
evals: EVALS_HELP,
|
|
14127
|
+
twin: TWIN_HELP,
|
|
14128
|
+
strategy: STRATEGY_HELP,
|
|
14129
|
+
advisors: ADVISORS_HELP,
|
|
14130
|
+
"campaigns-orchestration": CAMPAIGNS_ORCH_HELP,
|
|
14131
|
+
notebooks: NOTEBOOKS_HELP,
|
|
14132
|
+
podcasts: PODCASTS_HELP,
|
|
14133
|
+
"priority-matrix": PRIORITY_MATRIX_HELP,
|
|
14134
|
+
config: CONFIG_HELP,
|
|
14135
|
+
doctor: DOCTOR_HELP,
|
|
14136
|
+
health: HEALTH_HELP,
|
|
14137
|
+
capabilities: CAPABILITIES_HELP,
|
|
14138
|
+
caps: CAPABILITIES_HELP,
|
|
14139
|
+
media: MEDIA_HELP,
|
|
14140
|
+
conformance: CONFORMANCE_HELP,
|
|
14141
|
+
memory: MEMORY_HELP,
|
|
14142
|
+
webhooks: WEBHOOKS_HELP,
|
|
14143
|
+
webhook: WEBHOOKS_HELP,
|
|
14144
|
+
cron: CRON_HELP,
|
|
14145
|
+
messaging: MESSAGING_HELP,
|
|
14146
|
+
relay: RELAY_HELP,
|
|
14147
|
+
notifications: NOTIFICATIONS_HELP,
|
|
14148
|
+
notification: NOTIFICATIONS_HELP,
|
|
14149
|
+
interrupts: INTERRUPTS_HELP,
|
|
14150
|
+
interrupt: INTERRUPTS_HELP,
|
|
14151
|
+
prompts: PROMPTS_HELP,
|
|
14152
|
+
prompt: PROMPTS_HELP,
|
|
14153
|
+
notify: NOTIFY_HELP,
|
|
14154
|
+
account: ACCOUNT_HELP,
|
|
14155
|
+
admin: ADMIN_HELP,
|
|
14156
|
+
governance: GOVERNANCE_HELP,
|
|
14157
|
+
policy: GOVERNANCE_HELP,
|
|
14158
|
+
approvals: APPROVALS_HELP,
|
|
14159
|
+
approval: APPROVALS_HELP,
|
|
14160
|
+
consent: CONSENT_HELP,
|
|
14161
|
+
mcp: MCP_HELP,
|
|
14162
|
+
connections: CONNECTIONS_HELP,
|
|
14163
|
+
conn: CONNECTIONS_HELP,
|
|
14164
|
+
profiles: PROFILES_HELP,
|
|
14165
|
+
completion: COMPLETION_HELP,
|
|
14166
|
+
upgrade: UPGRADE_HELP,
|
|
14167
|
+
toggles: TOGGLES_HELP,
|
|
14168
|
+
users: USERS_HELP,
|
|
14169
|
+
workforces: WORKFORCES_HELP,
|
|
14170
|
+
fleet: WORKFORCES_HELP,
|
|
14171
|
+
auth: AUTH_HELP,
|
|
14172
|
+
sso: AUTH_HELP,
|
|
14173
|
+
analytics: ANALYTICS_HELP,
|
|
14174
|
+
usage: ANALYTICS_HELP,
|
|
14175
|
+
proposals: PROPOSALS_HELP,
|
|
14176
|
+
goals: GOALS_HELP,
|
|
14177
|
+
export: EXPORT_HELP,
|
|
14178
|
+
import: IMPORT_HELP,
|
|
14179
|
+
triggers: TRIGGERS_HELP,
|
|
14180
|
+
a2a: A2A_HELP
|
|
14181
|
+
};
|
|
14182
|
+
var COMMAND_NAMES = Object.keys(HELP_MAP);
|
|
10722
14183
|
function showHelp(io, command) {
|
|
10723
|
-
|
|
10724
|
-
demo: DEMO_HELP,
|
|
10725
|
-
runs: RUNS_HELP,
|
|
10726
|
-
run: RUNS_HELP,
|
|
10727
|
-
chat: CHAT_HELP,
|
|
10728
|
-
workflows: WORKFLOWS_HELP,
|
|
10729
|
-
workflow: WORKFLOWS_HELP,
|
|
10730
|
-
catalog: CATALOG_HELP,
|
|
10731
|
-
packs: PACKS_HELP,
|
|
10732
|
-
pack: PACKS_HELP,
|
|
10733
|
-
onboard: ONBOARD_HELP,
|
|
10734
|
-
providers: PROVIDERS_HELP,
|
|
10735
|
-
provider: PROVIDERS_HELP,
|
|
10736
|
-
agents: AGENTS_HELP,
|
|
10737
|
-
agent: AGENTS_HELP,
|
|
10738
|
-
roster: ROSTER_HELP,
|
|
10739
|
-
"org-chart": ORG_CHART_HELP,
|
|
10740
|
-
orgchart: ORG_CHART_HELP,
|
|
10741
|
-
kanban: KANBAN_HELP,
|
|
10742
|
-
boards: KANBAN_HELP,
|
|
10743
|
-
orgs: ORGS_HELP,
|
|
10744
|
-
org: ORGS_HELP,
|
|
10745
|
-
workspace: WORKSPACE_HELP,
|
|
10746
|
-
byok: BYOK_HELP,
|
|
10747
|
-
config: CONFIG_HELP,
|
|
10748
|
-
doctor: DOCTOR_HELP,
|
|
10749
|
-
health: HEALTH_HELP,
|
|
10750
|
-
capabilities: CAPABILITIES_HELP,
|
|
10751
|
-
caps: CAPABILITIES_HELP,
|
|
10752
|
-
media: MEDIA_HELP,
|
|
10753
|
-
conformance: CONFORMANCE_HELP,
|
|
10754
|
-
memory: MEMORY_HELP,
|
|
10755
|
-
webhooks: WEBHOOKS_HELP,
|
|
10756
|
-
webhook: WEBHOOKS_HELP,
|
|
10757
|
-
cron: CRON_HELP,
|
|
10758
|
-
messaging: MESSAGING_HELP,
|
|
10759
|
-
relay: RELAY_HELP,
|
|
10760
|
-
notifications: NOTIFICATIONS_HELP,
|
|
10761
|
-
notification: NOTIFICATIONS_HELP,
|
|
10762
|
-
interrupts: INTERRUPTS_HELP,
|
|
10763
|
-
interrupt: INTERRUPTS_HELP,
|
|
10764
|
-
prompts: PROMPTS_HELP,
|
|
10765
|
-
prompt: PROMPTS_HELP,
|
|
10766
|
-
notify: NOTIFY_HELP,
|
|
10767
|
-
account: ACCOUNT_HELP,
|
|
10768
|
-
admin: ADMIN_HELP,
|
|
10769
|
-
governance: GOVERNANCE_HELP,
|
|
10770
|
-
policy: GOVERNANCE_HELP,
|
|
10771
|
-
approvals: APPROVALS_HELP,
|
|
10772
|
-
approval: APPROVALS_HELP,
|
|
10773
|
-
consent: CONSENT_HELP,
|
|
10774
|
-
mcp: MCP_HELP,
|
|
10775
|
-
connections: CONNECTIONS_HELP,
|
|
10776
|
-
conn: CONNECTIONS_HELP,
|
|
10777
|
-
profiles: PROFILES_HELP,
|
|
10778
|
-
toggles: TOGGLES_HELP,
|
|
10779
|
-
users: USERS_HELP,
|
|
10780
|
-
workforces: WORKFORCES_HELP,
|
|
10781
|
-
fleet: WORKFORCES_HELP,
|
|
10782
|
-
auth: AUTH_HELP,
|
|
10783
|
-
sso: AUTH_HELP,
|
|
10784
|
-
analytics: ANALYTICS_HELP,
|
|
10785
|
-
usage: ANALYTICS_HELP,
|
|
10786
|
-
proposals: PROPOSALS_HELP,
|
|
10787
|
-
goals: GOALS_HELP,
|
|
10788
|
-
export: EXPORT_HELP,
|
|
10789
|
-
import: IMPORT_HELP,
|
|
10790
|
-
triggers: TRIGGERS_HELP,
|
|
10791
|
-
a2a: A2A_HELP
|
|
10792
|
-
};
|
|
10793
|
-
write(io.stdout, map[command] ?? ROOT_HELP);
|
|
14184
|
+
write(io.stdout, HELP_MAP[command] ?? ROOT_HELP);
|
|
10794
14185
|
return 0;
|
|
10795
14186
|
}
|
|
10796
14187
|
var ROOT_HELP = `openwop - operate OpenWOP hosts and the workflow-engine demo app
|
|
@@ -10799,14 +14190,24 @@ Usage:
|
|
|
10799
14190
|
openwop [global options] <command> [options]
|
|
10800
14191
|
|
|
10801
14192
|
Global options:
|
|
10802
|
-
--base-url <url> Host base URL (default: OPENWOP_BASE_URL
|
|
10803
|
-
--api-key <key> Bearer API key (default: OPENWOP_API_KEY
|
|
14193
|
+
--base-url <url> Host base URL (default: --base-url > OPENWOP_BASE_URL > saved config > http://localhost:8080)
|
|
14194
|
+
--api-key <key> Bearer API key (default: OPENWOP_API_KEY > saved config > sample-token for localhost)
|
|
14195
|
+
--profile <name> Use a named config profile (~/.openwop-<name>); also OPENWOP_PROFILE
|
|
10804
14196
|
--json Print machine-readable JSON where supported
|
|
10805
14197
|
--quiet Suppress non-essential output
|
|
10806
14198
|
--verbose Print extra diagnostics
|
|
10807
14199
|
--version Print CLI version
|
|
10808
14200
|
--help, -h Show help
|
|
10809
14201
|
|
|
14202
|
+
Exit codes:
|
|
14203
|
+
0 success (a read succeeded; a run/goal completed; a check passed)
|
|
14204
|
+
1 runtime/host error (HTTP >=500, unreachable host, an operation failed)
|
|
14205
|
+
2 usage error (bad flags/args) or a client/host 4xx (e.g. not found, validation)
|
|
14206
|
+
3 attention needed \u2014 a non-failure "open" state: a run escalated/paused, a
|
|
14207
|
+
proposal is pending, a goal is still open (set by run/goal/proposal/approval-style
|
|
14208
|
+
commands so scripts can branch on 0 vs 3 vs 1)
|
|
14209
|
+
4 auth/permission denied (e.g. a super-admin surface without a super-admin principal)
|
|
14210
|
+
|
|
10810
14211
|
Commands:
|
|
10811
14212
|
onboard Guided first-run setup (host + provider + model + BYOK key)
|
|
10812
14213
|
providers list List stored BYOK credential refs (never values)
|
|
@@ -10815,6 +14216,8 @@ Commands:
|
|
|
10815
14216
|
providers test Verify the credential ref is reachable
|
|
10816
14217
|
config file Print the local config file path
|
|
10817
14218
|
config get|set|unset Read or modify ~/.openwop/config.json
|
|
14219
|
+
completion <shell> Emit a bash/zsh/fish completion script
|
|
14220
|
+
upgrade Check npm for a newer @openwop/cli
|
|
10818
14221
|
doctor Check local prerequisites and demo reachability
|
|
10819
14222
|
demo status Inspect the workflow-engine demo app
|
|
10820
14223
|
demo start Start the demo backend and frontend (--detach to background)
|
|
@@ -10871,6 +14274,34 @@ Commands:
|
|
|
10871
14274
|
byok list List BYOK credential refs (never values)
|
|
10872
14275
|
byok set Store a BYOK secret (prompts if --value omitted)
|
|
10873
14276
|
byok delete Remove a BYOK secret ref
|
|
14277
|
+
brand public Show the applied white-label app identity (ADR 0170/0171)
|
|
14278
|
+
brand get|set Read / edit the app brand + generative theme (super-admin)
|
|
14279
|
+
crm list|create Manage CRM contacts (+ get/update/delete/triage)
|
|
14280
|
+
csm list|create Manage Customer-Success accounts (+ get/update/delete)
|
|
14281
|
+
comments list Threaded comments on a resource (--org; +create/update/delete)
|
|
14282
|
+
sharing list Shareable resource links (--org; +create/revoke/resolve)
|
|
14283
|
+
forms list Form builder + intake (--org; +create/update/status/submissions)
|
|
14284
|
+
email templates Email templates + campaigns (--org; create/send/sends)
|
|
14285
|
+
chat-widget list Embeddable chat widgets (--org; +create/rotate-token)
|
|
14286
|
+
marketplace listings Browse/install packs + reviews (install; review --org)
|
|
14287
|
+
kb collections Knowledge base (--org; collections/docs/search/rag)
|
|
14288
|
+
cms pages CMS pages + authoring lifecycle (--org; submit/approve/publish)
|
|
14289
|
+
documents list Document generation + templates (--org; versions/render)
|
|
14290
|
+
projects list Project workspaces (create --org; +members)
|
|
14291
|
+
reviews list The unified review inbox (get; action <id> <action>)
|
|
14292
|
+
workspaces list Your B2B workspaces / tenants (create/switch; vs the file 'workspace')
|
|
14293
|
+
agent-profile get Rich agent profile + connector readiness (set/readiness)
|
|
14294
|
+
agent-allowlists Super-admin per-agent tool allowlists (list/get/set/clear)
|
|
14295
|
+
agent-packs list Agent (persona) pack registry (install)
|
|
14296
|
+
agent-ops status Demo example-data + roster/fleet activity (seed/run/clear)
|
|
14297
|
+
evals leaderboard Model eval leaderboard + arena (--org; match)
|
|
14298
|
+
twin get Agent digital-twin config + grants (set/grant/revoke)
|
|
14299
|
+
strategy list Strategy documents (create --org; context/health)
|
|
14300
|
+
advisors list Advisory boards (create --org; by-handle)
|
|
14301
|
+
campaigns-orchestration Orchestrated campaigns (create/finalize)
|
|
14302
|
+
notebooks list NotebookLM-style notebooks (create --org; notes)
|
|
14303
|
+
podcasts list Podcast episodes (create/retry)
|
|
14304
|
+
priority-matrix lists Prioritization boards (create --org; ideas)
|
|
10874
14305
|
webhooks list List webhook subscriptions
|
|
10875
14306
|
webhooks add Register a webhook subscription
|
|
10876
14307
|
webhooks remove Delete a webhook subscription
|