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