@openwop/cli 1.1.0 → 1.2.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 +2 -0
- package/dist/cli.js +511 -365
- package/dist/openwop.js +504 -365
- package/package.json +1 -1
package/dist/openwop.js
CHANGED
|
@@ -566,6 +566,12 @@ function httpErrorHint(err, env) {
|
|
|
566
566
|
}
|
|
567
567
|
return void 0;
|
|
568
568
|
}
|
|
569
|
+
function isNoCredentialChallenge(err) {
|
|
570
|
+
if (!(err instanceof HttpError) || err.status !== 401) return false;
|
|
571
|
+
const challenge = err.headers?.get?.("www-authenticate");
|
|
572
|
+
if (!challenge || !/^\s*Bearer\b/i.test(challenge)) return false;
|
|
573
|
+
return !/\berror\s*=/i.test(challenge);
|
|
574
|
+
}
|
|
569
575
|
function errText(err) {
|
|
570
576
|
return err instanceof Error ? err.message : String(err);
|
|
571
577
|
}
|
|
@@ -573,7 +579,7 @@ function errText(err) {
|
|
|
573
579
|
// package.json
|
|
574
580
|
var package_default = {
|
|
575
581
|
name: "@openwop/cli",
|
|
576
|
-
version: "1.1
|
|
582
|
+
version: "1.2.1",
|
|
577
583
|
description: "OpenWOP CLI for operating the workflow-engine demo app and OpenWOP-compatible hosts.",
|
|
578
584
|
license: "Apache-2.0",
|
|
579
585
|
type: "module",
|
|
@@ -704,6 +710,7 @@ function prefixChunk(stream, label, chunk) {
|
|
|
704
710
|
function extractGlobalOptions(argv, env = process.env) {
|
|
705
711
|
const globals = {
|
|
706
712
|
baseUrl: void 0,
|
|
713
|
+
streamBaseUrl: void 0,
|
|
707
714
|
apiKey: void 0,
|
|
708
715
|
profile: void 0,
|
|
709
716
|
json: false,
|
|
@@ -721,6 +728,10 @@ function extractGlobalOptions(argv, env = process.env) {
|
|
|
721
728
|
globals.baseUrl = value ?? takeValue(argv, ++i, "--base-url");
|
|
722
729
|
continue;
|
|
723
730
|
}
|
|
731
|
+
if (flag === "--stream-base-url") {
|
|
732
|
+
globals.streamBaseUrl = value ?? takeValue(argv, ++i, "--stream-base-url");
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
724
735
|
if (flag === "--api-key") {
|
|
725
736
|
globals.apiKey = value ?? takeValue(argv, ++i, "--api-key");
|
|
726
737
|
continue;
|
|
@@ -1199,6 +1210,7 @@ async function negotiateMajor(ctx) {
|
|
|
1199
1210
|
const doc = JSON.parse(await res.text());
|
|
1200
1211
|
if (res.ok) {
|
|
1201
1212
|
ctx.discovery = { doc, servedVersion: res.headers?.get?.("openwop-version") ?? void 0 };
|
|
1213
|
+
ctx.discoveredStreamBase = streamBaseFrom(doc?.extensions, ctx.baseUrl);
|
|
1202
1214
|
major = selectMajor(doc?.protocolVersions);
|
|
1203
1215
|
if (major === 2) roots = hostRootsFrom(doc?.extensions);
|
|
1204
1216
|
} else if (res.status === 406) {
|
|
@@ -1219,6 +1231,67 @@ async function resolveRequest(ctx, path, headers) {
|
|
|
1219
1231
|
if (rooted !== null) return { path: rooted, headers };
|
|
1220
1232
|
return { path, headers };
|
|
1221
1233
|
}
|
|
1234
|
+
function isLoopback(url) {
|
|
1235
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]" || url.hostname === "::1";
|
|
1236
|
+
}
|
|
1237
|
+
function explicitStreamBase(raw) {
|
|
1238
|
+
if (typeof raw !== "string" || raw.trim() === "") return void 0;
|
|
1239
|
+
let url;
|
|
1240
|
+
try {
|
|
1241
|
+
url = new URL(raw.trim());
|
|
1242
|
+
} catch {
|
|
1243
|
+
throw new CliError(`--stream-base-url / OPENWOP_STREAM_BASE_URL is not a URL: ${raw}`);
|
|
1244
|
+
}
|
|
1245
|
+
if (url.username !== "" || url.password !== "") {
|
|
1246
|
+
throw new CliError("--stream-base-url / OPENWOP_STREAM_BASE_URL must not carry credentials (user:pass@); pass them with --api-key.");
|
|
1247
|
+
}
|
|
1248
|
+
return raw.trim().replace(/\/+$/, "");
|
|
1249
|
+
}
|
|
1250
|
+
function streamBaseFrom(extensions, baseUrl) {
|
|
1251
|
+
if (!extensions || typeof extensions !== "object" || Array.isArray(extensions)) return void 0;
|
|
1252
|
+
let base16;
|
|
1253
|
+
try {
|
|
1254
|
+
base16 = new URL(baseUrl);
|
|
1255
|
+
} catch {
|
|
1256
|
+
return void 0;
|
|
1257
|
+
}
|
|
1258
|
+
for (const value of Object.values(extensions)) {
|
|
1259
|
+
const raw = value && typeof value === "object" ? value.streamBase : void 0;
|
|
1260
|
+
if (typeof raw !== "string") continue;
|
|
1261
|
+
let url;
|
|
1262
|
+
try {
|
|
1263
|
+
url = new URL(raw);
|
|
1264
|
+
} catch {
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") continue;
|
|
1268
|
+
const secure = url.protocol === "https:" && (base16.protocol === "https:" || isLoopback(base16));
|
|
1269
|
+
const localDev = url.protocol === "http:" && isLoopback(url) && isLoopback(base16);
|
|
1270
|
+
if (!secure && !localDev) continue;
|
|
1271
|
+
return raw.replace(/\/+$/, "");
|
|
1272
|
+
}
|
|
1273
|
+
return void 0;
|
|
1274
|
+
}
|
|
1275
|
+
async function resolveStreamRequest(ctx, requestedPath, headers) {
|
|
1276
|
+
const resolved = await resolveRequest(ctx, requestedPath, { accept: "text/event-stream", ...headers });
|
|
1277
|
+
const { origin, source } = streamOrigin(ctx);
|
|
1278
|
+
if (ctx.verbose && source !== "base") ctx.io.stderr.write(`openwop: reading the event stream from ${origin} (${STREAM_SOURCE_LABEL[source]})
|
|
1279
|
+
`);
|
|
1280
|
+
const url = new URL(resolved.path.replace(/^\//, ""), origin.endsWith("/") ? origin : `${origin}/`);
|
|
1281
|
+
const out = { ...resolved.headers };
|
|
1282
|
+
if (ctx.apiKey) out.authorization = `Bearer ${ctx.apiKey}`;
|
|
1283
|
+
return { url, headers: out };
|
|
1284
|
+
}
|
|
1285
|
+
var STREAM_SOURCE_LABEL = {
|
|
1286
|
+
setting: "your --stream-base-url / OPENWOP_STREAM_BASE_URL / config setting",
|
|
1287
|
+
advertised: "the host's advertised streamBase",
|
|
1288
|
+
base: "--base-url"
|
|
1289
|
+
};
|
|
1290
|
+
function streamOrigin(ctx) {
|
|
1291
|
+
if (ctx.streamBaseUrl) return { origin: ctx.streamBaseUrl, source: "setting" };
|
|
1292
|
+
if (ctx.discoveredStreamBase) return { origin: ctx.discoveredStreamBase, source: "advertised" };
|
|
1293
|
+
return { origin: ctx.baseUrl, source: "base" };
|
|
1294
|
+
}
|
|
1222
1295
|
|
|
1223
1296
|
// src/wire.ts
|
|
1224
1297
|
import { randomUUID } from "node:crypto";
|
|
@@ -1412,6 +1485,10 @@ async function streamRunEvents(ctx, runId, opts = {}) {
|
|
|
1412
1485
|
} catch (err) {
|
|
1413
1486
|
if (err instanceof HttpError && err.status >= 400 && err.status < 500 && (opts.streamMode || opts.lastEventId !== void 0 || opts.afterSequence !== void 0)) throw err;
|
|
1414
1487
|
if (err instanceof SseResumeExhausted) throw err.reason instanceof HttpError ? err.reason : err;
|
|
1488
|
+
if (err instanceof StreamSilent && err.beforeHeaders && !ctx.quiet) {
|
|
1489
|
+
ctx.io.stderr.write(`openwop: the event stream sent nothing for ${err.ms >= 1e3 ? `${Math.round(err.ms / 1e3)} s` : `${err.ms} ms`} \u2014 the host's front door may be buffering streams. Following by polling instead; for live events pass --stream-base-url <a direct origin> (see \`openwop doctor\`).
|
|
1490
|
+
`);
|
|
1491
|
+
}
|
|
1415
1492
|
}
|
|
1416
1493
|
}
|
|
1417
1494
|
if (opts.streamMode && opts.streamMode !== "updates" && !ctx.quiet) {
|
|
@@ -1457,12 +1534,14 @@ async function streamViaSse(ctx, runId, deliver, opts) {
|
|
|
1457
1534
|
for (; ; ) {
|
|
1458
1535
|
let progressed = false;
|
|
1459
1536
|
let dropError;
|
|
1537
|
+
let idle;
|
|
1460
1538
|
try {
|
|
1461
|
-
const {
|
|
1462
|
-
const url = new URL(path.replace(/^\//, ""), ctx.baseUrl.endsWith("/") ? ctx.baseUrl : `${ctx.baseUrl}/`);
|
|
1463
|
-
if (ctx.apiKey) headers.authorization = `Bearer ${ctx.apiKey}`;
|
|
1539
|
+
const { url, headers } = await resolveStreamRequest(ctx, `/v1/runs/${encodeURIComponent(runId)}/events${query}`, {});
|
|
1464
1540
|
if (lastEventId !== void 0 && lastEventId !== "") headers["last-event-id"] = lastEventId;
|
|
1465
|
-
const
|
|
1541
|
+
const idleMs = opts.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
1542
|
+
idle = idleWatchdog(idleMs, opts.headersTimeoutMs ?? (idleMs > 0 ? Math.min(idleMs, DEFAULT_HEADERS_TIMEOUT_MS) : 0));
|
|
1543
|
+
const res = await ctx.fetchImpl(url, { method: "GET", headers, signal: idle.signal });
|
|
1544
|
+
idle.headersArrived();
|
|
1466
1545
|
if (!res.ok) {
|
|
1467
1546
|
const text = await res.text().catch(() => "");
|
|
1468
1547
|
let body = null;
|
|
@@ -1479,7 +1558,7 @@ async function streamViaSse(ctx, runId, deliver, opts) {
|
|
|
1479
1558
|
throw new HttpError("resumed events stream is not text/event-stream", res.status, null, res.headers);
|
|
1480
1559
|
}
|
|
1481
1560
|
opened = true;
|
|
1482
|
-
await consumeSse(res.body, (frame) => {
|
|
1561
|
+
await consumeSse(idle.watch(res.body), (frame) => {
|
|
1483
1562
|
if (frame.retry !== void 0) retryMs = frame.retry;
|
|
1484
1563
|
if (frame.id !== void 0) lastEventId = frame.id;
|
|
1485
1564
|
if (frame.data === void 0) return;
|
|
@@ -1498,18 +1577,23 @@ async function streamViaSse(ctx, runId, deliver, opts) {
|
|
|
1498
1577
|
if (!opened) throw err;
|
|
1499
1578
|
if (err instanceof HttpError && err.status !== 429 && err.status < 500) throw new SseResumeExhausted(err.message, err);
|
|
1500
1579
|
dropError = err;
|
|
1580
|
+
} finally {
|
|
1581
|
+
idle?.stop();
|
|
1501
1582
|
}
|
|
1502
1583
|
if (terminal) return true;
|
|
1503
1584
|
if (lastEventId === void 0 && deliver.highest() >= 0) lastEventId = String(deliver.highest());
|
|
1504
1585
|
if (await runIsTerminal(ctx, runId)) return true;
|
|
1505
1586
|
failures = progressed ? 1 : failures + 1;
|
|
1506
1587
|
if (failures > maxReconnects) {
|
|
1507
|
-
throw new SseResumeExhausted(`events stream for ${runId} dropped ${failures} times without progress${dropError instanceof Error ? `: ${dropError.message}` : ""}
|
|
1588
|
+
throw new SseResumeExhausted(`events stream for ${runId} dropped ${failures} times without progress${dropError instanceof Error ? `: ${dropError.message}` : ""}. Re-run with --since <last sequence printed> to continue, or --no-stream to follow by polling.`, dropError);
|
|
1508
1589
|
}
|
|
1509
1590
|
const delayMs = Math.min(retryMs * 2 ** (failures - 1), 3e4);
|
|
1510
1591
|
opts.onReconnect?.({ attempt: failures, lastEventId, delayMs });
|
|
1511
|
-
if (ctx.verbose)
|
|
1592
|
+
if (ctx.verbose) {
|
|
1593
|
+
const why = dropError instanceof StreamSilent ? `stalled (${dropError.message})` : "dropped";
|
|
1594
|
+
ctx.io.stderr.write(`openwop: events stream ${why}; reconnecting in ${delayMs}ms with Last-Event-ID ${lastEventId ?? "(none)"}
|
|
1512
1595
|
`);
|
|
1596
|
+
}
|
|
1513
1597
|
await sleep(delayMs);
|
|
1514
1598
|
}
|
|
1515
1599
|
}
|
|
@@ -1707,6 +1791,74 @@ function defaultReadTurn(ctx) {
|
|
|
1707
1791
|
rl.once("close", onClose);
|
|
1708
1792
|
});
|
|
1709
1793
|
}
|
|
1794
|
+
var DEFAULT_IDLE_TIMEOUT_MS = 45e3;
|
|
1795
|
+
var DEFAULT_HEADERS_TIMEOUT_MS = 1e4;
|
|
1796
|
+
var StreamSilent = class extends Error {
|
|
1797
|
+
constructor(ms2, beforeHeaders) {
|
|
1798
|
+
super(beforeHeaders ? `no response headers within ${ms2}ms` : `no bytes for ${ms2}ms`);
|
|
1799
|
+
this.ms = ms2;
|
|
1800
|
+
this.beforeHeaders = beforeHeaders;
|
|
1801
|
+
this.name = "StreamSilent";
|
|
1802
|
+
}
|
|
1803
|
+
};
|
|
1804
|
+
function idleWatchdog(ms2, headersMs = ms2) {
|
|
1805
|
+
const controller = new AbortController();
|
|
1806
|
+
let timer;
|
|
1807
|
+
let onExpire;
|
|
1808
|
+
let beforeHeaders = true;
|
|
1809
|
+
const expire = (after) => {
|
|
1810
|
+
controller.abort(new StreamSilent(after, beforeHeaders));
|
|
1811
|
+
onExpire?.();
|
|
1812
|
+
};
|
|
1813
|
+
const arm = (after = ms2) => {
|
|
1814
|
+
if (!(after > 0)) return;
|
|
1815
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1816
|
+
timer = setTimeout(() => expire(after), after);
|
|
1817
|
+
};
|
|
1818
|
+
arm(headersMs);
|
|
1819
|
+
return {
|
|
1820
|
+
signal: controller.signal,
|
|
1821
|
+
/** The response headers arrived: from here on only the idle timeout applies. */
|
|
1822
|
+
headersArrived() {
|
|
1823
|
+
beforeHeaders = false;
|
|
1824
|
+
arm();
|
|
1825
|
+
},
|
|
1826
|
+
watch(body) {
|
|
1827
|
+
if (!(ms2 > 0) || typeof body?.getReader !== "function") return body;
|
|
1828
|
+
const source = body.getReader();
|
|
1829
|
+
return new ReadableStream({
|
|
1830
|
+
start(out) {
|
|
1831
|
+
onExpire = () => {
|
|
1832
|
+
out.error(new StreamSilent(ms2, false));
|
|
1833
|
+
source.cancel().catch(() => {
|
|
1834
|
+
});
|
|
1835
|
+
};
|
|
1836
|
+
},
|
|
1837
|
+
async pull(out) {
|
|
1838
|
+
try {
|
|
1839
|
+
const { value, done: done2 } = await source.read();
|
|
1840
|
+
if (done2) {
|
|
1841
|
+
out.close();
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
arm();
|
|
1845
|
+
out.enqueue(value);
|
|
1846
|
+
} catch (err) {
|
|
1847
|
+
out.error(err);
|
|
1848
|
+
}
|
|
1849
|
+
},
|
|
1850
|
+
cancel(reason) {
|
|
1851
|
+
return source.cancel(reason);
|
|
1852
|
+
}
|
|
1853
|
+
});
|
|
1854
|
+
},
|
|
1855
|
+
stop() {
|
|
1856
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1857
|
+
timer = void 0;
|
|
1858
|
+
onExpire = void 0;
|
|
1859
|
+
}
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1710
1862
|
|
|
1711
1863
|
// src/cli/chatShared.ts
|
|
1712
1864
|
var enc = (value) => encodeURIComponent(value);
|
|
@@ -1731,9 +1883,7 @@ function writeFields(ctx, fields) {
|
|
|
1731
1883
|
}
|
|
1732
1884
|
}
|
|
1733
1885
|
async function streamHostSse(ctx, requestedPath, opts) {
|
|
1734
|
-
const {
|
|
1735
|
-
const url = new URL(path.replace(/^\//, ""), ctx.baseUrl.endsWith("/") ? ctx.baseUrl : `${ctx.baseUrl}/`);
|
|
1736
|
-
if (ctx.apiKey) headers.authorization = `Bearer ${ctx.apiKey}`;
|
|
1886
|
+
const { url, headers } = await resolveStreamRequest(ctx, requestedPath, {});
|
|
1737
1887
|
const controller = new AbortController();
|
|
1738
1888
|
let timer;
|
|
1739
1889
|
if (opts.timeoutMs && opts.timeoutMs > 0) timer = setTimeout(() => controller.abort(), opts.timeoutMs);
|
|
@@ -1790,6 +1940,82 @@ function renderFrame(frame) {
|
|
|
1790
1940
|
|
|
1791
1941
|
// src/cli/routeKit.ts
|
|
1792
1942
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
1943
|
+
|
|
1944
|
+
// src/cli/shared.ts
|
|
1945
|
+
function requireOrg(org2) {
|
|
1946
|
+
if (!org2) throw new CliError("This command is org-scoped \u2014 pass --org <orgId>.", 2);
|
|
1947
|
+
return String(org2);
|
|
1948
|
+
}
|
|
1949
|
+
function buildInputs(options) {
|
|
1950
|
+
const fromJson = options.inputsJson ? JSON.parse(options.inputsJson) : {};
|
|
1951
|
+
if (fromJson === null || typeof fromJson !== "object" || Array.isArray(fromJson)) {
|
|
1952
|
+
throw new CliError("--inputs-json must be a JSON object");
|
|
1953
|
+
}
|
|
1954
|
+
const inputs = { ...fromJson };
|
|
1955
|
+
for (const pair of options.input ?? []) {
|
|
1956
|
+
const eq = pair.indexOf("=");
|
|
1957
|
+
if (eq <= 0) throw new CliError(`--input must be key=value, got: ${pair}`);
|
|
1958
|
+
inputs[pair.slice(0, eq)] = parseInputValue(pair.slice(eq + 1));
|
|
1959
|
+
}
|
|
1960
|
+
return inputs;
|
|
1961
|
+
}
|
|
1962
|
+
function parseInputValue(value) {
|
|
1963
|
+
try {
|
|
1964
|
+
return JSON.parse(value);
|
|
1965
|
+
} catch {
|
|
1966
|
+
return value;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
function parseNodeVersion(version) {
|
|
1970
|
+
const [major, minor, patch] = version.split(".").map((v) => Number(v));
|
|
1971
|
+
return { major: major || 0, minor: minor || 0, patch: patch || 0 };
|
|
1972
|
+
}
|
|
1973
|
+
function defaultApiKeyFor(baseUrl) {
|
|
1974
|
+
try {
|
|
1975
|
+
const host = new URL(baseUrl).hostname;
|
|
1976
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "::1") return DEFAULT_API_KEY;
|
|
1977
|
+
} catch {
|
|
1978
|
+
return void 0;
|
|
1979
|
+
}
|
|
1980
|
+
return void 0;
|
|
1981
|
+
}
|
|
1982
|
+
function normalizeBaseUrl(value) {
|
|
1983
|
+
if (!value) return DEFAULT_BASE_URL;
|
|
1984
|
+
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
1985
|
+
}
|
|
1986
|
+
function npmCommand() {
|
|
1987
|
+
return process.platform === "win32" ? "npm.cmd" : "npm";
|
|
1988
|
+
}
|
|
1989
|
+
function ok(name, message) {
|
|
1990
|
+
return { status: "ok", name, message };
|
|
1991
|
+
}
|
|
1992
|
+
function warn(name, message) {
|
|
1993
|
+
return { status: "warn", name, message };
|
|
1994
|
+
}
|
|
1995
|
+
function fail(name, message) {
|
|
1996
|
+
return { status: "fail", name, message };
|
|
1997
|
+
}
|
|
1998
|
+
function formatCheckTable(checks) {
|
|
1999
|
+
return formatTable(
|
|
2000
|
+
checks.map((c) => ({ status: c.status.toUpperCase(), check: c.name, message: c.message })),
|
|
2001
|
+
["status", "check", "message"]
|
|
2002
|
+
);
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
// src/cli/routeKit.ts
|
|
2006
|
+
var errText2 = (err) => err instanceof Error ? err.message : String(err);
|
|
2007
|
+
var DEFAULT_MESSAGES = {
|
|
2008
|
+
required: (flag, usage6) => `${usage6}
|
|
2009
|
+
${flag} is required.`,
|
|
2010
|
+
refusal: (words, positionals) => `Refusing to ${words.join(" ")} ${positionals.join(" ")} without --yes.`.replace(/\s+/g, " ").trim(),
|
|
2011
|
+
invalidNumber: (flag, raw) => `${flag} must be a number (got "${raw}").`,
|
|
2012
|
+
invalidBool: (flag, raw) => `${flag} must be true or false (got "${raw}").`,
|
|
2013
|
+
invalidJson: (flag) => `${flag} must be valid JSON.`,
|
|
2014
|
+
unreadable: (flag, path, err) => `Could not read ${path} for ${flag}: ${errText2(err)}`,
|
|
2015
|
+
unreadableBody: (path, err) => `Could not read ${path}: ${errText2(err)}`,
|
|
2016
|
+
invalidBodyJson: "--body / --body-file must be a JSON object.",
|
|
2017
|
+
bodyNotObject: "--body / --body-file must be a JSON object."
|
|
2018
|
+
};
|
|
1793
2019
|
var COMMON_BOOL = ["--help", "--yes"];
|
|
1794
2020
|
function templates(cmd) {
|
|
1795
2021
|
return Array.isArray(cmd.path) ? cmd.path : [cmd.path];
|
|
@@ -1797,18 +2023,31 @@ function templates(cmd) {
|
|
|
1797
2023
|
function paramNames(path) {
|
|
1798
2024
|
return [...path.matchAll(/:([A-Za-z][A-Za-z0-9_]*)/g)].map((m) => m[1]);
|
|
1799
2025
|
}
|
|
2026
|
+
function positionalNames(cmd, path) {
|
|
2027
|
+
return paramNames(path).filter((p) => !(cmd.orgFlag && p === "org"));
|
|
2028
|
+
}
|
|
2029
|
+
function bindsOrg(cmd) {
|
|
2030
|
+
return Boolean(cmd.orgFlag) && templates(cmd).some((p) => paramNames(p).includes("org"));
|
|
2031
|
+
}
|
|
2032
|
+
function fillPath(cmd, template, positionals, org2) {
|
|
2033
|
+
let i = 0;
|
|
2034
|
+
return template.replace(/:([A-Za-z][A-Za-z0-9_]*)/g, (_m, name) => {
|
|
2035
|
+
if (cmd.orgFlag && name === "org") return encodeURIComponent(requireOrg(org2));
|
|
2036
|
+
return encodeURIComponent(positionals[i++] ?? "");
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
1800
2039
|
function flagHint(f) {
|
|
1801
2040
|
const t = f.type ?? "string";
|
|
1802
|
-
const v = t === "boolean" ? "" : t === "file" || t === "file64" || t === "json-file" ? " <path>" : t === "map" ? " <key=value>..." : t === "list" ? ` <${f.key}>...` : t === "json" ? ` <json>` : ` <${f.key.split(".").pop()}>`;
|
|
2041
|
+
const v = t === "boolean" ? "" : t === "bool" ? " <true|false>" : t === "file" || t === "file64" || t === "json-file" ? " <path>" : t === "map" ? " <key=value>..." : t === "list" ? ` <${f.key}>...` : t === "json" ? ` <json>` : ` <${f.key.split(".").pop()}>`;
|
|
1803
2042
|
return f.required ? `${f.flag}${v}` : `[${f.flag}${v}]`;
|
|
1804
2043
|
}
|
|
1805
2044
|
function usageLine(group, cmd) {
|
|
1806
2045
|
const paths = templates(cmd);
|
|
1807
|
-
const longest = paths.reduce((a, b) =>
|
|
1808
|
-
const shortest = paths.reduce((a, b) =>
|
|
1809
|
-
const req2 =
|
|
1810
|
-
const params =
|
|
1811
|
-
const flags = [...cmd.query ?? [], ...cmd.body ?? []].map(flagHint);
|
|
2046
|
+
const longest = paths.reduce((a, b) => positionalNames(cmd, b).length > positionalNames(cmd, a).length ? b : a);
|
|
2047
|
+
const shortest = paths.reduce((a, b) => positionalNames(cmd, b).length < positionalNames(cmd, a).length ? b : a);
|
|
2048
|
+
const req2 = positionalNames(cmd, shortest);
|
|
2049
|
+
const params = positionalNames(cmd, longest).map((p, i) => i < req2.length ? `<${p}>` : `[<${p}>]`);
|
|
2050
|
+
const flags = [...bindsOrg(cmd) ? ["--org <orgId>"] : [], ...[...cmd.query ?? [], ...cmd.body ?? []].map(flagHint)];
|
|
1812
2051
|
if (cmd.body?.some((f) => f.type === "json")) flags.push("[--body <json>|--body-file <path>]");
|
|
1813
2052
|
if (cmd.confirm ?? cmd.method === "DELETE") flags.push("--yes");
|
|
1814
2053
|
if (cmd.method === "GET") flags.push("[--json]");
|
|
@@ -1844,9 +2083,15 @@ function setPath(target, key, value) {
|
|
|
1844
2083
|
}
|
|
1845
2084
|
node[parts[parts.length - 1]] = value;
|
|
1846
2085
|
}
|
|
1847
|
-
function coerce(f, raw) {
|
|
2086
|
+
function coerce(f, raw, cmd, msg) {
|
|
1848
2087
|
const t = f.type ?? "string";
|
|
1849
2088
|
if (t === "boolean") return raw;
|
|
2089
|
+
if (t === "bool") {
|
|
2090
|
+
const s2 = String(raw);
|
|
2091
|
+
if (s2 === "true" || s2 === "yes" || s2 === "1") return true;
|
|
2092
|
+
if (s2 === "false" || s2 === "no" || s2 === "0") return false;
|
|
2093
|
+
throw new CliError(msg.invalidBool(f.flag, s2), 2);
|
|
2094
|
+
}
|
|
1850
2095
|
if (t === "list") return Array.isArray(raw) ? raw.map(String) : [String(raw)];
|
|
1851
2096
|
if (t === "map") {
|
|
1852
2097
|
const out = {};
|
|
@@ -1862,7 +2107,7 @@ function coerce(f, raw) {
|
|
|
1862
2107
|
const s = String(raw);
|
|
1863
2108
|
if (t === "number") {
|
|
1864
2109
|
const n = Number(s);
|
|
1865
|
-
if (!Number.isFinite(n)) throw new CliError(
|
|
2110
|
+
if (!Number.isFinite(n) || cmd.strictNumbers && s.trim() === "") throw new CliError(msg.invalidNumber(f.flag, s), 2);
|
|
1866
2111
|
return n;
|
|
1867
2112
|
}
|
|
1868
2113
|
if (t === "json-file") {
|
|
@@ -1870,7 +2115,7 @@ function coerce(f, raw) {
|
|
|
1870
2115
|
try {
|
|
1871
2116
|
text = readFileSync4(s, "utf8");
|
|
1872
2117
|
} catch (err) {
|
|
1873
|
-
throw new CliError(
|
|
2118
|
+
throw new CliError(msg.unreadable(f.flag, s, err), 2);
|
|
1874
2119
|
}
|
|
1875
2120
|
try {
|
|
1876
2121
|
return JSON.parse(text);
|
|
@@ -1882,14 +2127,14 @@ function coerce(f, raw) {
|
|
|
1882
2127
|
try {
|
|
1883
2128
|
return readFileSync4(s).toString("base64");
|
|
1884
2129
|
} catch (err) {
|
|
1885
|
-
throw new CliError(
|
|
2130
|
+
throw new CliError(msg.unreadable(f.flag, s, err), 2);
|
|
1886
2131
|
}
|
|
1887
2132
|
}
|
|
1888
2133
|
if (t === "file") {
|
|
1889
2134
|
try {
|
|
1890
2135
|
return readFileSync4(s, "utf8").replace(/\r?\n$/, "");
|
|
1891
2136
|
} catch (err) {
|
|
1892
|
-
throw new CliError(
|
|
2137
|
+
throw new CliError(msg.unreadable(f.flag, s, err), 2);
|
|
1893
2138
|
}
|
|
1894
2139
|
}
|
|
1895
2140
|
if (t === "csv") return s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
@@ -1897,18 +2142,19 @@ function coerce(f, raw) {
|
|
|
1897
2142
|
try {
|
|
1898
2143
|
return JSON.parse(s);
|
|
1899
2144
|
} catch {
|
|
1900
|
-
throw new CliError(
|
|
2145
|
+
throw new CliError(msg.invalidJson(f.flag, s), 2);
|
|
1901
2146
|
}
|
|
1902
2147
|
}
|
|
1903
2148
|
return s;
|
|
1904
2149
|
}
|
|
1905
|
-
function readBodyOptions(options) {
|
|
2150
|
+
function readBodyOptions(options, msg) {
|
|
1906
2151
|
let raw;
|
|
1907
2152
|
if (options.bodyFile !== void 0) {
|
|
2153
|
+
if (String(options.bodyFile).trim() === "") throw new CliError("--body-file needs a path (got an empty value).", 2);
|
|
1908
2154
|
try {
|
|
1909
2155
|
raw = readFileSync4(String(options.bodyFile), "utf8");
|
|
1910
2156
|
} catch (err) {
|
|
1911
|
-
throw new CliError(
|
|
2157
|
+
throw new CliError(msg.unreadableBody(String(options.bodyFile), err), 2);
|
|
1912
2158
|
}
|
|
1913
2159
|
} else if (options.body !== void 0) {
|
|
1914
2160
|
raw = String(options.body);
|
|
@@ -1918,9 +2164,9 @@ function readBodyOptions(options) {
|
|
|
1918
2164
|
try {
|
|
1919
2165
|
parsed = JSON.parse(raw);
|
|
1920
2166
|
} catch {
|
|
1921
|
-
throw new CliError(
|
|
2167
|
+
throw new CliError(msg.invalidBodyJson, 2);
|
|
1922
2168
|
}
|
|
1923
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(
|
|
2169
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(msg.bodyNotObject, 2);
|
|
1924
2170
|
return parsed;
|
|
1925
2171
|
}
|
|
1926
2172
|
function hostError(err) {
|
|
@@ -1931,30 +2177,34 @@ function hostError(err) {
|
|
|
1931
2177
|
${hint}` : line, code2);
|
|
1932
2178
|
}
|
|
1933
2179
|
async function runRoute(ctx, group, cmd, argv) {
|
|
2180
|
+
const msg = { ...DEFAULT_MESSAGES, ...cmd.messages ?? {} };
|
|
1934
2181
|
const rest = argv.slice(cmd.words.length);
|
|
1935
2182
|
const fields = [...cmd.query ?? [], ...cmd.body ?? []];
|
|
1936
2183
|
const bool = [...COMMON_BOOL, ...fields.filter((f) => f.type === "boolean").flatMap((f) => [f.flag, f.flag.replace(/^--/, "--no-")])];
|
|
1937
2184
|
const isMulti = (f) => f.type === "list" || f.type === "map";
|
|
1938
|
-
const value = [
|
|
2185
|
+
const value = [
|
|
2186
|
+
...cmd.bodyFlags !== false ? ["--body", "--body-file"] : [],
|
|
2187
|
+
...bindsOrg(cmd) ? ["--org"] : [],
|
|
2188
|
+
...fields.filter((f) => f.type !== "boolean" && !isMulti(f)).map((f) => f.flag)
|
|
2189
|
+
];
|
|
1939
2190
|
const multi = fields.filter(isMulti).map((f) => f.flag);
|
|
1940
2191
|
const { options, positionals } = parseOptions(rest, { bool, value, multi });
|
|
1941
2192
|
if (options.help) {
|
|
1942
|
-
|
|
2193
|
+
if (cmd.usageText !== void 0) write(ctx.io.stdout, cmd.usageText);
|
|
2194
|
+
else writeLine(ctx.io.stdout, `Usage: ${usageLine(group, cmd)}
|
|
1943
2195
|
|
|
1944
2196
|
${cmd.summary}
|
|
1945
2197
|
Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
1946
2198
|
return 0;
|
|
1947
2199
|
}
|
|
1948
|
-
const template = templates(cmd).find((p) =>
|
|
2200
|
+
const template = templates(cmd).find((p) => positionalNames(cmd, p).length === positionals.length);
|
|
1949
2201
|
if (!template) {
|
|
1950
|
-
write(ctx.io.stderr, `Usage: ${usageLine(group, cmd)}
|
|
2202
|
+
write(ctx.io.stderr, cmd.usageText ?? `Usage: ${usageLine(group, cmd)}
|
|
1951
2203
|
`);
|
|
1952
2204
|
return 2;
|
|
1953
2205
|
}
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
path = path.replace(`:${name}`, encodeURIComponent(positionals[i]));
|
|
1957
|
-
});
|
|
2206
|
+
const path = fillPath(cmd, template, positionals, options.org);
|
|
2207
|
+
const requiredError = (f) => new CliError(msg.required(f.flag, cmd.usageText ?? usageLine(group, cmd)), 2);
|
|
1958
2208
|
const optionOf = (f) => {
|
|
1959
2209
|
const name = f.flag.replace(/^--/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1960
2210
|
if (f.type === "boolean") {
|
|
@@ -1969,34 +2219,39 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
1969
2219
|
for (const f of cmd.query ?? []) {
|
|
1970
2220
|
const v = optionOf(f);
|
|
1971
2221
|
if (v === void 0) {
|
|
1972
|
-
if (f.required) throw
|
|
1973
|
-
${f.flag} is required.`, 2);
|
|
2222
|
+
if (f.required) throw requiredError(f);
|
|
1974
2223
|
continue;
|
|
1975
2224
|
}
|
|
1976
|
-
const c = coerce(f, v);
|
|
1977
|
-
if (
|
|
1978
|
-
else qs2.
|
|
2225
|
+
const c = coerce(f, v, cmd, msg);
|
|
2226
|
+
if (f.type === "csv") qs2.set(f.key, c.join(","));
|
|
2227
|
+
else if (Array.isArray(c)) c.forEach((x) => qs2.append(f.key, String(x)));
|
|
2228
|
+
else qs2.set(f.key, c !== null && typeof c === "object" ? JSON.stringify(c) : String(c));
|
|
1979
2229
|
}
|
|
1980
2230
|
const url = qs2.size ? `${path}${path.includes("?") ? "&" : "?"}${qs2.toString()}` : path;
|
|
1981
2231
|
const confirm2 = cmd.confirm ?? cmd.method === "DELETE";
|
|
1982
|
-
|
|
1983
|
-
|
|
2232
|
+
const gate = () => {
|
|
2233
|
+
if (confirm2 && !options.yes) throw new CliError(msg.refusal(cmd.words, positionals), 2);
|
|
2234
|
+
};
|
|
2235
|
+
if (!cmd.validateFirst) gate();
|
|
1984
2236
|
const isWrite = cmd.method !== "GET" && cmd.method !== "DELETE";
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
2237
|
+
const takesBody = isWrite || Boolean(cmd.body?.length) || cmd.bodyFlags === true;
|
|
2238
|
+
let explicit = {};
|
|
2239
|
+
const overlay = {};
|
|
2240
|
+
if (takesBody) {
|
|
2241
|
+
explicit = readBodyOptions(options, msg);
|
|
1988
2242
|
for (const f of cmd.body ?? []) {
|
|
1989
2243
|
const v = optionOf(f);
|
|
1990
|
-
if (v
|
|
1991
|
-
|
|
2244
|
+
if (v !== void 0) setPath(overlay, f.key, coerce(f, v, cmd, msg));
|
|
2245
|
+
else if (cmd.validateFirst && f.required && getPath(explicit, f.key) === void 0) throw requiredError(f);
|
|
1992
2246
|
}
|
|
2247
|
+
}
|
|
2248
|
+
if (cmd.validateFirst) gate();
|
|
2249
|
+
let body;
|
|
2250
|
+
if (takesBody) {
|
|
1993
2251
|
let base16 = {};
|
|
1994
2252
|
if (cmd.rmw) {
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
from = from.replace(`:${name}`, encodeURIComponent(positionals[i]));
|
|
1998
|
-
});
|
|
1999
|
-
const current = await send(ctx, from, { method: "GET" }, cmd.anonymous);
|
|
2253
|
+
const from = fillPath(cmd, cmd.rmw.from ?? template, positionals, options.org);
|
|
2254
|
+
const current = await send(ctx, cmd, from, { method: "GET" });
|
|
2000
2255
|
const picked = cmd.rmw.pick ? cmd.rmw.pick(current.body) : current.body;
|
|
2001
2256
|
base16 = picked && typeof picked === "object" && !Array.isArray(picked) ? { ...picked } : {};
|
|
2002
2257
|
}
|
|
@@ -2005,13 +2260,15 @@ ${f.flag} is required.`, 2);
|
|
|
2005
2260
|
const v = getPath(overlay, f.key);
|
|
2006
2261
|
if (v !== void 0) setPath(body, f.key, v);
|
|
2007
2262
|
}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2263
|
+
if (!cmd.validateFirst) {
|
|
2264
|
+
for (const f of cmd.body ?? []) {
|
|
2265
|
+
if (f.required && getPath(body, f.key) === void 0) throw requiredError(f);
|
|
2266
|
+
}
|
|
2011
2267
|
}
|
|
2012
2268
|
if (!isWrite && Object.keys(body).length === 0) body = void 0;
|
|
2013
2269
|
if (isWrite && body && Object.keys(body).length === 0 && cmd.emptyBody === false) body = void 0;
|
|
2014
2270
|
}
|
|
2271
|
+
if (cmd.notice !== void 0) writeLine(ctx.io.stderr, cmd.notice);
|
|
2015
2272
|
try {
|
|
2016
2273
|
const res = await requestJson(ctx, url, { method: cmd.method, ...body !== void 0 ? { body } : {}, ...cmd.anonymous ? { auth: false } : {} });
|
|
2017
2274
|
render(ctx, cmd, positionals, res.status, res.body);
|
|
@@ -2021,25 +2278,42 @@ ${f.flag} is required.`, 2);
|
|
|
2021
2278
|
render(ctx, { ...cmd, method: "GET" }, positionals, err.status, err.body);
|
|
2022
2279
|
return 1;
|
|
2023
2280
|
}
|
|
2024
|
-
if (err instanceof HttpError) throw hostError(err);
|
|
2281
|
+
if (err instanceof HttpError && cmd.hostErrors !== false) throw hostError(err);
|
|
2025
2282
|
throw err;
|
|
2026
2283
|
}
|
|
2027
2284
|
}
|
|
2028
2285
|
function getPath(obj, key) {
|
|
2029
2286
|
return key.split(".").reduce((node, part) => node && typeof node === "object" ? node[part] : void 0, obj);
|
|
2030
2287
|
}
|
|
2031
|
-
async function send(ctx, url, init
|
|
2288
|
+
async function send(ctx, cmd, url, init) {
|
|
2032
2289
|
try {
|
|
2033
|
-
return await requestJson(ctx, url, { method: init.method, ...init.body !== void 0 ? { body: init.body } : {}, ...anonymous ? { auth: false } : {} });
|
|
2290
|
+
return await requestJson(ctx, url, { method: init.method, ...init.body !== void 0 ? { body: init.body } : {}, ...cmd.anonymous ? { auth: false } : {} });
|
|
2034
2291
|
} catch (err) {
|
|
2035
|
-
if (err instanceof HttpError) throw hostError(err);
|
|
2292
|
+
if (err instanceof HttpError && cmd.hostErrors !== false) throw hostError(err);
|
|
2036
2293
|
throw err;
|
|
2037
2294
|
}
|
|
2038
2295
|
}
|
|
2296
|
+
function tableRows(table3, body) {
|
|
2297
|
+
if (!table3.anyArray) return table3.key ? getPath(body ?? {}, table3.key) : body;
|
|
2298
|
+
if (Array.isArray(body)) return body;
|
|
2299
|
+
const keyed = table3.key ? getPath(body ?? {}, table3.key) : void 0;
|
|
2300
|
+
if (Array.isArray(keyed)) return keyed;
|
|
2301
|
+
return Object.values(body ?? {}).find((v) => Array.isArray(v)) ?? [];
|
|
2302
|
+
}
|
|
2039
2303
|
function render(ctx, cmd, positionals, status, rawBody) {
|
|
2040
2304
|
const body = cmd.transform ? cmd.transform(rawBody) : rawBody;
|
|
2305
|
+
const noContent = body === null || body === void 0 ? cmd.noContent : void 0;
|
|
2041
2306
|
if (ctx.json) {
|
|
2042
|
-
writeJson(ctx.io.stdout, body);
|
|
2307
|
+
writeJson(ctx.io.stdout, noContent !== void 0 ? { ok: true, status } : body);
|
|
2308
|
+
return;
|
|
2309
|
+
}
|
|
2310
|
+
if (cmd.rawText && body && typeof body === "object" && typeof body.raw === "string" && Object.keys(body).length === 1) {
|
|
2311
|
+
write(ctx.io.stdout, body.raw.endsWith("\n") ? body.raw : `${body.raw}
|
|
2312
|
+
`);
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
if (noContent !== void 0) {
|
|
2316
|
+
writeLine(ctx.io.stdout, noContent.replace("{status}", String(status)));
|
|
2043
2317
|
return;
|
|
2044
2318
|
}
|
|
2045
2319
|
if (cmd.human) {
|
|
@@ -2047,7 +2321,7 @@ function render(ctx, cmd, positionals, status, rawBody) {
|
|
|
2047
2321
|
return;
|
|
2048
2322
|
}
|
|
2049
2323
|
if (cmd.table) {
|
|
2050
|
-
const rows = cmd.table
|
|
2324
|
+
const rows = tableRows(cmd.table, body);
|
|
2051
2325
|
if (rows === null || rows === void 0) {
|
|
2052
2326
|
writeLine(ctx.io.stdout, cmd.table.empty ?? "None.");
|
|
2053
2327
|
return;
|
|
@@ -2065,7 +2339,7 @@ function render(ctx, cmd, positionals, status, rawBody) {
|
|
|
2065
2339
|
return;
|
|
2066
2340
|
}
|
|
2067
2341
|
}
|
|
2068
|
-
if (cmd.method === "GET") {
|
|
2342
|
+
if (cmd.method === "GET" || cmd.writeOutput === "json") {
|
|
2069
2343
|
writeJson(ctx.io.stdout, body);
|
|
2070
2344
|
return;
|
|
2071
2345
|
}
|
|
@@ -2404,7 +2678,8 @@ async function runInterrupts(ctx, argv) {
|
|
|
2404
2678
|
if (b.key) writeLine(ctx.io.stdout, `key: ${b.key}`);
|
|
2405
2679
|
writeLine(ctx.io.stdout, `resolved: ${b.resolved ? "yes" : "no"}`);
|
|
2406
2680
|
if (b.expiresAt) writeLine(ctx.io.stdout, `expiresAt: ${b.expiresAt}`);
|
|
2407
|
-
|
|
2681
|
+
const prompt = b.data && typeof b.data === "object" ? b.data.prompt : void 0;
|
|
2682
|
+
if (typeof prompt === "string") writeLine(ctx.io.stdout, `prompt: ${prompt}`);
|
|
2408
2683
|
if (b.resumeSchema !== void 0) writeLine(ctx.io.stdout, `resumeSchema: ${JSON.stringify(b.resumeSchema)}`);
|
|
2409
2684
|
return 0;
|
|
2410
2685
|
}
|
|
@@ -2460,67 +2735,6 @@ function lifecycleError(err) {
|
|
|
2460
2735
|
throw err;
|
|
2461
2736
|
}
|
|
2462
2737
|
|
|
2463
|
-
// src/cli/shared.ts
|
|
2464
|
-
function requireOrg(org2) {
|
|
2465
|
-
if (!org2) throw new CliError("This command is org-scoped \u2014 pass --org <orgId>.", 2);
|
|
2466
|
-
return String(org2);
|
|
2467
|
-
}
|
|
2468
|
-
function buildInputs(options) {
|
|
2469
|
-
const fromJson = options.inputsJson ? JSON.parse(options.inputsJson) : {};
|
|
2470
|
-
if (fromJson === null || typeof fromJson !== "object" || Array.isArray(fromJson)) {
|
|
2471
|
-
throw new CliError("--inputs-json must be a JSON object");
|
|
2472
|
-
}
|
|
2473
|
-
const inputs = { ...fromJson };
|
|
2474
|
-
for (const pair of options.input ?? []) {
|
|
2475
|
-
const eq = pair.indexOf("=");
|
|
2476
|
-
if (eq <= 0) throw new CliError(`--input must be key=value, got: ${pair}`);
|
|
2477
|
-
inputs[pair.slice(0, eq)] = parseInputValue(pair.slice(eq + 1));
|
|
2478
|
-
}
|
|
2479
|
-
return inputs;
|
|
2480
|
-
}
|
|
2481
|
-
function parseInputValue(value) {
|
|
2482
|
-
try {
|
|
2483
|
-
return JSON.parse(value);
|
|
2484
|
-
} catch {
|
|
2485
|
-
return value;
|
|
2486
|
-
}
|
|
2487
|
-
}
|
|
2488
|
-
function parseNodeVersion(version) {
|
|
2489
|
-
const [major, minor, patch] = version.split(".").map((v) => Number(v));
|
|
2490
|
-
return { major: major || 0, minor: minor || 0, patch: patch || 0 };
|
|
2491
|
-
}
|
|
2492
|
-
function defaultApiKeyFor(baseUrl) {
|
|
2493
|
-
try {
|
|
2494
|
-
const host = new URL(baseUrl).hostname;
|
|
2495
|
-
if (host === "localhost" || host === "127.0.0.1" || host === "::1") return DEFAULT_API_KEY;
|
|
2496
|
-
} catch {
|
|
2497
|
-
return void 0;
|
|
2498
|
-
}
|
|
2499
|
-
return void 0;
|
|
2500
|
-
}
|
|
2501
|
-
function normalizeBaseUrl(value) {
|
|
2502
|
-
if (!value) return DEFAULT_BASE_URL;
|
|
2503
|
-
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
2504
|
-
}
|
|
2505
|
-
function npmCommand() {
|
|
2506
|
-
return process.platform === "win32" ? "npm.cmd" : "npm";
|
|
2507
|
-
}
|
|
2508
|
-
function ok(name, message) {
|
|
2509
|
-
return { status: "ok", name, message };
|
|
2510
|
-
}
|
|
2511
|
-
function warn(name, message) {
|
|
2512
|
-
return { status: "warn", name, message };
|
|
2513
|
-
}
|
|
2514
|
-
function fail(name, message) {
|
|
2515
|
-
return { status: "fail", name, message };
|
|
2516
|
-
}
|
|
2517
|
-
function formatCheckTable(checks) {
|
|
2518
|
-
return formatTable(
|
|
2519
|
-
checks.map((c) => ({ status: c.status.toUpperCase(), check: c.name, message: c.message })),
|
|
2520
|
-
["status", "check", "message"]
|
|
2521
|
-
);
|
|
2522
|
-
}
|
|
2523
|
-
|
|
2524
2738
|
// src/cli/contentHelpers.ts
|
|
2525
2739
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
2526
2740
|
import { resolve as resolvePath2 } from "node:path";
|
|
@@ -2873,19 +3087,37 @@ import { readFileSync as readFileSync6 } from "node:fs";
|
|
|
2873
3087
|
|
|
2874
3088
|
// src/cli/requestHelpers.ts
|
|
2875
3089
|
var NOT_SERVED = /* @__PURE__ */ new Set([404, 405, 501]);
|
|
3090
|
+
function presentsCredential(ctx, req2) {
|
|
3091
|
+
if (req2.auth !== false && ctx.apiKey) return true;
|
|
3092
|
+
return Object.keys(req2.headers ?? {}).some((h) => /^(authorization|cookie)$/i.test(h));
|
|
3093
|
+
}
|
|
2876
3094
|
async function requestNormativeOrHost(ctx, normativePath, hostPath, options = {}) {
|
|
2877
3095
|
const { forceHost, ...req2 } = options;
|
|
3096
|
+
let anonymousRetryOf;
|
|
2878
3097
|
if (!forceHost) {
|
|
2879
3098
|
try {
|
|
2880
3099
|
const res2 = await requestJson(ctx, normativePath, req2);
|
|
2881
3100
|
if (ctx.verbose) writeLine(ctx.io.stderr, `openwop: served by the normative path ${normativePath}`);
|
|
2882
3101
|
return { ...res2, path: normativePath, via: "normative" };
|
|
2883
3102
|
} catch (err) {
|
|
2884
|
-
if (!(err instanceof HttpError)
|
|
3103
|
+
if (!(err instanceof HttpError)) throw err;
|
|
3104
|
+
if (isNoCredentialChallenge(err) && !presentsCredential(ctx, req2)) anonymousRetryOf = err;
|
|
3105
|
+
else if (!NOT_SERVED.has(err.status)) throw err;
|
|
2885
3106
|
}
|
|
2886
3107
|
}
|
|
2887
|
-
|
|
2888
|
-
|
|
3108
|
+
let res;
|
|
3109
|
+
try {
|
|
3110
|
+
res = await requestJson(ctx, hostPath, req2);
|
|
3111
|
+
} catch (err) {
|
|
3112
|
+
if (anonymousRetryOf && err instanceof HttpError && (NOT_SERVED.has(err.status) || err.status === 401)) throw anonymousRetryOf;
|
|
3113
|
+
throw err;
|
|
3114
|
+
}
|
|
3115
|
+
if (anonymousRetryOf) {
|
|
3116
|
+
const sent = (await resolveRequest(ctx, hostPath, {})).path;
|
|
3117
|
+
writeLine(ctx.io.stderr, `openwop: no credential sent \u2014 showing this host's anonymous view (${sent}), not your workspace. Pass --api-key, or run \`openwop onboard\` to save one.`);
|
|
3118
|
+
} else if (ctx.verbose) {
|
|
3119
|
+
writeLine(ctx.io.stderr, `openwop: served by the host-extension path ${hostPath}`);
|
|
3120
|
+
}
|
|
2889
3121
|
return { ...res, path: hostPath, via: "host" };
|
|
2890
3122
|
}
|
|
2891
3123
|
function failClosedOn404(err, surface) {
|
|
@@ -4847,6 +5079,8 @@ GET /v1/agents and GET /v1/agents/{agentId}. When the host does not serve the
|
|
|
4847
5079
|
normative read (404/405/501) they fall back to the host-extension alias
|
|
4848
5080
|
GET /v1/host/openwop-app/agents[/{agentId}]; --host forces the alias (which also
|
|
4849
5081
|
reports the host's runtime posture); --verbose names the path that answered.
|
|
5082
|
+
With no credential, a v2 host refuses the normative read (401); the command
|
|
5083
|
+
then shows the host's anonymous view from the alias and says so on stderr.
|
|
4850
5084
|
'run' dispatches one agent turn via
|
|
4851
5085
|
POST /v1/host/openwop-app/agents/{agentId}/dispatch \u2014 the tool surface is filtered to
|
|
4852
5086
|
the agent's toolAllowlist (RFC 0002 \xA7A14), task/return payloads are validated
|
|
@@ -5170,6 +5404,8 @@ GET /v1/agents/roster. The normative view carries only protocol-shaped entries
|
|
|
5170
5404
|
(the host may omit host-only entries such as advisors); when the host does not
|
|
5171
5405
|
serve it (404/405/501), or with --host, 'list' reads the host-extension
|
|
5172
5406
|
GET /v1/host/openwop-app/roster instead (--verbose names the path that answered).
|
|
5407
|
+
With no credential, a v2 host refuses the normative read (401); 'list' then
|
|
5408
|
+
shows the host's anonymous view from the alias and says so on stderr.
|
|
5173
5409
|
'get'/'create'/'update'/'delete' drive the host-extension surface
|
|
5174
5410
|
GET/POST/PATCH/DELETE /v1/host/openwop-app/roster[/{rosterId}].
|
|
5175
5411
|
|
|
@@ -5352,7 +5588,9 @@ field).
|
|
|
5352
5588
|
GET /v1/agents/org-chart/{departmentId}. When the host does not serve it
|
|
5353
5589
|
(404/405/501 on 'get'), or with --host, they read the host-extension
|
|
5354
5590
|
GET /v1/host/openwop-app/org-chart[/{departmentId}] (--verbose names the path
|
|
5355
|
-
that answered).
|
|
5591
|
+
that answered). With no credential, a v2 host refuses the normative read (401);
|
|
5592
|
+
'get'/'dept' then show the host's anonymous view from the alias and say so on
|
|
5593
|
+
stderr. 'set'/'clear' drive the host-extension PUT/DELETE
|
|
5356
5594
|
/v1/host/openwop-app/org-chart. 'set' replaces the whole chart from a JSON file
|
|
5357
5595
|
with { "departments": [...], "members": [...] }.
|
|
5358
5596
|
|
|
@@ -5734,9 +5972,7 @@ async function boardWatch(ctx, argv) {
|
|
|
5734
5972
|
write(ctx.io.stdout, "Usage: openwop kanban watch <boardId>\n");
|
|
5735
5973
|
return options.help ? 0 : 2;
|
|
5736
5974
|
}
|
|
5737
|
-
const {
|
|
5738
|
-
const url = new URL(path.replace(/^\//, ""), ctx.baseUrl.endsWith("/") ? ctx.baseUrl : `${ctx.baseUrl}/`);
|
|
5739
|
-
if (ctx.apiKey) headers.authorization = `Bearer ${ctx.apiKey}`;
|
|
5975
|
+
const { url, headers } = await resolveStreamRequest(ctx, `/v1/host/openwop-app/kanban/boards/${encodeURIComponent(positionals[0])}/events`, {});
|
|
5740
5976
|
const res = await ctx.fetchImpl(url, { headers });
|
|
5741
5977
|
if (!res.ok || !res.body) {
|
|
5742
5978
|
throw new CliError(`Could not open the board event stream (HTTP ${res.status}).`, 1);
|
|
@@ -6061,7 +6297,6 @@ async function brandAsset(ctx, argv) {
|
|
|
6061
6297
|
}
|
|
6062
6298
|
|
|
6063
6299
|
// src/cli/resourceCommands.ts
|
|
6064
|
-
import { readFileSync as readFileSync9 } from "node:fs";
|
|
6065
6300
|
function parseField(spec) {
|
|
6066
6301
|
let s = spec;
|
|
6067
6302
|
let flagOverride;
|
|
@@ -6076,35 +6311,6 @@ function parseField(spec) {
|
|
|
6076
6311
|
const flag = `--${flagOverride ?? key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`).replace(/_/g, "-")}`;
|
|
6077
6312
|
return { key, flag, type, required };
|
|
6078
6313
|
}
|
|
6079
|
-
function coerce2(field, raw) {
|
|
6080
|
-
switch (field.type) {
|
|
6081
|
-
case "number": {
|
|
6082
|
-
const n = Number(raw);
|
|
6083
|
-
if (raw.trim() === "" || !Number.isFinite(n)) throw new CliError(`${field.flag} must be a number, got: ${raw}`);
|
|
6084
|
-
return n;
|
|
6085
|
-
}
|
|
6086
|
-
case "bool":
|
|
6087
|
-
if (raw === "true" || raw === "yes" || raw === "1") return true;
|
|
6088
|
-
if (raw === "false" || raw === "no" || raw === "0") return false;
|
|
6089
|
-
throw new CliError(`${field.flag} must be true or false, got: ${raw}`);
|
|
6090
|
-
case "json":
|
|
6091
|
-
try {
|
|
6092
|
-
return JSON.parse(raw);
|
|
6093
|
-
} catch {
|
|
6094
|
-
throw new CliError(`${field.flag} must be valid JSON`);
|
|
6095
|
-
}
|
|
6096
|
-
case "list":
|
|
6097
|
-
return raw.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
|
|
6098
|
-
case "file":
|
|
6099
|
-
try {
|
|
6100
|
-
return readFileSync9(raw, "utf8").replace(/\r?\n$/, "");
|
|
6101
|
-
} catch {
|
|
6102
|
-
throw new CliError(`Cannot read ${field.flag} ${raw}`);
|
|
6103
|
-
}
|
|
6104
|
-
default:
|
|
6105
|
-
return raw;
|
|
6106
|
-
}
|
|
6107
|
-
}
|
|
6108
6314
|
function hasOrg(route) {
|
|
6109
6315
|
return /:org(?![A-Za-z0-9_])/.test(route);
|
|
6110
6316
|
}
|
|
@@ -6147,142 +6353,62 @@ ${footer ? `
|
|
|
6147
6353
|
${footer.trim()}
|
|
6148
6354
|
` : ""}`;
|
|
6149
6355
|
}
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
if (spec.cmd.every((w, i) => argv[i] === w) && (!best || spec.cmd.length > best.spec.cmd.length)) {
|
|
6155
|
-
best = { spec, rest: argv.slice(spec.cmd.length) };
|
|
6156
|
-
}
|
|
6157
|
-
}
|
|
6158
|
-
return best;
|
|
6159
|
-
}
|
|
6160
|
-
function readBodyOption2(options) {
|
|
6161
|
-
let raw;
|
|
6162
|
-
if (options.bodyFile) {
|
|
6163
|
-
try {
|
|
6164
|
-
raw = readFileSync9(String(options.bodyFile), "utf8");
|
|
6165
|
-
} catch {
|
|
6166
|
-
throw new CliError(`Cannot read --body-file ${options.bodyFile}`);
|
|
6167
|
-
}
|
|
6168
|
-
} else if (options.body !== void 0) {
|
|
6169
|
-
raw = String(options.body);
|
|
6170
|
-
}
|
|
6171
|
-
if (raw === void 0) return {};
|
|
6172
|
-
let parsed;
|
|
6173
|
-
try {
|
|
6174
|
-
parsed = JSON.parse(raw);
|
|
6175
|
-
} catch {
|
|
6176
|
-
throw new CliError("--body/--body-file must be valid JSON");
|
|
6177
|
-
}
|
|
6178
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError("--body/--body-file must be a JSON object");
|
|
6179
|
-
return parsed;
|
|
6356
|
+
var TYPES = { string: "string", number: "number", bool: "bool", json: "json", list: "csv", file: "file" };
|
|
6357
|
+
function toField(spec) {
|
|
6358
|
+
const f = parseField(spec);
|
|
6359
|
+
return { flag: f.flag, key: f.key, type: TYPES[f.type] ?? "string", required: f.required };
|
|
6180
6360
|
}
|
|
6181
|
-
|
|
6182
|
-
const queryFields = (spec.query ?? []).map(parseField);
|
|
6183
|
-
const bodyFields = (spec.body ?? []).map(parseField);
|
|
6184
|
-
const allowBody = Boolean(spec.body || spec.rawBody);
|
|
6185
|
-
const valueFlags = [...queryFields, ...bodyFields].map((f) => f.flag);
|
|
6186
|
-
if (hasOrg(spec.route)) valueFlags.push("--org");
|
|
6187
|
-
if (allowBody) valueFlags.push("--body", "--body-file");
|
|
6188
|
-
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--yes"], value: valueFlags });
|
|
6361
|
+
function toRouteCmd(group, spec) {
|
|
6189
6362
|
const usage6 = `Usage: ${usageLine2(group, spec)}
|
|
6190
6363
|
${spec.method} ${spec.route} \u2014 ${spec.summary}
|
|
6191
6364
|
`;
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
}
|
|
6196
|
-
const params = paramsOf(spec.route);
|
|
6197
|
-
if (positionals.length !== params.length) {
|
|
6198
|
-
write(ctx.io.stderr, usage6);
|
|
6199
|
-
return 2;
|
|
6200
|
-
}
|
|
6201
|
-
const optionValue = (f) => options[f.flag.slice(2).replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase())];
|
|
6202
|
-
const fill = (route) => {
|
|
6203
|
-
let i = 0;
|
|
6204
|
-
return route.replace(/:[A-Za-z][A-Za-z0-9_]*/g, (m) => {
|
|
6205
|
-
if (m === ":org") return encodeURIComponent(requireOrg(options.org));
|
|
6206
|
-
return encodeURIComponent(positionals[i++] ?? "");
|
|
6207
|
-
});
|
|
6208
|
-
};
|
|
6209
|
-
let path = fill(spec.route);
|
|
6210
|
-
const qs2 = new URLSearchParams();
|
|
6211
|
-
for (const f of queryFields) {
|
|
6212
|
-
const v = optionValue(f);
|
|
6213
|
-
if (v === void 0) {
|
|
6214
|
-
if (f.required) throw new CliError(`${f.flag} is required.
|
|
6215
|
-
${usage6}`);
|
|
6216
|
-
continue;
|
|
6217
|
-
}
|
|
6218
|
-
const c = coerce2(f, String(v));
|
|
6219
|
-
qs2.set(f.key, Array.isArray(c) ? c.join(",") : typeof c === "object" ? JSON.stringify(c) : String(c));
|
|
6220
|
-
}
|
|
6221
|
-
const q = qs2.toString();
|
|
6222
|
-
if (q) path += `${path.includes("?") ? "&" : "?"}${q}`;
|
|
6223
|
-
let body;
|
|
6224
|
-
if (allowBody || spec.method === "POST" || spec.method === "PUT" || spec.method === "PATCH") {
|
|
6225
|
-
body = allowBody ? readBodyOption2(options) : {};
|
|
6226
|
-
for (const f of bodyFields) {
|
|
6227
|
-
const v = optionValue(f);
|
|
6228
|
-
if (v !== void 0) body[f.key] = coerce2(f, String(v));
|
|
6229
|
-
else if (f.required && body[f.key] === void 0) throw new CliError(`${f.flag} is required.
|
|
6230
|
-
${usage6}`);
|
|
6231
|
-
}
|
|
6232
|
-
if (spec.method === "DELETE" && Object.keys(body).length === 0) body = void 0;
|
|
6233
|
-
}
|
|
6234
|
-
if (spec.confirm && !options.yes) {
|
|
6235
|
-
throw new CliError(`Refusing to ${spec.summary.charAt(0).toLowerCase()}${spec.summary.slice(1).replace(/\.$/, "")} without --yes.`, 2);
|
|
6236
|
-
}
|
|
6237
|
-
if (spec.rmw) {
|
|
6238
|
-
const current = await requestJson(ctx, fill(spec.rmw), { auth: spec.auth });
|
|
6239
|
-
const base16 = spec.rmwKey ? current.body?.[spec.rmwKey] : current.body;
|
|
6240
|
-
body = { ...base16 && typeof base16 === "object" && !Array.isArray(base16) ? base16 : {}, ...body ?? {} };
|
|
6241
|
-
}
|
|
6242
|
-
if (spec.notice) writeLine(ctx.io.stderr, spec.notice);
|
|
6243
|
-
const res = await requestJson(ctx, path, {
|
|
6365
|
+
const rmwKey = spec.rmwKey;
|
|
6366
|
+
return {
|
|
6367
|
+
words: spec.cmd,
|
|
6244
6368
|
method: spec.method,
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6369
|
+
path: spec.route,
|
|
6370
|
+
summary: spec.summary,
|
|
6371
|
+
query: (spec.query ?? []).map(toField),
|
|
6372
|
+
body: (spec.body ?? []).map(toField),
|
|
6373
|
+
orgFlag: true,
|
|
6374
|
+
bodyFlags: Boolean(spec.body || spec.rawBody),
|
|
6375
|
+
emptyBody: true,
|
|
6376
|
+
confirm: Boolean(spec.confirm),
|
|
6377
|
+
anonymous: spec.auth === false,
|
|
6378
|
+
...spec.list ? { table: { key: spec.list.key, columns: spec.list.columns, empty: spec.list.empty ?? "None.", anyArray: true } } : {},
|
|
6379
|
+
...spec.rmw ? { rmw: { from: spec.rmw, pick: (body) => rmwKey ? body?.[rmwKey] : body } } : {},
|
|
6380
|
+
...spec.notice !== void 0 ? { notice: spec.notice } : {},
|
|
6381
|
+
rawText: true,
|
|
6382
|
+
// every spec, as before — `text` only documents the route
|
|
6383
|
+
noContent: spec.method === "DELETE" ? "Deleted." : "Done (HTTP {status}).",
|
|
6384
|
+
writeOutput: "json",
|
|
6385
|
+
hostErrors: false,
|
|
6386
|
+
validateFirst: true,
|
|
6387
|
+
strictNumbers: true,
|
|
6388
|
+
usageText: usage6,
|
|
6389
|
+
messages: {
|
|
6390
|
+
required: (flag, usageText) => `${flag} is required.
|
|
6391
|
+
${usageText}`,
|
|
6392
|
+
refusal: () => `Refusing to ${spec.summary.charAt(0).toLowerCase()}${spec.summary.slice(1).replace(/\.$/, "")} without --yes.`,
|
|
6393
|
+
invalidNumber: (flag, raw) => `${flag} must be a number, got: ${raw}`,
|
|
6394
|
+
invalidBool: (flag, raw) => `${flag} must be true or false, got: ${raw}`,
|
|
6395
|
+
invalidJson: (flag) => `${flag} must be valid JSON`,
|
|
6396
|
+
unreadable: (flag, path) => `Cannot read ${flag} ${path}`,
|
|
6397
|
+
unreadableBody: (path) => `Cannot read --body-file ${path}`,
|
|
6398
|
+
invalidBodyJson: "--body/--body-file must be valid JSON",
|
|
6399
|
+
bodyNotObject: "--body/--body-file must be a JSON object"
|
|
6269
6400
|
}
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
}), spec.list.columns));
|
|
6278
|
-
return 0;
|
|
6279
|
-
}
|
|
6280
|
-
writeJson(ctx.io.stdout, body);
|
|
6281
|
-
return 0;
|
|
6401
|
+
};
|
|
6402
|
+
}
|
|
6403
|
+
function matchSpec(specs, argv) {
|
|
6404
|
+
return matchRoute(specs.map((spec) => ({ words: spec.cmd, spec })), argv)?.spec;
|
|
6405
|
+
}
|
|
6406
|
+
async function runSpec(ctx, group, spec, argv) {
|
|
6407
|
+
return runRoute(ctx, group, toRouteCmd(group, spec), [...spec.cmd, ...argv]);
|
|
6282
6408
|
}
|
|
6283
6409
|
async function dispatchSpecs(ctx, group, specs, argv) {
|
|
6284
|
-
const
|
|
6285
|
-
return
|
|
6410
|
+
const spec = matchSpec(specs, argv);
|
|
6411
|
+
return spec ? runSpec(ctx, group, spec, argv.slice(spec.cmd.length)) : void 0;
|
|
6286
6412
|
}
|
|
6287
6413
|
function specsUsage(group, specs) {
|
|
6288
6414
|
return specs.map((s) => ` ${usageLine2(group, s)}
|
|
@@ -6293,12 +6419,12 @@ async function runResourceGroup(ctx, group, help, specs, argv) {
|
|
|
6293
6419
|
write(ctx.io.stdout, help);
|
|
6294
6420
|
return 0;
|
|
6295
6421
|
}
|
|
6296
|
-
const
|
|
6297
|
-
if (!
|
|
6422
|
+
const spec = matchSpec(specs, argv);
|
|
6423
|
+
if (!spec) {
|
|
6298
6424
|
throw new CliError(`Unknown ${group} command: ${argv.filter((a) => !a.startsWith("-")).slice(0, 2).join(" ")}
|
|
6299
6425
|
Run \`openwop ${group} --help\` for usage.`);
|
|
6300
6426
|
}
|
|
6301
|
-
return runSpec(ctx, group,
|
|
6427
|
+
return runSpec(ctx, group, spec, argv.slice(spec.cmd.length));
|
|
6302
6428
|
}
|
|
6303
6429
|
|
|
6304
6430
|
// src/cli/crmOrg.ts
|
|
@@ -6975,7 +7101,7 @@ async function sharingFrameViews(ctx, argv) {
|
|
|
6975
7101
|
}
|
|
6976
7102
|
|
|
6977
7103
|
// src/cli/marketingShared.ts
|
|
6978
|
-
import { readFileSync as
|
|
7104
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
6979
7105
|
import { resolve as resolvePath5 } from "node:path";
|
|
6980
7106
|
var APP = "/v1/host/openwop-app";
|
|
6981
7107
|
async function dispatchTable(ctx, group, help, table3, argv, defaultSub) {
|
|
@@ -7033,14 +7159,14 @@ function jsonObject(text, flag) {
|
|
|
7033
7159
|
}
|
|
7034
7160
|
function readText(ctx, path, flag) {
|
|
7035
7161
|
try {
|
|
7036
|
-
return
|
|
7162
|
+
return readFileSync9(resolvePath5(ctx.cwd, path), "utf8");
|
|
7037
7163
|
} catch (err) {
|
|
7038
7164
|
throw new CliError(`Cannot read ${flag} ${path}: ${err instanceof Error ? err.message : String(err)}`, 2);
|
|
7039
7165
|
}
|
|
7040
7166
|
}
|
|
7041
7167
|
function readBase64(ctx, path, flag) {
|
|
7042
7168
|
try {
|
|
7043
|
-
return
|
|
7169
|
+
return readFileSync9(resolvePath5(ctx.cwd, path)).toString("base64");
|
|
7044
7170
|
} catch (err) {
|
|
7045
7171
|
throw new CliError(`Cannot read ${flag} ${path}: ${err instanceof Error ? err.message : String(err)}`, 2);
|
|
7046
7172
|
}
|
|
@@ -8652,10 +8778,10 @@ Run \`openwop documents --help\` for usage.`);
|
|
|
8652
8778
|
if (!id) return usage(ctx, "add-version <documentId> --org <orgId> (--content <text> | --content-file <path>)");
|
|
8653
8779
|
let content = options.content !== void 0 ? String(options.content) : void 0;
|
|
8654
8780
|
if (options.contentFile) {
|
|
8655
|
-
const { readFileSync:
|
|
8781
|
+
const { readFileSync: readFileSync22 } = await import("node:fs");
|
|
8656
8782
|
const { resolve: resolve5 } = await import("node:path");
|
|
8657
8783
|
try {
|
|
8658
|
-
content =
|
|
8784
|
+
content = readFileSync22(resolve5(ctx.cwd, String(options.contentFile)), "utf8");
|
|
8659
8785
|
} catch (err) {
|
|
8660
8786
|
throw new CliError(`Cannot read --content-file ${String(options.contentFile)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
8661
8787
|
}
|
|
@@ -9242,7 +9368,7 @@ Run \`openwop reviews --help\` for usage.`);
|
|
|
9242
9368
|
}
|
|
9243
9369
|
|
|
9244
9370
|
// src/cli/adminShared.ts
|
|
9245
|
-
import { readFileSync as
|
|
9371
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
9246
9372
|
import { resolve as resolve2 } from "node:path";
|
|
9247
9373
|
|
|
9248
9374
|
// src/prompt.ts
|
|
@@ -9361,7 +9487,7 @@ async function readSecretInput(ctx, options, label) {
|
|
|
9361
9487
|
let value;
|
|
9362
9488
|
if (options.valueFile !== void 0) {
|
|
9363
9489
|
try {
|
|
9364
|
-
value =
|
|
9490
|
+
value = readFileSync10(resolve2(ctx.cwd, String(options.valueFile)), "utf8").replace(/\r?\n$/, "");
|
|
9365
9491
|
} catch (err) {
|
|
9366
9492
|
throw new CliError(`Cannot read --value-file ${options.valueFile}: ${err instanceof Error ? err.message : String(err)}`, 2);
|
|
9367
9493
|
}
|
|
@@ -10315,7 +10441,7 @@ async function runCampaignsOrch(ctx, argv) {
|
|
|
10315
10441
|
}
|
|
10316
10442
|
|
|
10317
10443
|
// src/cli/notebooks.ts
|
|
10318
|
-
import { readFileSync as
|
|
10444
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
10319
10445
|
import { extname as extname3, resolve as resolvePath7 } from "node:path";
|
|
10320
10446
|
var BASE9 = "/v1/host/openwop-app/notebooks";
|
|
10321
10447
|
var NOTEBOOKS_HELP = `Usage:
|
|
@@ -10390,7 +10516,7 @@ var MIME = {
|
|
|
10390
10516
|
function readFileBase64(ctx, file, contentType) {
|
|
10391
10517
|
let buf;
|
|
10392
10518
|
try {
|
|
10393
|
-
buf =
|
|
10519
|
+
buf = readFileSync11(resolvePath7(ctx.cwd, file));
|
|
10394
10520
|
} catch (err) {
|
|
10395
10521
|
throw new CliError(`Cannot read file ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
10396
10522
|
}
|
|
@@ -12319,6 +12445,13 @@ honouring the server's \`retry:\` field), and no event is printed twice.
|
|
|
12319
12445
|
debug, or a comma list of updates/messages/debug
|
|
12320
12446
|
(\`values\` never combines). A host that does not serve
|
|
12321
12447
|
the mode answers 400 unsupported_stream_mode.
|
|
12448
|
+
--idle-timeout-ms N a connection that delivers no bytes for N ms (default
|
|
12449
|
+
45000; 0 disables) is treated as dropped and resumed. A
|
|
12450
|
+
stream whose headers do not arrive within 10 s is given
|
|
12451
|
+
up on and followed by polling, with a hint on stderr.
|
|
12452
|
+
Streams are read from the stream origin: --stream-base-url (global), else the
|
|
12453
|
+
host's advertised \`streamBase\` (https only), else --base-url \u2014 some front
|
|
12454
|
+
doors buffer event streams entirely (e.g. a CDN rewrite).
|
|
12322
12455
|
Exit 0 when the run completes, 1 when it fails or is cancelled. \`runs list\` pages with --cursor: pass the
|
|
12323
12456
|
\`nextCursor\` the previous page printed.
|
|
12324
12457
|
|
|
@@ -12541,7 +12674,7 @@ async function runRunsBulkCancel(ctx, argv) {
|
|
|
12541
12674
|
return 0;
|
|
12542
12675
|
}
|
|
12543
12676
|
async function runRunsEvents(ctx, argv) {
|
|
12544
|
-
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--follow", "--no-stream"], value: ["--since", "--limit", "--last-event-id", "--stream-mode", "--timeout-ms"] });
|
|
12677
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--follow", "--no-stream"], value: ["--since", "--limit", "--last-event-id", "--stream-mode", "--timeout-ms", "--idle-timeout-ms"] });
|
|
12545
12678
|
if (options.help || positionals.length !== 1) {
|
|
12546
12679
|
write(ctx.io.stdout, "Usage: openwop runs events <runId> [--since <sequence>] [--limit <n>] [--json]\n openwop runs events <runId> --follow [--since <sequence> | --last-event-id <id>] [--stream-mode <mode>] [--no-stream] [--json]\n");
|
|
12547
12680
|
return options.help ? 0 : 2;
|
|
@@ -12579,6 +12712,7 @@ async function followRunEvents(ctx, runId, options) {
|
|
|
12579
12712
|
...start,
|
|
12580
12713
|
useStream: !options.noStream,
|
|
12581
12714
|
timeoutMs,
|
|
12715
|
+
...options.idleTimeoutMs !== void 0 ? { idleTimeoutMs: nonNegativeInt(options.idleTimeoutMs, "--idle-timeout-ms") } : {},
|
|
12582
12716
|
onEvent: (ev) => {
|
|
12583
12717
|
if (isTerminalRunEvent(ev)) terminalType = canonicalEventType(ev.type);
|
|
12584
12718
|
if (ctx.json) {
|
|
@@ -12818,10 +12952,15 @@ async function runRunsRedrive(ctx, argv) {
|
|
|
12818
12952
|
})), ["runId", "redriveRunId", "error"]));
|
|
12819
12953
|
return anyFailed ? 1 : 0;
|
|
12820
12954
|
}
|
|
12955
|
+
function nonNegativeInt(value, flag) {
|
|
12956
|
+
const n = Number(value);
|
|
12957
|
+
if (!Number.isInteger(n) || n < 0) throw new CliError(`${flag} must be a non-negative integer (got ${String(value)})`);
|
|
12958
|
+
return n;
|
|
12959
|
+
}
|
|
12821
12960
|
|
|
12822
12961
|
// src/cli/chat.ts
|
|
12823
12962
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
12824
|
-
import { readFileSync as
|
|
12963
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
|
|
12825
12964
|
var CHAT_BASE = "/v1/host/openwop-app/chat";
|
|
12826
12965
|
var CHAT_SUBCOMMANDS = ["repl", "sessions", "session", "messages", "message", "participants", "open", "feedback", "models", "search", "export", "import", "tools"];
|
|
12827
12966
|
var CHAT_HELP = `Usage:
|
|
@@ -13411,7 +13550,7 @@ Requires a signed-in account.
|
|
|
13411
13550
|
}
|
|
13412
13551
|
let data;
|
|
13413
13552
|
try {
|
|
13414
|
-
data = JSON.parse(
|
|
13553
|
+
data = JSON.parse(readFileSync12(String(options.file), "utf8"));
|
|
13415
13554
|
} catch (err) {
|
|
13416
13555
|
throw new CliError(`--file: cannot read JSON from ${options.file} (${err instanceof Error ? err.message : String(err)})`, 2);
|
|
13417
13556
|
}
|
|
@@ -13505,7 +13644,7 @@ Run \`openwop chat tools --help\` for usage.`, 2);
|
|
|
13505
13644
|
}
|
|
13506
13645
|
|
|
13507
13646
|
// src/cli/relayShared.ts
|
|
13508
|
-
import { existsSync as existsSync4, readFileSync as
|
|
13647
|
+
import { existsSync as existsSync4, readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync3, rmSync as rmSync2, chmodSync as chmodSync3 } from "node:fs";
|
|
13509
13648
|
import { dirname as dirname4, join as join5 } from "node:path";
|
|
13510
13649
|
var MESSAGING_BASE = "/v1/host/openwop-app/messaging";
|
|
13511
13650
|
var DEVICE_TOKEN_HEADER = "x-openwop-device-token";
|
|
@@ -13516,7 +13655,7 @@ function relayCredsPath(env) {
|
|
|
13516
13655
|
function loadRelayConfig(ctx) {
|
|
13517
13656
|
try {
|
|
13518
13657
|
const p = relayCredsPath(ctx.env);
|
|
13519
|
-
if (existsSync4(p)) return JSON.parse(
|
|
13658
|
+
if (existsSync4(p)) return JSON.parse(readFileSync13(p, "utf8"));
|
|
13520
13659
|
} catch {
|
|
13521
13660
|
}
|
|
13522
13661
|
return {};
|
|
@@ -13552,7 +13691,7 @@ function relayLogPath(env) {
|
|
|
13552
13691
|
function readRelayRecord(env) {
|
|
13553
13692
|
try {
|
|
13554
13693
|
const p = relayPidPath(env);
|
|
13555
|
-
return existsSync4(p) ? JSON.parse(
|
|
13694
|
+
return existsSync4(p) ? JSON.parse(readFileSync13(p, "utf8")) : null;
|
|
13556
13695
|
} catch {
|
|
13557
13696
|
return null;
|
|
13558
13697
|
}
|
|
@@ -14147,7 +14286,7 @@ Usage: openwop notify <email|sms> --to <addr> --text <msg> [--subject s]`);
|
|
|
14147
14286
|
|
|
14148
14287
|
// src/cli/relay.ts
|
|
14149
14288
|
import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
|
|
14150
|
-
import { existsSync as existsSync5, readFileSync as
|
|
14289
|
+
import { existsSync as existsSync5, readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync4, rmSync as rmSync3 } from "node:fs";
|
|
14151
14290
|
import { dirname as dirname5 } from "node:path";
|
|
14152
14291
|
import { fileURLToPath } from "node:url";
|
|
14153
14292
|
var RELAY_HELP = `Usage:
|
|
@@ -14498,7 +14637,7 @@ async function runRelayLogs(ctx, argv) {
|
|
|
14498
14637
|
const r = spawnSync3("tail", ["-f", logPath], { stdio: "inherit" });
|
|
14499
14638
|
if (r.status === 0 || r.signal) return 0;
|
|
14500
14639
|
}
|
|
14501
|
-
write(ctx.io.stdout,
|
|
14640
|
+
write(ctx.io.stdout, readFileSync14(logPath, "utf8"));
|
|
14502
14641
|
return 0;
|
|
14503
14642
|
}
|
|
14504
14643
|
|
|
@@ -14867,7 +15006,7 @@ async function runProvidersTest(ctx, argv) {
|
|
|
14867
15006
|
// src/cli/packs.ts
|
|
14868
15007
|
import { createHash, createPrivateKey, createPublicKey, generateKeyPairSync, sign as ed25519Sign, verify as ed25519Verify } from "node:crypto";
|
|
14869
15008
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
14870
|
-
import { existsSync as existsSync6, readFileSync as
|
|
15009
|
+
import { existsSync as existsSync6, readFileSync as readFileSync15, writeFileSync as writeFileSync10, mkdirSync as mkdirSync5, readdirSync, statSync } from "node:fs";
|
|
14871
15010
|
import { join as join6, resolve as resolvePath8 } from "node:path";
|
|
14872
15011
|
import { homedir as homedir3 } from "node:os";
|
|
14873
15012
|
var PACKS_HELP = `Usage:
|
|
@@ -15124,7 +15263,7 @@ async function runPacksPublish(ctx, argv) {
|
|
|
15124
15263
|
if (!existsSync6(manifestPath)) {
|
|
15125
15264
|
throw new CliError(`No pack.json found in ${absPackDir}.`);
|
|
15126
15265
|
}
|
|
15127
|
-
const manifest = JSON.parse(
|
|
15266
|
+
const manifest = JSON.parse(readFileSync15(manifestPath, "utf8"));
|
|
15128
15267
|
const name = manifest.name;
|
|
15129
15268
|
const version = manifest.version;
|
|
15130
15269
|
if (!name || !version) throw new CliError('pack.json must declare both "name" and "version".');
|
|
@@ -15137,11 +15276,11 @@ async function runPacksPublish(ctx, argv) {
|
|
|
15137
15276
|
let privateKey;
|
|
15138
15277
|
let ephemeralPublicB64 = null;
|
|
15139
15278
|
if (options.key) {
|
|
15140
|
-
privateKey = createPrivateKey({ key:
|
|
15279
|
+
privateKey = createPrivateKey({ key: readFileSync15(resolvePath8(ctx.cwd, options.key), "utf8"), format: "pem" });
|
|
15141
15280
|
} else {
|
|
15142
15281
|
const conventional = join6(homedir3(), ".openwop-keys", `${keyId}.private.pem`);
|
|
15143
15282
|
if (existsSync6(conventional)) {
|
|
15144
|
-
privateKey = createPrivateKey({ key:
|
|
15283
|
+
privateKey = createPrivateKey({ key: readFileSync15(conventional, "utf8"), format: "pem" });
|
|
15145
15284
|
} else {
|
|
15146
15285
|
const kp = generateKeyPairSync("ed25519");
|
|
15147
15286
|
privateKey = kp.privateKey;
|
|
@@ -15220,7 +15359,7 @@ async function runPacksYank(ctx, argv) {
|
|
|
15220
15359
|
throw new CliError(`Version manifest not found: ${manifestPath}
|
|
15221
15360
|
(packs yank edits a local registry checkout; the published change lands via PR.)`);
|
|
15222
15361
|
}
|
|
15223
|
-
const manifest = JSON.parse(
|
|
15362
|
+
const manifest = JSON.parse(readFileSync15(manifestPath, "utf8"));
|
|
15224
15363
|
const newValue = !options.undo;
|
|
15225
15364
|
if (Boolean(manifest.yanked) === newValue) {
|
|
15226
15365
|
writeLine(ctx.io.stdout, `${name}@${version} is already ${newValue ? "yanked" : "un-yanked"}; no change.`);
|
|
@@ -15277,11 +15416,11 @@ function walkPackDir(packDir) {
|
|
|
15277
15416
|
const st = statSync(full);
|
|
15278
15417
|
if (st.isFile()) {
|
|
15279
15418
|
if (!ALLOWED_TOPS.has(entry)) continue;
|
|
15280
|
-
entries2.push({ name: entry, content:
|
|
15419
|
+
entries2.push({ name: entry, content: readFileSync15(full) });
|
|
15281
15420
|
} else if (st.isDirectory() && ALLOWED_DIRS.has(entry)) {
|
|
15282
15421
|
for (const f of readdirSync(full).sort()) {
|
|
15283
15422
|
if (!f.endsWith(".json") && !f.endsWith(".pem") && !f.endsWith(".sig")) continue;
|
|
15284
|
-
entries2.push({ name: `${entry}/${f}`, content:
|
|
15423
|
+
entries2.push({ name: `${entry}/${f}`, content: readFileSync15(join6(full, f)) });
|
|
15285
15424
|
}
|
|
15286
15425
|
}
|
|
15287
15426
|
}
|
|
@@ -15450,6 +15589,8 @@ async function protocolChecks(ctx) {
|
|
|
15450
15589
|
} else if (floor.required !== void 0) {
|
|
15451
15590
|
rows.push(warn("min client", `host minClientVersion ${floor.required} is not a <major>.<minor> version`));
|
|
15452
15591
|
}
|
|
15592
|
+
const stream = streamOrigin(ctx);
|
|
15593
|
+
rows.push(stream.source === "base" ? ok("stream origin", `event streams use --base-url (${stream.origin})`) : ok("stream origin", `event streams use ${stream.origin} \u2014 ${STREAM_SOURCE_LABEL[stream.source]}; set --stream-base-url to your --base-url to keep them there`));
|
|
15453
15594
|
return rows;
|
|
15454
15595
|
}
|
|
15455
15596
|
|
|
@@ -15841,7 +15982,7 @@ async function runGovernanceEgress(ctx, argv) {
|
|
|
15841
15982
|
|
|
15842
15983
|
// src/cli/demo.ts
|
|
15843
15984
|
import { spawn as spawn3, spawnSync as spawnSync5 } from "node:child_process";
|
|
15844
|
-
import { existsSync as existsSync8, readFileSync as
|
|
15985
|
+
import { existsSync as existsSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync12, createReadStream, openSync as openSync2, statSync as statSync2, mkdirSync as mkdirSync6, rmSync as rmSync4, chmodSync as chmodSync4, watch } from "node:fs";
|
|
15845
15986
|
import { join as join7, dirname as dirname7, relative as relative2 } from "node:path";
|
|
15846
15987
|
var DEMO_HELP = `Usage:
|
|
15847
15988
|
openwop demo status [--json]
|
|
@@ -16208,7 +16349,7 @@ async function runDemoLogs(ctx, argv) {
|
|
|
16208
16349
|
return 2;
|
|
16209
16350
|
}
|
|
16210
16351
|
const lineCount = Number(options.lines ?? 50);
|
|
16211
|
-
const existing =
|
|
16352
|
+
const existing = readFileSync16(logPath, "utf8");
|
|
16212
16353
|
const lines = existing.split("\n");
|
|
16213
16354
|
const tail = lines.slice(Math.max(0, lines.length - lineCount - 1));
|
|
16214
16355
|
write(ctx.io.stdout, tail.join("\n"));
|
|
@@ -17294,6 +17435,24 @@ async function runPromptsGet(ctx, argv) {
|
|
|
17294
17435
|
return 0;
|
|
17295
17436
|
}
|
|
17296
17437
|
|
|
17438
|
+
// src/redact.ts
|
|
17439
|
+
var BASE_SECRET_KEY = /secret|token|password|private[-_]?key|client[-_]?secret|api[-_]?key|credential/i;
|
|
17440
|
+
function redactSecrets(value, rules = {}) {
|
|
17441
|
+
const secret = rules.secret ?? BASE_SECRET_KEY;
|
|
17442
|
+
const walk = (v) => {
|
|
17443
|
+
if (Array.isArray(v)) return v.map(walk);
|
|
17444
|
+
if (v && typeof v === "object") {
|
|
17445
|
+
const out = {};
|
|
17446
|
+
for (const [k, inner] of Object.entries(v)) {
|
|
17447
|
+
out[k] = secret.test(k) && !(rules.keep?.test(k) ?? false) ? "[redacted]" : walk(inner);
|
|
17448
|
+
}
|
|
17449
|
+
return out;
|
|
17450
|
+
}
|
|
17451
|
+
return v;
|
|
17452
|
+
};
|
|
17453
|
+
return walk(value);
|
|
17454
|
+
}
|
|
17455
|
+
|
|
17297
17456
|
// src/cli/connections.ts
|
|
17298
17457
|
var CONN = "/v1/host/openwop-app/connections/:id";
|
|
17299
17458
|
var CONNECTIONS_ROUTES = [
|
|
@@ -17369,15 +17528,7 @@ Examples:
|
|
|
17369
17528
|
`;
|
|
17370
17529
|
var SECRET_KEY = /secret|token|password|private[-_]?key|client[-_]?secret|api[-_]?key|credential/i;
|
|
17371
17530
|
function redact(value) {
|
|
17372
|
-
|
|
17373
|
-
if (value && typeof value === "object") {
|
|
17374
|
-
const out = {};
|
|
17375
|
-
for (const [k, v] of Object.entries(value)) {
|
|
17376
|
-
out[k] = SECRET_KEY.test(k) ? "[redacted]" : redact(v);
|
|
17377
|
-
}
|
|
17378
|
-
return out;
|
|
17379
|
-
}
|
|
17380
|
-
return value;
|
|
17531
|
+
return redactSecrets(value, { secret: SECRET_KEY });
|
|
17381
17532
|
}
|
|
17382
17533
|
var TOP = ["list", "get", "test", "authorize", "oauth-clients", "oauth-client"];
|
|
17383
17534
|
async function runConnections(ctx, argv) {
|
|
@@ -17581,7 +17732,8 @@ Run \`openwop connections --help\` for usage.`);
|
|
|
17581
17732
|
} catch (err) {
|
|
17582
17733
|
gate4042(err);
|
|
17583
17734
|
}
|
|
17584
|
-
const
|
|
17735
|
+
const rawClients = Array.isArray(res.body?.clients) ? res.body.clients : [];
|
|
17736
|
+
const clients = rawClients.map(redact);
|
|
17585
17737
|
if (action === "get") {
|
|
17586
17738
|
if (positionals.length !== 1) {
|
|
17587
17739
|
write(ctx.io.stdout, "Usage: openwop connections oauth-clients get <provider> [--json]\n");
|
|
@@ -18873,7 +19025,7 @@ async function runWfList(ctx, argv) {
|
|
|
18873
19025
|
writeLine(ctx.io.stdout, formatTable(rows, ["workforceId", "name", "businessFunction", "status", "autonomy", "agents"]));
|
|
18874
19026
|
return 0;
|
|
18875
19027
|
}
|
|
18876
|
-
async function runWfReadOne(ctx, argv, suffix,
|
|
19028
|
+
async function runWfReadOne(ctx, argv, suffix, render2) {
|
|
18877
19029
|
const { options, positionals } = parseOptions(argv, { bool: ["--help"] });
|
|
18878
19030
|
if (options.help || positionals.length !== 1) {
|
|
18879
19031
|
write(ctx.io.stdout, WORKFORCES_HELP);
|
|
@@ -18890,7 +19042,7 @@ async function runWfReadOne(ctx, argv, suffix, render3) {
|
|
|
18890
19042
|
writeJson(ctx.io.stdout, res.body);
|
|
18891
19043
|
return 0;
|
|
18892
19044
|
}
|
|
18893
|
-
|
|
19045
|
+
render2(ctx, res.body ?? {}, positionals[0]);
|
|
18894
19046
|
return 0;
|
|
18895
19047
|
}
|
|
18896
19048
|
function renderWorkforce(ctx, w, id) {
|
|
@@ -19097,15 +19249,7 @@ Examples:
|
|
|
19097
19249
|
var BASE11 = "/v1/host/openwop-app/auth";
|
|
19098
19250
|
var SECRET_KEY2 = /secret|token|password|private[-_]?key|client[-_]?secret|api[-_]?key|credential|bearer|assertion|certificate|cert|pem/i;
|
|
19099
19251
|
function redact2(value) {
|
|
19100
|
-
|
|
19101
|
-
if (value && typeof value === "object") {
|
|
19102
|
-
const out = {};
|
|
19103
|
-
for (const [k, v] of Object.entries(value)) {
|
|
19104
|
-
out[k] = SECRET_KEY2.test(k) ? "[redacted]" : redact2(v);
|
|
19105
|
-
}
|
|
19106
|
-
return out;
|
|
19107
|
-
}
|
|
19108
|
-
return value;
|
|
19252
|
+
return redactSecrets(value, { secret: SECRET_KEY2 });
|
|
19109
19253
|
}
|
|
19110
19254
|
async function authRequest(ctx, path, options, notConfiguredMsg) {
|
|
19111
19255
|
try {
|
|
@@ -20164,7 +20308,7 @@ function truncate4(s, n) {
|
|
|
20164
20308
|
}
|
|
20165
20309
|
|
|
20166
20310
|
// src/cli/portability.ts
|
|
20167
|
-
import { readFileSync as
|
|
20311
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "node:fs";
|
|
20168
20312
|
import { resolve as resolve4 } from "node:path";
|
|
20169
20313
|
var EXPORT_HELP = `Usage:
|
|
20170
20314
|
openwop export [--kinds <kind>]... [--out <file>] [--json]
|
|
@@ -20217,15 +20361,7 @@ Examples:
|
|
|
20217
20361
|
var SECRET_KEY3 = /secret|token|password|private[-_]?key|client[-_]?secret|api[-_]?key|credential/i;
|
|
20218
20362
|
var REF_ONLY_KEY = /^secretsToRebind$/;
|
|
20219
20363
|
function redact3(value) {
|
|
20220
|
-
|
|
20221
|
-
if (value && typeof value === "object") {
|
|
20222
|
-
const out = {};
|
|
20223
|
-
for (const [k, v] of Object.entries(value)) {
|
|
20224
|
-
out[k] = SECRET_KEY3.test(k) && !REF_ONLY_KEY.test(k) ? "[redacted]" : redact3(v);
|
|
20225
|
-
}
|
|
20226
|
-
return out;
|
|
20227
|
-
}
|
|
20228
|
-
return value;
|
|
20364
|
+
return redactSecrets(value, { secret: SECRET_KEY3, keep: REF_ONLY_KEY });
|
|
20229
20365
|
}
|
|
20230
20366
|
async function ensureAdvertised5(ctx) {
|
|
20231
20367
|
const wk = await safeRequest(ctx, "/.well-known/openwop", { auth: false });
|
|
@@ -20302,7 +20438,7 @@ async function runImport(ctx, argv) {
|
|
|
20302
20438
|
}
|
|
20303
20439
|
let bundle;
|
|
20304
20440
|
try {
|
|
20305
|
-
const raw =
|
|
20441
|
+
const raw = readFileSync17(resolve4(ctx.cwd, positionals[0]), "utf8");
|
|
20306
20442
|
bundle = JSON.parse(raw);
|
|
20307
20443
|
} catch (err) {
|
|
20308
20444
|
throw new CliError(`import: could not read bundle file ${positionals[0]} \u2014 ${err instanceof Error ? err.message : "invalid JSON"}`, 2);
|
|
@@ -21074,7 +21210,7 @@ Run \`openwop knowledge-sync --help\` for usage.`);
|
|
|
21074
21210
|
}
|
|
21075
21211
|
|
|
21076
21212
|
// src/cli/entities.ts
|
|
21077
|
-
import { readFileSync as
|
|
21213
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "node:fs";
|
|
21078
21214
|
import { resolve as resolvePath9 } from "node:path";
|
|
21079
21215
|
var BASE13 = "/v1/host/openwop-app/entities";
|
|
21080
21216
|
var PUBLIC_BASE2 = "/v1/host/openwop-app/public-entities";
|
|
@@ -21310,7 +21446,7 @@ async function types(ctx, sub, pos, o, project4, q) {
|
|
|
21310
21446
|
if (!name || !o.file) return usage5(ctx, "types import <type> --file <file.ndjson|file.json>");
|
|
21311
21447
|
let raw;
|
|
21312
21448
|
try {
|
|
21313
|
-
raw =
|
|
21449
|
+
raw = readFileSync18(resolvePath9(ctx.cwd, String(o.file)), "utf8");
|
|
21314
21450
|
} catch (err) {
|
|
21315
21451
|
throw new CliError(`Cannot read --file ${String(o.file)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
21316
21452
|
}
|
|
@@ -21881,7 +22017,7 @@ async function startJob(ctx, path, body) {
|
|
|
21881
22017
|
const res = await requestJson(ctx, path, { method: "POST", body });
|
|
21882
22018
|
return renderDone(ctx, res.body, `Video job ${res.body?.jobId ?? ""} ${res.body?.status ?? "started"}.`);
|
|
21883
22019
|
} catch (err) {
|
|
21884
|
-
const b = err instanceof HttpError ? err.body : void 0;
|
|
22020
|
+
const b = err instanceof HttpError && err.body && typeof err.body === "object" ? err.body : void 0;
|
|
21885
22021
|
if (err instanceof HttpError && b && b.status === "failed") {
|
|
21886
22022
|
if (ctx.json) writeJson(ctx.io.stdout, b);
|
|
21887
22023
|
else writeLine(ctx.io.stderr, `openwop: video generation failed (HTTP ${err.status}): ${b.error ?? "unknown error"}${b.error === "no_connection" ? " \u2014 connect the video provider under Connections first" : ""}`);
|
|
@@ -22744,8 +22880,7 @@ async function followEvents(ctx, requestedPath, options) {
|
|
|
22744
22880
|
const max = options.max !== void 0 ? Number(options.max) : Infinity;
|
|
22745
22881
|
const timeoutMs = options.timeout !== void 0 ? Number(options.timeout) * 1e3 : Infinity;
|
|
22746
22882
|
if (Number.isNaN(max) || Number.isNaN(timeoutMs)) throw new CliError("--max and --timeout must be numbers.");
|
|
22747
|
-
const {
|
|
22748
|
-
const url = new URL(path.replace(/^\//, ""), ctx.baseUrl.endsWith("/") ? ctx.baseUrl : `${ctx.baseUrl}/`);
|
|
22883
|
+
const { url, headers } = await resolveStreamRequest(ctx, requestedPath, {});
|
|
22749
22884
|
const controller = new AbortController();
|
|
22750
22885
|
const timer = Number.isFinite(timeoutMs) ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
|
|
22751
22886
|
let seen = 0;
|
|
@@ -26552,7 +26687,7 @@ function renderKnowledge(ctx, agentId, k) {
|
|
|
26552
26687
|
}
|
|
26553
26688
|
|
|
26554
26689
|
// src/cli/compatEndpoints.ts
|
|
26555
|
-
import { readFileSync as
|
|
26690
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
26556
26691
|
var BASE32 = "/v1/host/openwop-app/compat-endpoints";
|
|
26557
26692
|
var COMPAT_ENDPOINTS_HELP = `Usage:
|
|
26558
26693
|
openwop compat-endpoints list --org <orgId> [--json]
|
|
@@ -26666,7 +26801,7 @@ function readApiKey(ctx, options) {
|
|
|
26666
26801
|
if (options.apiKeyFile && options.apiKeyEnv) throw new CliError("Pass only one of --api-key-file / --api-key-env.", 2);
|
|
26667
26802
|
if (options.apiKeyFile) {
|
|
26668
26803
|
try {
|
|
26669
|
-
return
|
|
26804
|
+
return readFileSync19(String(options.apiKeyFile), "utf8").trim() || void 0;
|
|
26670
26805
|
} catch (err) {
|
|
26671
26806
|
throw new CliError(`Could not read ${options.apiKeyFile}: ${err instanceof Error ? err.message : String(err)}`, 2);
|
|
26672
26807
|
}
|
|
@@ -27868,7 +28003,7 @@ Run \`openwop scheduled-chats --help\` for usage.`, 2);
|
|
|
27868
28003
|
}
|
|
27869
28004
|
|
|
27870
28005
|
// src/cli/voice.ts
|
|
27871
|
-
import { readFileSync as
|
|
28006
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync19 } from "node:fs";
|
|
27872
28007
|
var SESSION = "/v1/host/openwop-app/voice/session";
|
|
27873
28008
|
var REALTIME = "/v1/host/openwop-app/voice/realtime";
|
|
27874
28009
|
var AUDIO_CHUNK_BYTES = 192 * 1024;
|
|
@@ -27973,7 +28108,7 @@ Run \`openwop voice --help\` for usage.`, 2);
|
|
|
27973
28108
|
if (options.file) {
|
|
27974
28109
|
let bytes;
|
|
27975
28110
|
try {
|
|
27976
|
-
bytes =
|
|
28111
|
+
bytes = readFileSync20(String(options.file));
|
|
27977
28112
|
} catch (err) {
|
|
27978
28113
|
throw new CliError(`--file: cannot read ${options.file} (${err instanceof Error ? err.message : String(err)})`, 2);
|
|
27979
28114
|
}
|
|
@@ -28115,7 +28250,7 @@ async function runRealtime(ctx, argv) {
|
|
|
28115
28250
|
if (!options.sdpFile) throw new CliError("voice realtime connect requires --sdp-file <offer.sdp>.", 2);
|
|
28116
28251
|
let sdp;
|
|
28117
28252
|
try {
|
|
28118
|
-
sdp =
|
|
28253
|
+
sdp = readFileSync20(String(options.sdpFile), "utf8");
|
|
28119
28254
|
} catch (err) {
|
|
28120
28255
|
throw new CliError(`--sdp-file: cannot read ${options.sdpFile} (${err instanceof Error ? err.message : String(err)})`, 2);
|
|
28121
28256
|
}
|
|
@@ -28170,10 +28305,10 @@ async function runRealtimeConfig(ctx, argv) {
|
|
|
28170
28305
|
write(ctx.io.stdout, VOICE_HELP);
|
|
28171
28306
|
return 0;
|
|
28172
28307
|
}
|
|
28173
|
-
const
|
|
28308
|
+
const render2 = (b) => writeFields(ctx, [["provider", b?.provider], ["credentialRef", b?.credentialRef], ["model", b?.model]]);
|
|
28174
28309
|
if (sub === "get") {
|
|
28175
28310
|
const res = await requestJson(ctx, `${REALTIME}/config`);
|
|
28176
|
-
return emit(ctx, res.body, () =>
|
|
28311
|
+
return emit(ctx, res.body, () => render2(res.body));
|
|
28177
28312
|
}
|
|
28178
28313
|
if (sub === "set") {
|
|
28179
28314
|
if (!options.provider && options.credentialRef === void 0 && options.model === void 0) {
|
|
@@ -28188,7 +28323,7 @@ async function runRealtimeConfig(ctx, argv) {
|
|
|
28188
28323
|
const res = await requestJson(ctx, `${REALTIME}/config`, { method: "PUT", body });
|
|
28189
28324
|
return emit(ctx, res.body, () => {
|
|
28190
28325
|
writeLine(ctx.io.stdout, "Updated realtime voice config.");
|
|
28191
|
-
|
|
28326
|
+
render2(res.body);
|
|
28192
28327
|
});
|
|
28193
28328
|
}
|
|
28194
28329
|
throw new CliError(`Unknown voice realtime config command: ${sub}
|
|
@@ -28196,7 +28331,7 @@ Run \`openwop voice --help\` for usage.`, 2);
|
|
|
28196
28331
|
}
|
|
28197
28332
|
|
|
28198
28333
|
// src/cli/ai.ts
|
|
28199
|
-
import { readFileSync as
|
|
28334
|
+
import { readFileSync as readFileSync21 } from "node:fs";
|
|
28200
28335
|
var AI = "/v1/host/openwop-app/ai";
|
|
28201
28336
|
var AI_HELP = `Usage:
|
|
28202
28337
|
openwop ai call (--message <text> | --messages-json <json>) [--provider p] [--model m] [--json]
|
|
@@ -28294,7 +28429,7 @@ async function runAi(ctx, argv) {
|
|
|
28294
28429
|
let value;
|
|
28295
28430
|
if (options.valueFile) {
|
|
28296
28431
|
try {
|
|
28297
|
-
value =
|
|
28432
|
+
value = readFileSync21(String(options.valueFile), "utf8").trim();
|
|
28298
28433
|
} catch (err) {
|
|
28299
28434
|
throw new CliError(`--value-file: cannot read ${options.valueFile} (${err instanceof Error ? err.message : String(err)})`, 2);
|
|
28300
28435
|
}
|
|
@@ -29501,6 +29636,7 @@ async function runCli(argv, options = {}) {
|
|
|
29501
29636
|
parsed.globals.baseUrl ?? env.OPENWOP_BASE_URL ?? savedConfig?.host?.baseUrl ?? DEFAULT_BASE_URL
|
|
29502
29637
|
),
|
|
29503
29638
|
apiKey: parsed.globals.apiKey ?? env.OPENWOP_API_KEY ?? savedConfig?.host?.apiKey,
|
|
29639
|
+
streamBaseUrl: explicitStreamBase(parsed.globals.streamBaseUrl ?? env.OPENWOP_STREAM_BASE_URL ?? savedConfig?.host?.streamBaseUrl),
|
|
29504
29640
|
json: parsed.globals.json,
|
|
29505
29641
|
quiet: parsed.globals.quiet,
|
|
29506
29642
|
verbose: parsed.globals.verbose,
|
|
@@ -30061,6 +30197,9 @@ Usage:
|
|
|
30061
30197
|
|
|
30062
30198
|
Global options:
|
|
30063
30199
|
--base-url <url> Host base URL (default: --base-url > OPENWOP_BASE_URL > saved config > http://localhost:8080)
|
|
30200
|
+
--stream-base-url <url>
|
|
30201
|
+
Where to read event streams (default: OPENWOP_STREAM_BASE_URL >
|
|
30202
|
+
saved config > the host's advertised https streamBase > --base-url)
|
|
30064
30203
|
--api-key <key> Bearer API key (default: OPENWOP_API_KEY > saved config > sample-token for localhost)
|
|
30065
30204
|
--profile <name> Use a named config profile (~/.openwop-<name>); also OPENWOP_PROFILE
|
|
30066
30205
|
--json Print machine-readable JSON where supported
|