@openwop/cli 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/cli.js +451 -333
- package/dist/openwop.js +446 -333
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -565,6 +565,12 @@ function httpErrorHint(err, env) {
|
|
|
565
565
|
}
|
|
566
566
|
return void 0;
|
|
567
567
|
}
|
|
568
|
+
function isNoCredentialChallenge(err) {
|
|
569
|
+
if (!(err instanceof HttpError) || err.status !== 401) return false;
|
|
570
|
+
const challenge = err.headers?.get?.("www-authenticate");
|
|
571
|
+
if (!challenge || !/^\s*Bearer\b/i.test(challenge)) return false;
|
|
572
|
+
return !/\berror\s*=/i.test(challenge);
|
|
573
|
+
}
|
|
568
574
|
function errText(err) {
|
|
569
575
|
return err instanceof Error ? err.message : String(err);
|
|
570
576
|
}
|
|
@@ -572,7 +578,7 @@ function errText(err) {
|
|
|
572
578
|
// package.json
|
|
573
579
|
var package_default = {
|
|
574
580
|
name: "@openwop/cli",
|
|
575
|
-
version: "1.
|
|
581
|
+
version: "1.2.0",
|
|
576
582
|
description: "OpenWOP CLI for operating the workflow-engine demo app and OpenWOP-compatible hosts.",
|
|
577
583
|
license: "Apache-2.0",
|
|
578
584
|
type: "module",
|
|
@@ -703,6 +709,7 @@ function prefixChunk(stream, label, chunk) {
|
|
|
703
709
|
function extractGlobalOptions(argv, env = process.env) {
|
|
704
710
|
const globals = {
|
|
705
711
|
baseUrl: void 0,
|
|
712
|
+
streamBaseUrl: void 0,
|
|
706
713
|
apiKey: void 0,
|
|
707
714
|
profile: void 0,
|
|
708
715
|
json: false,
|
|
@@ -720,6 +727,10 @@ function extractGlobalOptions(argv, env = process.env) {
|
|
|
720
727
|
globals.baseUrl = value ?? takeValue(argv, ++i, "--base-url");
|
|
721
728
|
continue;
|
|
722
729
|
}
|
|
730
|
+
if (flag === "--stream-base-url") {
|
|
731
|
+
globals.streamBaseUrl = value ?? takeValue(argv, ++i, "--stream-base-url");
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
723
734
|
if (flag === "--api-key") {
|
|
724
735
|
globals.apiKey = value ?? takeValue(argv, ++i, "--api-key");
|
|
725
736
|
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,63 @@ 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
|
+
try {
|
|
1240
|
+
new URL(raw.trim());
|
|
1241
|
+
} catch {
|
|
1242
|
+
throw new CliError(`--stream-base-url / OPENWOP_STREAM_BASE_URL is not a URL: ${raw}`);
|
|
1243
|
+
}
|
|
1244
|
+
return raw.trim().replace(/\/+$/, "");
|
|
1245
|
+
}
|
|
1246
|
+
function streamBaseFrom(extensions, baseUrl) {
|
|
1247
|
+
if (!extensions || typeof extensions !== "object" || Array.isArray(extensions)) return void 0;
|
|
1248
|
+
let base16;
|
|
1249
|
+
try {
|
|
1250
|
+
base16 = new URL(baseUrl);
|
|
1251
|
+
} catch {
|
|
1252
|
+
return void 0;
|
|
1253
|
+
}
|
|
1254
|
+
for (const value of Object.values(extensions)) {
|
|
1255
|
+
const raw = value && typeof value === "object" ? value.streamBase : void 0;
|
|
1256
|
+
if (typeof raw !== "string") continue;
|
|
1257
|
+
let url;
|
|
1258
|
+
try {
|
|
1259
|
+
url = new URL(raw);
|
|
1260
|
+
} catch {
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") continue;
|
|
1264
|
+
const secure = url.protocol === "https:" && (base16.protocol === "https:" || isLoopback(base16));
|
|
1265
|
+
const localDev = url.protocol === "http:" && isLoopback(url) && isLoopback(base16);
|
|
1266
|
+
if (!secure && !localDev) continue;
|
|
1267
|
+
return raw.replace(/\/+$/, "");
|
|
1268
|
+
}
|
|
1269
|
+
return void 0;
|
|
1270
|
+
}
|
|
1271
|
+
async function resolveStreamRequest(ctx, requestedPath, headers) {
|
|
1272
|
+
const resolved = await resolveRequest(ctx, requestedPath, { accept: "text/event-stream", ...headers });
|
|
1273
|
+
const { origin, source } = streamOrigin(ctx);
|
|
1274
|
+
if (ctx.verbose && source !== "base") ctx.io.stderr.write(`openwop: reading the event stream from ${origin} (${STREAM_SOURCE_LABEL[source]})
|
|
1275
|
+
`);
|
|
1276
|
+
const url = new URL(resolved.path.replace(/^\//, ""), origin.endsWith("/") ? origin : `${origin}/`);
|
|
1277
|
+
const out = { ...resolved.headers };
|
|
1278
|
+
if (ctx.apiKey) out.authorization = `Bearer ${ctx.apiKey}`;
|
|
1279
|
+
return { url, headers: out };
|
|
1280
|
+
}
|
|
1281
|
+
var STREAM_SOURCE_LABEL = {
|
|
1282
|
+
setting: "your --stream-base-url / OPENWOP_STREAM_BASE_URL / config setting",
|
|
1283
|
+
advertised: "the host's advertised streamBase",
|
|
1284
|
+
base: "--base-url"
|
|
1285
|
+
};
|
|
1286
|
+
function streamOrigin(ctx) {
|
|
1287
|
+
if (ctx.streamBaseUrl) return { origin: ctx.streamBaseUrl, source: "setting" };
|
|
1288
|
+
if (ctx.discoveredStreamBase) return { origin: ctx.discoveredStreamBase, source: "advertised" };
|
|
1289
|
+
return { origin: ctx.baseUrl, source: "base" };
|
|
1290
|
+
}
|
|
1222
1291
|
|
|
1223
1292
|
// src/wire.ts
|
|
1224
1293
|
import { randomUUID } from "node:crypto";
|
|
@@ -1457,12 +1526,12 @@ async function streamViaSse(ctx, runId, deliver, opts) {
|
|
|
1457
1526
|
for (; ; ) {
|
|
1458
1527
|
let progressed = false;
|
|
1459
1528
|
let dropError;
|
|
1529
|
+
let idle;
|
|
1460
1530
|
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}`;
|
|
1531
|
+
const { url, headers } = await resolveStreamRequest(ctx, `/v1/runs/${encodeURIComponent(runId)}/events${query}`, {});
|
|
1464
1532
|
if (lastEventId !== void 0 && lastEventId !== "") headers["last-event-id"] = lastEventId;
|
|
1465
|
-
|
|
1533
|
+
idle = idleWatchdog(opts.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS);
|
|
1534
|
+
const res = await ctx.fetchImpl(url, { method: "GET", headers, signal: idle.signal });
|
|
1466
1535
|
if (!res.ok) {
|
|
1467
1536
|
const text = await res.text().catch(() => "");
|
|
1468
1537
|
let body = null;
|
|
@@ -1479,7 +1548,7 @@ async function streamViaSse(ctx, runId, deliver, opts) {
|
|
|
1479
1548
|
throw new HttpError("resumed events stream is not text/event-stream", res.status, null, res.headers);
|
|
1480
1549
|
}
|
|
1481
1550
|
opened = true;
|
|
1482
|
-
await consumeSse(res.body, (frame) => {
|
|
1551
|
+
await consumeSse(idle.watch(res.body), (frame) => {
|
|
1483
1552
|
if (frame.retry !== void 0) retryMs = frame.retry;
|
|
1484
1553
|
if (frame.id !== void 0) lastEventId = frame.id;
|
|
1485
1554
|
if (frame.data === void 0) return;
|
|
@@ -1498,6 +1567,8 @@ async function streamViaSse(ctx, runId, deliver, opts) {
|
|
|
1498
1567
|
if (!opened) throw err;
|
|
1499
1568
|
if (err instanceof HttpError && err.status !== 429 && err.status < 500) throw new SseResumeExhausted(err.message, err);
|
|
1500
1569
|
dropError = err;
|
|
1570
|
+
} finally {
|
|
1571
|
+
idle?.stop();
|
|
1501
1572
|
}
|
|
1502
1573
|
if (terminal) return true;
|
|
1503
1574
|
if (lastEventId === void 0 && deliver.highest() >= 0) lastEventId = String(deliver.highest());
|
|
@@ -1707,6 +1778,59 @@ function defaultReadTurn(ctx) {
|
|
|
1707
1778
|
rl.once("close", onClose);
|
|
1708
1779
|
});
|
|
1709
1780
|
}
|
|
1781
|
+
var DEFAULT_IDLE_TIMEOUT_MS = 45e3;
|
|
1782
|
+
function idleWatchdog(ms2) {
|
|
1783
|
+
const controller = new AbortController();
|
|
1784
|
+
let timer;
|
|
1785
|
+
let onExpire;
|
|
1786
|
+
const expire = () => {
|
|
1787
|
+
controller.abort(new Error(`no bytes for ${ms2}ms`));
|
|
1788
|
+
onExpire?.();
|
|
1789
|
+
};
|
|
1790
|
+
const arm = () => {
|
|
1791
|
+
if (!(ms2 > 0)) return;
|
|
1792
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1793
|
+
timer = setTimeout(expire, ms2);
|
|
1794
|
+
};
|
|
1795
|
+
arm();
|
|
1796
|
+
return {
|
|
1797
|
+
signal: controller.signal,
|
|
1798
|
+
watch(body) {
|
|
1799
|
+
if (!(ms2 > 0) || typeof body?.getReader !== "function") return body;
|
|
1800
|
+
const source = body.getReader();
|
|
1801
|
+
return new ReadableStream({
|
|
1802
|
+
start(out) {
|
|
1803
|
+
onExpire = () => {
|
|
1804
|
+
out.error(new Error(`events stream idle for ${ms2}ms`));
|
|
1805
|
+
source.cancel().catch(() => {
|
|
1806
|
+
});
|
|
1807
|
+
};
|
|
1808
|
+
},
|
|
1809
|
+
async pull(out) {
|
|
1810
|
+
try {
|
|
1811
|
+
const { value, done: done2 } = await source.read();
|
|
1812
|
+
if (done2) {
|
|
1813
|
+
out.close();
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
arm();
|
|
1817
|
+
out.enqueue(value);
|
|
1818
|
+
} catch (err) {
|
|
1819
|
+
out.error(err);
|
|
1820
|
+
}
|
|
1821
|
+
},
|
|
1822
|
+
cancel(reason) {
|
|
1823
|
+
return source.cancel(reason);
|
|
1824
|
+
}
|
|
1825
|
+
});
|
|
1826
|
+
},
|
|
1827
|
+
stop() {
|
|
1828
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1829
|
+
timer = void 0;
|
|
1830
|
+
onExpire = void 0;
|
|
1831
|
+
}
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1710
1834
|
|
|
1711
1835
|
// src/cli/chatShared.ts
|
|
1712
1836
|
var enc = (value) => encodeURIComponent(value);
|
|
@@ -1731,9 +1855,7 @@ function writeFields(ctx, fields) {
|
|
|
1731
1855
|
}
|
|
1732
1856
|
}
|
|
1733
1857
|
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}`;
|
|
1858
|
+
const { url, headers } = await resolveStreamRequest(ctx, requestedPath, {});
|
|
1737
1859
|
const controller = new AbortController();
|
|
1738
1860
|
let timer;
|
|
1739
1861
|
if (opts.timeoutMs && opts.timeoutMs > 0) timer = setTimeout(() => controller.abort(), opts.timeoutMs);
|
|
@@ -1790,6 +1912,82 @@ function renderFrame(frame) {
|
|
|
1790
1912
|
|
|
1791
1913
|
// src/cli/routeKit.ts
|
|
1792
1914
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
1915
|
+
|
|
1916
|
+
// src/cli/shared.ts
|
|
1917
|
+
function requireOrg(org2) {
|
|
1918
|
+
if (!org2) throw new CliError("This command is org-scoped \u2014 pass --org <orgId>.", 2);
|
|
1919
|
+
return String(org2);
|
|
1920
|
+
}
|
|
1921
|
+
function buildInputs(options) {
|
|
1922
|
+
const fromJson = options.inputsJson ? JSON.parse(options.inputsJson) : {};
|
|
1923
|
+
if (fromJson === null || typeof fromJson !== "object" || Array.isArray(fromJson)) {
|
|
1924
|
+
throw new CliError("--inputs-json must be a JSON object");
|
|
1925
|
+
}
|
|
1926
|
+
const inputs = { ...fromJson };
|
|
1927
|
+
for (const pair of options.input ?? []) {
|
|
1928
|
+
const eq = pair.indexOf("=");
|
|
1929
|
+
if (eq <= 0) throw new CliError(`--input must be key=value, got: ${pair}`);
|
|
1930
|
+
inputs[pair.slice(0, eq)] = parseInputValue(pair.slice(eq + 1));
|
|
1931
|
+
}
|
|
1932
|
+
return inputs;
|
|
1933
|
+
}
|
|
1934
|
+
function parseInputValue(value) {
|
|
1935
|
+
try {
|
|
1936
|
+
return JSON.parse(value);
|
|
1937
|
+
} catch {
|
|
1938
|
+
return value;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
function parseNodeVersion(version) {
|
|
1942
|
+
const [major, minor, patch] = version.split(".").map((v) => Number(v));
|
|
1943
|
+
return { major: major || 0, minor: minor || 0, patch: patch || 0 };
|
|
1944
|
+
}
|
|
1945
|
+
function defaultApiKeyFor(baseUrl) {
|
|
1946
|
+
try {
|
|
1947
|
+
const host = new URL(baseUrl).hostname;
|
|
1948
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "::1") return DEFAULT_API_KEY;
|
|
1949
|
+
} catch {
|
|
1950
|
+
return void 0;
|
|
1951
|
+
}
|
|
1952
|
+
return void 0;
|
|
1953
|
+
}
|
|
1954
|
+
function normalizeBaseUrl(value) {
|
|
1955
|
+
if (!value) return DEFAULT_BASE_URL;
|
|
1956
|
+
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
1957
|
+
}
|
|
1958
|
+
function npmCommand() {
|
|
1959
|
+
return process.platform === "win32" ? "npm.cmd" : "npm";
|
|
1960
|
+
}
|
|
1961
|
+
function ok(name, message) {
|
|
1962
|
+
return { status: "ok", name, message };
|
|
1963
|
+
}
|
|
1964
|
+
function warn(name, message) {
|
|
1965
|
+
return { status: "warn", name, message };
|
|
1966
|
+
}
|
|
1967
|
+
function fail(name, message) {
|
|
1968
|
+
return { status: "fail", name, message };
|
|
1969
|
+
}
|
|
1970
|
+
function formatCheckTable(checks) {
|
|
1971
|
+
return formatTable(
|
|
1972
|
+
checks.map((c) => ({ status: c.status.toUpperCase(), check: c.name, message: c.message })),
|
|
1973
|
+
["status", "check", "message"]
|
|
1974
|
+
);
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
// src/cli/routeKit.ts
|
|
1978
|
+
var errText2 = (err) => err instanceof Error ? err.message : String(err);
|
|
1979
|
+
var DEFAULT_MESSAGES = {
|
|
1980
|
+
required: (flag, usage6) => `${usage6}
|
|
1981
|
+
${flag} is required.`,
|
|
1982
|
+
refusal: (words, positionals) => `Refusing to ${words.join(" ")} ${positionals.join(" ")} without --yes.`.replace(/\s+/g, " ").trim(),
|
|
1983
|
+
invalidNumber: (flag, raw) => `${flag} must be a number (got "${raw}").`,
|
|
1984
|
+
invalidBool: (flag, raw) => `${flag} must be true or false (got "${raw}").`,
|
|
1985
|
+
invalidJson: (flag) => `${flag} must be valid JSON.`,
|
|
1986
|
+
unreadable: (flag, path, err) => `Could not read ${path} for ${flag}: ${errText2(err)}`,
|
|
1987
|
+
unreadableBody: (path, err) => `Could not read ${path}: ${errText2(err)}`,
|
|
1988
|
+
invalidBodyJson: "--body / --body-file must be a JSON object.",
|
|
1989
|
+
bodyNotObject: "--body / --body-file must be a JSON object."
|
|
1990
|
+
};
|
|
1793
1991
|
var COMMON_BOOL = ["--help", "--yes"];
|
|
1794
1992
|
function templates(cmd) {
|
|
1795
1993
|
return Array.isArray(cmd.path) ? cmd.path : [cmd.path];
|
|
@@ -1797,18 +1995,31 @@ function templates(cmd) {
|
|
|
1797
1995
|
function paramNames(path) {
|
|
1798
1996
|
return [...path.matchAll(/:([A-Za-z][A-Za-z0-9_]*)/g)].map((m) => m[1]);
|
|
1799
1997
|
}
|
|
1998
|
+
function positionalNames(cmd, path) {
|
|
1999
|
+
return paramNames(path).filter((p) => !(cmd.orgFlag && p === "org"));
|
|
2000
|
+
}
|
|
2001
|
+
function bindsOrg(cmd) {
|
|
2002
|
+
return Boolean(cmd.orgFlag) && templates(cmd).some((p) => paramNames(p).includes("org"));
|
|
2003
|
+
}
|
|
2004
|
+
function fillPath(cmd, template, positionals, org2) {
|
|
2005
|
+
let i = 0;
|
|
2006
|
+
return template.replace(/:([A-Za-z][A-Za-z0-9_]*)/g, (_m, name) => {
|
|
2007
|
+
if (cmd.orgFlag && name === "org") return encodeURIComponent(requireOrg(org2));
|
|
2008
|
+
return encodeURIComponent(positionals[i++] ?? "");
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
1800
2011
|
function flagHint(f) {
|
|
1801
2012
|
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()}>`;
|
|
2013
|
+
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
2014
|
return f.required ? `${f.flag}${v}` : `[${f.flag}${v}]`;
|
|
1804
2015
|
}
|
|
1805
2016
|
function usageLine(group, cmd) {
|
|
1806
2017
|
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);
|
|
2018
|
+
const longest = paths.reduce((a, b) => positionalNames(cmd, b).length > positionalNames(cmd, a).length ? b : a);
|
|
2019
|
+
const shortest = paths.reduce((a, b) => positionalNames(cmd, b).length < positionalNames(cmd, a).length ? b : a);
|
|
2020
|
+
const req2 = positionalNames(cmd, shortest);
|
|
2021
|
+
const params = positionalNames(cmd, longest).map((p, i) => i < req2.length ? `<${p}>` : `[<${p}>]`);
|
|
2022
|
+
const flags = [...bindsOrg(cmd) ? ["--org <orgId>"] : [], ...[...cmd.query ?? [], ...cmd.body ?? []].map(flagHint)];
|
|
1812
2023
|
if (cmd.body?.some((f) => f.type === "json")) flags.push("[--body <json>|--body-file <path>]");
|
|
1813
2024
|
if (cmd.confirm ?? cmd.method === "DELETE") flags.push("--yes");
|
|
1814
2025
|
if (cmd.method === "GET") flags.push("[--json]");
|
|
@@ -1844,9 +2055,15 @@ function setPath(target, key, value) {
|
|
|
1844
2055
|
}
|
|
1845
2056
|
node[parts[parts.length - 1]] = value;
|
|
1846
2057
|
}
|
|
1847
|
-
function coerce(f, raw) {
|
|
2058
|
+
function coerce(f, raw, cmd, msg) {
|
|
1848
2059
|
const t = f.type ?? "string";
|
|
1849
2060
|
if (t === "boolean") return raw;
|
|
2061
|
+
if (t === "bool") {
|
|
2062
|
+
const s2 = String(raw);
|
|
2063
|
+
if (s2 === "true" || s2 === "yes" || s2 === "1") return true;
|
|
2064
|
+
if (s2 === "false" || s2 === "no" || s2 === "0") return false;
|
|
2065
|
+
throw new CliError(msg.invalidBool(f.flag, s2), 2);
|
|
2066
|
+
}
|
|
1850
2067
|
if (t === "list") return Array.isArray(raw) ? raw.map(String) : [String(raw)];
|
|
1851
2068
|
if (t === "map") {
|
|
1852
2069
|
const out = {};
|
|
@@ -1862,7 +2079,7 @@ function coerce(f, raw) {
|
|
|
1862
2079
|
const s = String(raw);
|
|
1863
2080
|
if (t === "number") {
|
|
1864
2081
|
const n = Number(s);
|
|
1865
|
-
if (!Number.isFinite(n)) throw new CliError(
|
|
2082
|
+
if (!Number.isFinite(n) || cmd.strictNumbers && s.trim() === "") throw new CliError(msg.invalidNumber(f.flag, s), 2);
|
|
1866
2083
|
return n;
|
|
1867
2084
|
}
|
|
1868
2085
|
if (t === "json-file") {
|
|
@@ -1870,7 +2087,7 @@ function coerce(f, raw) {
|
|
|
1870
2087
|
try {
|
|
1871
2088
|
text = readFileSync4(s, "utf8");
|
|
1872
2089
|
} catch (err) {
|
|
1873
|
-
throw new CliError(
|
|
2090
|
+
throw new CliError(msg.unreadable(f.flag, s, err), 2);
|
|
1874
2091
|
}
|
|
1875
2092
|
try {
|
|
1876
2093
|
return JSON.parse(text);
|
|
@@ -1882,14 +2099,14 @@ function coerce(f, raw) {
|
|
|
1882
2099
|
try {
|
|
1883
2100
|
return readFileSync4(s).toString("base64");
|
|
1884
2101
|
} catch (err) {
|
|
1885
|
-
throw new CliError(
|
|
2102
|
+
throw new CliError(msg.unreadable(f.flag, s, err), 2);
|
|
1886
2103
|
}
|
|
1887
2104
|
}
|
|
1888
2105
|
if (t === "file") {
|
|
1889
2106
|
try {
|
|
1890
2107
|
return readFileSync4(s, "utf8").replace(/\r?\n$/, "");
|
|
1891
2108
|
} catch (err) {
|
|
1892
|
-
throw new CliError(
|
|
2109
|
+
throw new CliError(msg.unreadable(f.flag, s, err), 2);
|
|
1893
2110
|
}
|
|
1894
2111
|
}
|
|
1895
2112
|
if (t === "csv") return s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
@@ -1897,18 +2114,19 @@ function coerce(f, raw) {
|
|
|
1897
2114
|
try {
|
|
1898
2115
|
return JSON.parse(s);
|
|
1899
2116
|
} catch {
|
|
1900
|
-
throw new CliError(
|
|
2117
|
+
throw new CliError(msg.invalidJson(f.flag, s), 2);
|
|
1901
2118
|
}
|
|
1902
2119
|
}
|
|
1903
2120
|
return s;
|
|
1904
2121
|
}
|
|
1905
|
-
function readBodyOptions(options) {
|
|
2122
|
+
function readBodyOptions(options, msg) {
|
|
1906
2123
|
let raw;
|
|
1907
2124
|
if (options.bodyFile !== void 0) {
|
|
2125
|
+
if (String(options.bodyFile).trim() === "") throw new CliError("--body-file needs a path (got an empty value).", 2);
|
|
1908
2126
|
try {
|
|
1909
2127
|
raw = readFileSync4(String(options.bodyFile), "utf8");
|
|
1910
2128
|
} catch (err) {
|
|
1911
|
-
throw new CliError(
|
|
2129
|
+
throw new CliError(msg.unreadableBody(String(options.bodyFile), err), 2);
|
|
1912
2130
|
}
|
|
1913
2131
|
} else if (options.body !== void 0) {
|
|
1914
2132
|
raw = String(options.body);
|
|
@@ -1918,9 +2136,9 @@ function readBodyOptions(options) {
|
|
|
1918
2136
|
try {
|
|
1919
2137
|
parsed = JSON.parse(raw);
|
|
1920
2138
|
} catch {
|
|
1921
|
-
throw new CliError(
|
|
2139
|
+
throw new CliError(msg.invalidBodyJson, 2);
|
|
1922
2140
|
}
|
|
1923
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(
|
|
2141
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(msg.bodyNotObject, 2);
|
|
1924
2142
|
return parsed;
|
|
1925
2143
|
}
|
|
1926
2144
|
function hostError(err) {
|
|
@@ -1931,30 +2149,34 @@ function hostError(err) {
|
|
|
1931
2149
|
${hint}` : line, code);
|
|
1932
2150
|
}
|
|
1933
2151
|
async function runRoute(ctx, group, cmd, argv) {
|
|
2152
|
+
const msg = { ...DEFAULT_MESSAGES, ...cmd.messages ?? {} };
|
|
1934
2153
|
const rest = argv.slice(cmd.words.length);
|
|
1935
2154
|
const fields = [...cmd.query ?? [], ...cmd.body ?? []];
|
|
1936
2155
|
const bool = [...COMMON_BOOL, ...fields.filter((f) => f.type === "boolean").flatMap((f) => [f.flag, f.flag.replace(/^--/, "--no-")])];
|
|
1937
2156
|
const isMulti = (f) => f.type === "list" || f.type === "map";
|
|
1938
|
-
const value = [
|
|
2157
|
+
const value = [
|
|
2158
|
+
...cmd.bodyFlags !== false ? ["--body", "--body-file"] : [],
|
|
2159
|
+
...bindsOrg(cmd) ? ["--org"] : [],
|
|
2160
|
+
...fields.filter((f) => f.type !== "boolean" && !isMulti(f)).map((f) => f.flag)
|
|
2161
|
+
];
|
|
1939
2162
|
const multi = fields.filter(isMulti).map((f) => f.flag);
|
|
1940
2163
|
const { options, positionals } = parseOptions(rest, { bool, value, multi });
|
|
1941
2164
|
if (options.help) {
|
|
1942
|
-
|
|
2165
|
+
if (cmd.usageText !== void 0) write(ctx.io.stdout, cmd.usageText);
|
|
2166
|
+
else writeLine(ctx.io.stdout, `Usage: ${usageLine(group, cmd)}
|
|
1943
2167
|
|
|
1944
2168
|
${cmd.summary}
|
|
1945
2169
|
Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
1946
2170
|
return 0;
|
|
1947
2171
|
}
|
|
1948
|
-
const template = templates(cmd).find((p) =>
|
|
2172
|
+
const template = templates(cmd).find((p) => positionalNames(cmd, p).length === positionals.length);
|
|
1949
2173
|
if (!template) {
|
|
1950
|
-
write(ctx.io.stderr, `Usage: ${usageLine(group, cmd)}
|
|
2174
|
+
write(ctx.io.stderr, cmd.usageText ?? `Usage: ${usageLine(group, cmd)}
|
|
1951
2175
|
`);
|
|
1952
2176
|
return 2;
|
|
1953
2177
|
}
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
path = path.replace(`:${name}`, encodeURIComponent(positionals[i]));
|
|
1957
|
-
});
|
|
2178
|
+
const path = fillPath(cmd, template, positionals, options.org);
|
|
2179
|
+
const requiredError = (f) => new CliError(msg.required(f.flag, cmd.usageText ?? usageLine(group, cmd)), 2);
|
|
1958
2180
|
const optionOf = (f) => {
|
|
1959
2181
|
const name = f.flag.replace(/^--/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1960
2182
|
if (f.type === "boolean") {
|
|
@@ -1969,34 +2191,39 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
1969
2191
|
for (const f of cmd.query ?? []) {
|
|
1970
2192
|
const v = optionOf(f);
|
|
1971
2193
|
if (v === void 0) {
|
|
1972
|
-
if (f.required) throw
|
|
1973
|
-
${f.flag} is required.`, 2);
|
|
2194
|
+
if (f.required) throw requiredError(f);
|
|
1974
2195
|
continue;
|
|
1975
2196
|
}
|
|
1976
|
-
const c = coerce(f, v);
|
|
1977
|
-
if (
|
|
1978
|
-
else qs2.
|
|
2197
|
+
const c = coerce(f, v, cmd, msg);
|
|
2198
|
+
if (f.type === "csv") qs2.set(f.key, c.join(","));
|
|
2199
|
+
else if (Array.isArray(c)) c.forEach((x) => qs2.append(f.key, String(x)));
|
|
2200
|
+
else qs2.set(f.key, c !== null && typeof c === "object" ? JSON.stringify(c) : String(c));
|
|
1979
2201
|
}
|
|
1980
2202
|
const url = qs2.size ? `${path}${path.includes("?") ? "&" : "?"}${qs2.toString()}` : path;
|
|
1981
2203
|
const confirm2 = cmd.confirm ?? cmd.method === "DELETE";
|
|
1982
|
-
|
|
1983
|
-
|
|
2204
|
+
const gate = () => {
|
|
2205
|
+
if (confirm2 && !options.yes) throw new CliError(msg.refusal(cmd.words, positionals), 2);
|
|
2206
|
+
};
|
|
2207
|
+
if (!cmd.validateFirst) gate();
|
|
1984
2208
|
const isWrite = cmd.method !== "GET" && cmd.method !== "DELETE";
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
2209
|
+
const takesBody = isWrite || Boolean(cmd.body?.length) || cmd.bodyFlags === true;
|
|
2210
|
+
let explicit = {};
|
|
2211
|
+
const overlay = {};
|
|
2212
|
+
if (takesBody) {
|
|
2213
|
+
explicit = readBodyOptions(options, msg);
|
|
1988
2214
|
for (const f of cmd.body ?? []) {
|
|
1989
2215
|
const v = optionOf(f);
|
|
1990
|
-
if (v
|
|
1991
|
-
|
|
2216
|
+
if (v !== void 0) setPath(overlay, f.key, coerce(f, v, cmd, msg));
|
|
2217
|
+
else if (cmd.validateFirst && f.required && getPath(explicit, f.key) === void 0) throw requiredError(f);
|
|
1992
2218
|
}
|
|
2219
|
+
}
|
|
2220
|
+
if (cmd.validateFirst) gate();
|
|
2221
|
+
let body;
|
|
2222
|
+
if (takesBody) {
|
|
1993
2223
|
let base16 = {};
|
|
1994
2224
|
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);
|
|
2225
|
+
const from = fillPath(cmd, cmd.rmw.from ?? template, positionals, options.org);
|
|
2226
|
+
const current = await send(ctx, cmd, from, { method: "GET" });
|
|
2000
2227
|
const picked = cmd.rmw.pick ? cmd.rmw.pick(current.body) : current.body;
|
|
2001
2228
|
base16 = picked && typeof picked === "object" && !Array.isArray(picked) ? { ...picked } : {};
|
|
2002
2229
|
}
|
|
@@ -2005,13 +2232,15 @@ ${f.flag} is required.`, 2);
|
|
|
2005
2232
|
const v = getPath(overlay, f.key);
|
|
2006
2233
|
if (v !== void 0) setPath(body, f.key, v);
|
|
2007
2234
|
}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2235
|
+
if (!cmd.validateFirst) {
|
|
2236
|
+
for (const f of cmd.body ?? []) {
|
|
2237
|
+
if (f.required && getPath(body, f.key) === void 0) throw requiredError(f);
|
|
2238
|
+
}
|
|
2011
2239
|
}
|
|
2012
2240
|
if (!isWrite && Object.keys(body).length === 0) body = void 0;
|
|
2013
2241
|
if (isWrite && body && Object.keys(body).length === 0 && cmd.emptyBody === false) body = void 0;
|
|
2014
2242
|
}
|
|
2243
|
+
if (cmd.notice !== void 0) writeLine(ctx.io.stderr, cmd.notice);
|
|
2015
2244
|
try {
|
|
2016
2245
|
const res = await requestJson(ctx, url, { method: cmd.method, ...body !== void 0 ? { body } : {}, ...cmd.anonymous ? { auth: false } : {} });
|
|
2017
2246
|
render(ctx, cmd, positionals, res.status, res.body);
|
|
@@ -2021,25 +2250,42 @@ ${f.flag} is required.`, 2);
|
|
|
2021
2250
|
render(ctx, { ...cmd, method: "GET" }, positionals, err.status, err.body);
|
|
2022
2251
|
return 1;
|
|
2023
2252
|
}
|
|
2024
|
-
if (err instanceof HttpError) throw hostError(err);
|
|
2253
|
+
if (err instanceof HttpError && cmd.hostErrors !== false) throw hostError(err);
|
|
2025
2254
|
throw err;
|
|
2026
2255
|
}
|
|
2027
2256
|
}
|
|
2028
2257
|
function getPath(obj, key) {
|
|
2029
2258
|
return key.split(".").reduce((node, part) => node && typeof node === "object" ? node[part] : void 0, obj);
|
|
2030
2259
|
}
|
|
2031
|
-
async function send(ctx, url, init
|
|
2260
|
+
async function send(ctx, cmd, url, init) {
|
|
2032
2261
|
try {
|
|
2033
|
-
return await requestJson(ctx, url, { method: init.method, ...init.body !== void 0 ? { body: init.body } : {}, ...anonymous ? { auth: false } : {} });
|
|
2262
|
+
return await requestJson(ctx, url, { method: init.method, ...init.body !== void 0 ? { body: init.body } : {}, ...cmd.anonymous ? { auth: false } : {} });
|
|
2034
2263
|
} catch (err) {
|
|
2035
|
-
if (err instanceof HttpError) throw hostError(err);
|
|
2264
|
+
if (err instanceof HttpError && cmd.hostErrors !== false) throw hostError(err);
|
|
2036
2265
|
throw err;
|
|
2037
2266
|
}
|
|
2038
2267
|
}
|
|
2268
|
+
function tableRows(table3, body) {
|
|
2269
|
+
if (!table3.anyArray) return table3.key ? getPath(body ?? {}, table3.key) : body;
|
|
2270
|
+
if (Array.isArray(body)) return body;
|
|
2271
|
+
const keyed = table3.key ? getPath(body ?? {}, table3.key) : void 0;
|
|
2272
|
+
if (Array.isArray(keyed)) return keyed;
|
|
2273
|
+
return Object.values(body ?? {}).find((v) => Array.isArray(v)) ?? [];
|
|
2274
|
+
}
|
|
2039
2275
|
function render(ctx, cmd, positionals, status, rawBody) {
|
|
2040
2276
|
const body = cmd.transform ? cmd.transform(rawBody) : rawBody;
|
|
2277
|
+
const noContent = body === null || body === void 0 ? cmd.noContent : void 0;
|
|
2041
2278
|
if (ctx.json) {
|
|
2042
|
-
writeJson(ctx.io.stdout, body);
|
|
2279
|
+
writeJson(ctx.io.stdout, noContent !== void 0 ? { ok: true, status } : body);
|
|
2280
|
+
return;
|
|
2281
|
+
}
|
|
2282
|
+
if (cmd.rawText && body && typeof body === "object" && typeof body.raw === "string" && Object.keys(body).length === 1) {
|
|
2283
|
+
write(ctx.io.stdout, body.raw.endsWith("\n") ? body.raw : `${body.raw}
|
|
2284
|
+
`);
|
|
2285
|
+
return;
|
|
2286
|
+
}
|
|
2287
|
+
if (noContent !== void 0) {
|
|
2288
|
+
writeLine(ctx.io.stdout, noContent.replace("{status}", String(status)));
|
|
2043
2289
|
return;
|
|
2044
2290
|
}
|
|
2045
2291
|
if (cmd.human) {
|
|
@@ -2047,7 +2293,7 @@ function render(ctx, cmd, positionals, status, rawBody) {
|
|
|
2047
2293
|
return;
|
|
2048
2294
|
}
|
|
2049
2295
|
if (cmd.table) {
|
|
2050
|
-
const rows = cmd.table
|
|
2296
|
+
const rows = tableRows(cmd.table, body);
|
|
2051
2297
|
if (rows === null || rows === void 0) {
|
|
2052
2298
|
writeLine(ctx.io.stdout, cmd.table.empty ?? "None.");
|
|
2053
2299
|
return;
|
|
@@ -2065,7 +2311,7 @@ function render(ctx, cmd, positionals, status, rawBody) {
|
|
|
2065
2311
|
return;
|
|
2066
2312
|
}
|
|
2067
2313
|
}
|
|
2068
|
-
if (cmd.method === "GET") {
|
|
2314
|
+
if (cmd.method === "GET" || cmd.writeOutput === "json") {
|
|
2069
2315
|
writeJson(ctx.io.stdout, body);
|
|
2070
2316
|
return;
|
|
2071
2317
|
}
|
|
@@ -2460,67 +2706,6 @@ function lifecycleError(err) {
|
|
|
2460
2706
|
throw err;
|
|
2461
2707
|
}
|
|
2462
2708
|
|
|
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
2709
|
// src/cli/contentHelpers.ts
|
|
2525
2710
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
2526
2711
|
import { resolve as resolvePath2 } from "node:path";
|
|
@@ -2873,19 +3058,37 @@ import { readFileSync as readFileSync6 } from "node:fs";
|
|
|
2873
3058
|
|
|
2874
3059
|
// src/cli/requestHelpers.ts
|
|
2875
3060
|
var NOT_SERVED = /* @__PURE__ */ new Set([404, 405, 501]);
|
|
3061
|
+
function presentsCredential(ctx, req2) {
|
|
3062
|
+
if (req2.auth !== false && ctx.apiKey) return true;
|
|
3063
|
+
return Object.keys(req2.headers ?? {}).some((h) => /^(authorization|cookie)$/i.test(h));
|
|
3064
|
+
}
|
|
2876
3065
|
async function requestNormativeOrHost(ctx, normativePath, hostPath, options = {}) {
|
|
2877
3066
|
const { forceHost, ...req2 } = options;
|
|
3067
|
+
let anonymousRetryOf;
|
|
2878
3068
|
if (!forceHost) {
|
|
2879
3069
|
try {
|
|
2880
3070
|
const res2 = await requestJson(ctx, normativePath, req2);
|
|
2881
3071
|
if (ctx.verbose) writeLine(ctx.io.stderr, `openwop: served by the normative path ${normativePath}`);
|
|
2882
3072
|
return { ...res2, path: normativePath, via: "normative" };
|
|
2883
3073
|
} catch (err) {
|
|
2884
|
-
if (!(err instanceof HttpError)
|
|
3074
|
+
if (!(err instanceof HttpError)) throw err;
|
|
3075
|
+
if (isNoCredentialChallenge(err) && !presentsCredential(ctx, req2)) anonymousRetryOf = err;
|
|
3076
|
+
else if (!NOT_SERVED.has(err.status)) throw err;
|
|
2885
3077
|
}
|
|
2886
3078
|
}
|
|
2887
|
-
|
|
2888
|
-
|
|
3079
|
+
let res;
|
|
3080
|
+
try {
|
|
3081
|
+
res = await requestJson(ctx, hostPath, req2);
|
|
3082
|
+
} catch (err) {
|
|
3083
|
+
if (anonymousRetryOf && err instanceof HttpError && (NOT_SERVED.has(err.status) || err.status === 401)) throw anonymousRetryOf;
|
|
3084
|
+
throw err;
|
|
3085
|
+
}
|
|
3086
|
+
if (anonymousRetryOf) {
|
|
3087
|
+
const sent = (await resolveRequest(ctx, hostPath, {})).path;
|
|
3088
|
+
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.`);
|
|
3089
|
+
} else if (ctx.verbose) {
|
|
3090
|
+
writeLine(ctx.io.stderr, `openwop: served by the host-extension path ${hostPath}`);
|
|
3091
|
+
}
|
|
2889
3092
|
return { ...res, path: hostPath, via: "host" };
|
|
2890
3093
|
}
|
|
2891
3094
|
function failClosedOn404(err, surface) {
|
|
@@ -4847,6 +5050,8 @@ GET /v1/agents and GET /v1/agents/{agentId}. When the host does not serve the
|
|
|
4847
5050
|
normative read (404/405/501) they fall back to the host-extension alias
|
|
4848
5051
|
GET /v1/host/openwop-app/agents[/{agentId}]; --host forces the alias (which also
|
|
4849
5052
|
reports the host's runtime posture); --verbose names the path that answered.
|
|
5053
|
+
With no credential, a v2 host refuses the normative read (401); the command
|
|
5054
|
+
then shows the host's anonymous view from the alias and says so on stderr.
|
|
4850
5055
|
'run' dispatches one agent turn via
|
|
4851
5056
|
POST /v1/host/openwop-app/agents/{agentId}/dispatch \u2014 the tool surface is filtered to
|
|
4852
5057
|
the agent's toolAllowlist (RFC 0002 \xA7A14), task/return payloads are validated
|
|
@@ -5170,6 +5375,8 @@ GET /v1/agents/roster. The normative view carries only protocol-shaped entries
|
|
|
5170
5375
|
(the host may omit host-only entries such as advisors); when the host does not
|
|
5171
5376
|
serve it (404/405/501), or with --host, 'list' reads the host-extension
|
|
5172
5377
|
GET /v1/host/openwop-app/roster instead (--verbose names the path that answered).
|
|
5378
|
+
With no credential, a v2 host refuses the normative read (401); 'list' then
|
|
5379
|
+
shows the host's anonymous view from the alias and says so on stderr.
|
|
5173
5380
|
'get'/'create'/'update'/'delete' drive the host-extension surface
|
|
5174
5381
|
GET/POST/PATCH/DELETE /v1/host/openwop-app/roster[/{rosterId}].
|
|
5175
5382
|
|
|
@@ -5352,7 +5559,9 @@ field).
|
|
|
5352
5559
|
GET /v1/agents/org-chart/{departmentId}. When the host does not serve it
|
|
5353
5560
|
(404/405/501 on 'get'), or with --host, they read the host-extension
|
|
5354
5561
|
GET /v1/host/openwop-app/org-chart[/{departmentId}] (--verbose names the path
|
|
5355
|
-
that answered).
|
|
5562
|
+
that answered). With no credential, a v2 host refuses the normative read (401);
|
|
5563
|
+
'get'/'dept' then show the host's anonymous view from the alias and say so on
|
|
5564
|
+
stderr. 'set'/'clear' drive the host-extension PUT/DELETE
|
|
5356
5565
|
/v1/host/openwop-app/org-chart. 'set' replaces the whole chart from a JSON file
|
|
5357
5566
|
with { "departments": [...], "members": [...] }.
|
|
5358
5567
|
|
|
@@ -5734,9 +5943,7 @@ async function boardWatch(ctx, argv) {
|
|
|
5734
5943
|
write(ctx.io.stdout, "Usage: openwop kanban watch <boardId>\n");
|
|
5735
5944
|
return options.help ? 0 : 2;
|
|
5736
5945
|
}
|
|
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}`;
|
|
5946
|
+
const { url, headers } = await resolveStreamRequest(ctx, `/v1/host/openwop-app/kanban/boards/${encodeURIComponent(positionals[0])}/events`, {});
|
|
5740
5947
|
const res = await ctx.fetchImpl(url, { headers });
|
|
5741
5948
|
if (!res.ok || !res.body) {
|
|
5742
5949
|
throw new CliError(`Could not open the board event stream (HTTP ${res.status}).`, 1);
|
|
@@ -6061,7 +6268,6 @@ async function brandAsset(ctx, argv) {
|
|
|
6061
6268
|
}
|
|
6062
6269
|
|
|
6063
6270
|
// src/cli/resourceCommands.ts
|
|
6064
|
-
import { readFileSync as readFileSync9 } from "node:fs";
|
|
6065
6271
|
function parseField(spec) {
|
|
6066
6272
|
let s = spec;
|
|
6067
6273
|
let flagOverride;
|
|
@@ -6076,35 +6282,6 @@ function parseField(spec) {
|
|
|
6076
6282
|
const flag = `--${flagOverride ?? key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`).replace(/_/g, "-")}`;
|
|
6077
6283
|
return { key, flag, type, required };
|
|
6078
6284
|
}
|
|
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
6285
|
function hasOrg(route) {
|
|
6109
6286
|
return /:org(?![A-Za-z0-9_])/.test(route);
|
|
6110
6287
|
}
|
|
@@ -6147,142 +6324,62 @@ ${footer ? `
|
|
|
6147
6324
|
${footer.trim()}
|
|
6148
6325
|
` : ""}`;
|
|
6149
6326
|
}
|
|
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;
|
|
6327
|
+
var TYPES = { string: "string", number: "number", bool: "bool", json: "json", list: "csv", file: "file" };
|
|
6328
|
+
function toField(spec) {
|
|
6329
|
+
const f = parseField(spec);
|
|
6330
|
+
return { flag: f.flag, key: f.key, type: TYPES[f.type] ?? "string", required: f.required };
|
|
6180
6331
|
}
|
|
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 });
|
|
6332
|
+
function toRouteCmd(group, spec) {
|
|
6189
6333
|
const usage6 = `Usage: ${usageLine2(group, spec)}
|
|
6190
6334
|
${spec.method} ${spec.route} \u2014 ${spec.summary}
|
|
6191
6335
|
`;
|
|
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, {
|
|
6336
|
+
const rmwKey = spec.rmwKey;
|
|
6337
|
+
return {
|
|
6338
|
+
words: spec.cmd,
|
|
6244
6339
|
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
|
-
|
|
6340
|
+
path: spec.route,
|
|
6341
|
+
summary: spec.summary,
|
|
6342
|
+
query: (spec.query ?? []).map(toField),
|
|
6343
|
+
body: (spec.body ?? []).map(toField),
|
|
6344
|
+
orgFlag: true,
|
|
6345
|
+
bodyFlags: Boolean(spec.body || spec.rawBody),
|
|
6346
|
+
emptyBody: true,
|
|
6347
|
+
confirm: Boolean(spec.confirm),
|
|
6348
|
+
anonymous: spec.auth === false,
|
|
6349
|
+
...spec.list ? { table: { key: spec.list.key, columns: spec.list.columns, empty: spec.list.empty ?? "None.", anyArray: true } } : {},
|
|
6350
|
+
...spec.rmw ? { rmw: { from: spec.rmw, pick: (body) => rmwKey ? body?.[rmwKey] : body } } : {},
|
|
6351
|
+
...spec.notice !== void 0 ? { notice: spec.notice } : {},
|
|
6352
|
+
rawText: true,
|
|
6353
|
+
// every spec, as before — `text` only documents the route
|
|
6354
|
+
noContent: spec.method === "DELETE" ? "Deleted." : "Done (HTTP {status}).",
|
|
6355
|
+
writeOutput: "json",
|
|
6356
|
+
hostErrors: false,
|
|
6357
|
+
validateFirst: true,
|
|
6358
|
+
strictNumbers: true,
|
|
6359
|
+
usageText: usage6,
|
|
6360
|
+
messages: {
|
|
6361
|
+
required: (flag, usageText) => `${flag} is required.
|
|
6362
|
+
${usageText}`,
|
|
6363
|
+
refusal: () => `Refusing to ${spec.summary.charAt(0).toLowerCase()}${spec.summary.slice(1).replace(/\.$/, "")} without --yes.`,
|
|
6364
|
+
invalidNumber: (flag, raw) => `${flag} must be a number, got: ${raw}`,
|
|
6365
|
+
invalidBool: (flag, raw) => `${flag} must be true or false, got: ${raw}`,
|
|
6366
|
+
invalidJson: (flag) => `${flag} must be valid JSON`,
|
|
6367
|
+
unreadable: (flag, path) => `Cannot read ${flag} ${path}`,
|
|
6368
|
+
unreadableBody: (path) => `Cannot read --body-file ${path}`,
|
|
6369
|
+
invalidBodyJson: "--body/--body-file must be valid JSON",
|
|
6370
|
+
bodyNotObject: "--body/--body-file must be a JSON object"
|
|
6269
6371
|
}
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
}), spec.list.columns));
|
|
6278
|
-
return 0;
|
|
6279
|
-
}
|
|
6280
|
-
writeJson(ctx.io.stdout, body);
|
|
6281
|
-
return 0;
|
|
6372
|
+
};
|
|
6373
|
+
}
|
|
6374
|
+
function matchSpec(specs, argv) {
|
|
6375
|
+
return matchRoute(specs.map((spec) => ({ words: spec.cmd, spec })), argv)?.spec;
|
|
6376
|
+
}
|
|
6377
|
+
async function runSpec(ctx, group, spec, argv) {
|
|
6378
|
+
return runRoute(ctx, group, toRouteCmd(group, spec), [...spec.cmd, ...argv]);
|
|
6282
6379
|
}
|
|
6283
6380
|
async function dispatchSpecs(ctx, group, specs, argv) {
|
|
6284
|
-
const
|
|
6285
|
-
return
|
|
6381
|
+
const spec = matchSpec(specs, argv);
|
|
6382
|
+
return spec ? runSpec(ctx, group, spec, argv.slice(spec.cmd.length)) : void 0;
|
|
6286
6383
|
}
|
|
6287
6384
|
function specsUsage(group, specs) {
|
|
6288
6385
|
return specs.map((s) => ` ${usageLine2(group, s)}
|
|
@@ -6293,12 +6390,12 @@ async function runResourceGroup(ctx, group, help, specs, argv) {
|
|
|
6293
6390
|
write(ctx.io.stdout, help);
|
|
6294
6391
|
return 0;
|
|
6295
6392
|
}
|
|
6296
|
-
const
|
|
6297
|
-
if (!
|
|
6393
|
+
const spec = matchSpec(specs, argv);
|
|
6394
|
+
if (!spec) {
|
|
6298
6395
|
throw new CliError(`Unknown ${group} command: ${argv.filter((a) => !a.startsWith("-")).slice(0, 2).join(" ")}
|
|
6299
6396
|
Run \`openwop ${group} --help\` for usage.`);
|
|
6300
6397
|
}
|
|
6301
|
-
return runSpec(ctx, group,
|
|
6398
|
+
return runSpec(ctx, group, spec, argv.slice(spec.cmd.length));
|
|
6302
6399
|
}
|
|
6303
6400
|
|
|
6304
6401
|
// src/cli/crmOrg.ts
|
|
@@ -6975,7 +7072,7 @@ async function sharingFrameViews(ctx, argv) {
|
|
|
6975
7072
|
}
|
|
6976
7073
|
|
|
6977
7074
|
// src/cli/marketingShared.ts
|
|
6978
|
-
import { readFileSync as
|
|
7075
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
6979
7076
|
import { resolve as resolvePath5 } from "node:path";
|
|
6980
7077
|
var APP = "/v1/host/openwop-app";
|
|
6981
7078
|
async function dispatchTable(ctx, group, help, table3, argv, defaultSub) {
|
|
@@ -7033,14 +7130,14 @@ function jsonObject(text, flag) {
|
|
|
7033
7130
|
}
|
|
7034
7131
|
function readText(ctx, path, flag) {
|
|
7035
7132
|
try {
|
|
7036
|
-
return
|
|
7133
|
+
return readFileSync9(resolvePath5(ctx.cwd, path), "utf8");
|
|
7037
7134
|
} catch (err) {
|
|
7038
7135
|
throw new CliError(`Cannot read ${flag} ${path}: ${err instanceof Error ? err.message : String(err)}`, 2);
|
|
7039
7136
|
}
|
|
7040
7137
|
}
|
|
7041
7138
|
function readBase64(ctx, path, flag) {
|
|
7042
7139
|
try {
|
|
7043
|
-
return
|
|
7140
|
+
return readFileSync9(resolvePath5(ctx.cwd, path)).toString("base64");
|
|
7044
7141
|
} catch (err) {
|
|
7045
7142
|
throw new CliError(`Cannot read ${flag} ${path}: ${err instanceof Error ? err.message : String(err)}`, 2);
|
|
7046
7143
|
}
|
|
@@ -8652,10 +8749,10 @@ Run \`openwop documents --help\` for usage.`);
|
|
|
8652
8749
|
if (!id) return usage(ctx, "add-version <documentId> --org <orgId> (--content <text> | --content-file <path>)");
|
|
8653
8750
|
let content = options.content !== void 0 ? String(options.content) : void 0;
|
|
8654
8751
|
if (options.contentFile) {
|
|
8655
|
-
const { readFileSync:
|
|
8752
|
+
const { readFileSync: readFileSync22 } = await import("node:fs");
|
|
8656
8753
|
const { resolve: resolve5 } = await import("node:path");
|
|
8657
8754
|
try {
|
|
8658
|
-
content =
|
|
8755
|
+
content = readFileSync22(resolve5(ctx.cwd, String(options.contentFile)), "utf8");
|
|
8659
8756
|
} catch (err) {
|
|
8660
8757
|
throw new CliError(`Cannot read --content-file ${String(options.contentFile)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
8661
8758
|
}
|
|
@@ -9242,7 +9339,7 @@ Run \`openwop reviews --help\` for usage.`);
|
|
|
9242
9339
|
}
|
|
9243
9340
|
|
|
9244
9341
|
// src/cli/adminShared.ts
|
|
9245
|
-
import { readFileSync as
|
|
9342
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
9246
9343
|
import { resolve as resolve2 } from "node:path";
|
|
9247
9344
|
|
|
9248
9345
|
// src/prompt.ts
|
|
@@ -9361,7 +9458,7 @@ async function readSecretInput(ctx, options, label) {
|
|
|
9361
9458
|
let value;
|
|
9362
9459
|
if (options.valueFile !== void 0) {
|
|
9363
9460
|
try {
|
|
9364
|
-
value =
|
|
9461
|
+
value = readFileSync10(resolve2(ctx.cwd, String(options.valueFile)), "utf8").replace(/\r?\n$/, "");
|
|
9365
9462
|
} catch (err) {
|
|
9366
9463
|
throw new CliError(`Cannot read --value-file ${options.valueFile}: ${err instanceof Error ? err.message : String(err)}`, 2);
|
|
9367
9464
|
}
|
|
@@ -10315,7 +10412,7 @@ async function runCampaignsOrch(ctx, argv) {
|
|
|
10315
10412
|
}
|
|
10316
10413
|
|
|
10317
10414
|
// src/cli/notebooks.ts
|
|
10318
|
-
import { readFileSync as
|
|
10415
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
10319
10416
|
import { extname as extname3, resolve as resolvePath7 } from "node:path";
|
|
10320
10417
|
var BASE9 = "/v1/host/openwop-app/notebooks";
|
|
10321
10418
|
var NOTEBOOKS_HELP = `Usage:
|
|
@@ -10390,7 +10487,7 @@ var MIME = {
|
|
|
10390
10487
|
function readFileBase64(ctx, file, contentType) {
|
|
10391
10488
|
let buf;
|
|
10392
10489
|
try {
|
|
10393
|
-
buf =
|
|
10490
|
+
buf = readFileSync11(resolvePath7(ctx.cwd, file));
|
|
10394
10491
|
} catch (err) {
|
|
10395
10492
|
throw new CliError(`Cannot read file ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
10396
10493
|
}
|
|
@@ -12319,6 +12416,11 @@ honouring the server's \`retry:\` field), and no event is printed twice.
|
|
|
12319
12416
|
debug, or a comma list of updates/messages/debug
|
|
12320
12417
|
(\`values\` never combines). A host that does not serve
|
|
12321
12418
|
the mode answers 400 unsupported_stream_mode.
|
|
12419
|
+
--idle-timeout-ms N a connection that delivers no bytes for N ms (default
|
|
12420
|
+
45000; 0 disables) is treated as dropped and resumed.
|
|
12421
|
+
Streams are read from the stream origin: --stream-base-url (global), else the
|
|
12422
|
+
host's advertised \`streamBase\` (https only), else --base-url \u2014 some front
|
|
12423
|
+
doors buffer event streams entirely (e.g. a CDN rewrite).
|
|
12322
12424
|
Exit 0 when the run completes, 1 when it fails or is cancelled. \`runs list\` pages with --cursor: pass the
|
|
12323
12425
|
\`nextCursor\` the previous page printed.
|
|
12324
12426
|
|
|
@@ -12541,7 +12643,7 @@ async function runRunsBulkCancel(ctx, argv) {
|
|
|
12541
12643
|
return 0;
|
|
12542
12644
|
}
|
|
12543
12645
|
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"] });
|
|
12646
|
+
const { options, positionals } = parseOptions(argv, { bool: ["--help", "--follow", "--no-stream"], value: ["--since", "--limit", "--last-event-id", "--stream-mode", "--timeout-ms", "--idle-timeout-ms"] });
|
|
12545
12647
|
if (options.help || positionals.length !== 1) {
|
|
12546
12648
|
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
12649
|
return options.help ? 0 : 2;
|
|
@@ -12579,6 +12681,7 @@ async function followRunEvents(ctx, runId, options) {
|
|
|
12579
12681
|
...start,
|
|
12580
12682
|
useStream: !options.noStream,
|
|
12581
12683
|
timeoutMs,
|
|
12684
|
+
...options.idleTimeoutMs !== void 0 ? { idleTimeoutMs: nonNegativeInt(options.idleTimeoutMs, "--idle-timeout-ms") } : {},
|
|
12582
12685
|
onEvent: (ev) => {
|
|
12583
12686
|
if (isTerminalRunEvent(ev)) terminalType = canonicalEventType(ev.type);
|
|
12584
12687
|
if (ctx.json) {
|
|
@@ -12818,10 +12921,15 @@ async function runRunsRedrive(ctx, argv) {
|
|
|
12818
12921
|
})), ["runId", "redriveRunId", "error"]));
|
|
12819
12922
|
return anyFailed ? 1 : 0;
|
|
12820
12923
|
}
|
|
12924
|
+
function nonNegativeInt(value, flag) {
|
|
12925
|
+
const n = Number(value);
|
|
12926
|
+
if (!Number.isInteger(n) || n < 0) throw new CliError(`${flag} must be a non-negative integer (got ${String(value)})`);
|
|
12927
|
+
return n;
|
|
12928
|
+
}
|
|
12821
12929
|
|
|
12822
12930
|
// src/cli/chat.ts
|
|
12823
12931
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
12824
|
-
import { readFileSync as
|
|
12932
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
|
|
12825
12933
|
var CHAT_BASE = "/v1/host/openwop-app/chat";
|
|
12826
12934
|
var CHAT_SUBCOMMANDS = ["repl", "sessions", "session", "messages", "message", "participants", "open", "feedback", "models", "search", "export", "import", "tools"];
|
|
12827
12935
|
var CHAT_HELP = `Usage:
|
|
@@ -13411,7 +13519,7 @@ Requires a signed-in account.
|
|
|
13411
13519
|
}
|
|
13412
13520
|
let data;
|
|
13413
13521
|
try {
|
|
13414
|
-
data = JSON.parse(
|
|
13522
|
+
data = JSON.parse(readFileSync12(String(options.file), "utf8"));
|
|
13415
13523
|
} catch (err) {
|
|
13416
13524
|
throw new CliError(`--file: cannot read JSON from ${options.file} (${err instanceof Error ? err.message : String(err)})`, 2);
|
|
13417
13525
|
}
|
|
@@ -13505,7 +13613,7 @@ Run \`openwop chat tools --help\` for usage.`, 2);
|
|
|
13505
13613
|
}
|
|
13506
13614
|
|
|
13507
13615
|
// src/cli/relayShared.ts
|
|
13508
|
-
import { existsSync as existsSync4, readFileSync as
|
|
13616
|
+
import { existsSync as existsSync4, readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync3, rmSync as rmSync2, chmodSync as chmodSync3 } from "node:fs";
|
|
13509
13617
|
import { dirname as dirname4, join as join5 } from "node:path";
|
|
13510
13618
|
var MESSAGING_BASE = "/v1/host/openwop-app/messaging";
|
|
13511
13619
|
var DEVICE_TOKEN_HEADER = "x-openwop-device-token";
|
|
@@ -13516,7 +13624,7 @@ function relayCredsPath(env) {
|
|
|
13516
13624
|
function loadRelayConfig(ctx) {
|
|
13517
13625
|
try {
|
|
13518
13626
|
const p = relayCredsPath(ctx.env);
|
|
13519
|
-
if (existsSync4(p)) return JSON.parse(
|
|
13627
|
+
if (existsSync4(p)) return JSON.parse(readFileSync13(p, "utf8"));
|
|
13520
13628
|
} catch {
|
|
13521
13629
|
}
|
|
13522
13630
|
return {};
|
|
@@ -13552,7 +13660,7 @@ function relayLogPath(env) {
|
|
|
13552
13660
|
function readRelayRecord(env) {
|
|
13553
13661
|
try {
|
|
13554
13662
|
const p = relayPidPath(env);
|
|
13555
|
-
return existsSync4(p) ? JSON.parse(
|
|
13663
|
+
return existsSync4(p) ? JSON.parse(readFileSync13(p, "utf8")) : null;
|
|
13556
13664
|
} catch {
|
|
13557
13665
|
return null;
|
|
13558
13666
|
}
|
|
@@ -14147,7 +14255,7 @@ Usage: openwop notify <email|sms> --to <addr> --text <msg> [--subject s]`);
|
|
|
14147
14255
|
|
|
14148
14256
|
// src/cli/relay.ts
|
|
14149
14257
|
import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
|
|
14150
|
-
import { existsSync as existsSync5, readFileSync as
|
|
14258
|
+
import { existsSync as existsSync5, readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync4, rmSync as rmSync3 } from "node:fs";
|
|
14151
14259
|
import { dirname as dirname5 } from "node:path";
|
|
14152
14260
|
import { fileURLToPath } from "node:url";
|
|
14153
14261
|
var RELAY_HELP = `Usage:
|
|
@@ -14498,7 +14606,7 @@ async function runRelayLogs(ctx, argv) {
|
|
|
14498
14606
|
const r = spawnSync3("tail", ["-f", logPath], { stdio: "inherit" });
|
|
14499
14607
|
if (r.status === 0 || r.signal) return 0;
|
|
14500
14608
|
}
|
|
14501
|
-
write(ctx.io.stdout,
|
|
14609
|
+
write(ctx.io.stdout, readFileSync14(logPath, "utf8"));
|
|
14502
14610
|
return 0;
|
|
14503
14611
|
}
|
|
14504
14612
|
|
|
@@ -14867,7 +14975,7 @@ async function runProvidersTest(ctx, argv) {
|
|
|
14867
14975
|
// src/cli/packs.ts
|
|
14868
14976
|
import { createHash, createPrivateKey, createPublicKey, generateKeyPairSync, sign as ed25519Sign, verify as ed25519Verify } from "node:crypto";
|
|
14869
14977
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
14870
|
-
import { existsSync as existsSync6, readFileSync as
|
|
14978
|
+
import { existsSync as existsSync6, readFileSync as readFileSync15, writeFileSync as writeFileSync10, mkdirSync as mkdirSync5, readdirSync, statSync } from "node:fs";
|
|
14871
14979
|
import { join as join6, resolve as resolvePath8 } from "node:path";
|
|
14872
14980
|
import { homedir as homedir3 } from "node:os";
|
|
14873
14981
|
var PACKS_HELP = `Usage:
|
|
@@ -15124,7 +15232,7 @@ async function runPacksPublish(ctx, argv) {
|
|
|
15124
15232
|
if (!existsSync6(manifestPath)) {
|
|
15125
15233
|
throw new CliError(`No pack.json found in ${absPackDir}.`);
|
|
15126
15234
|
}
|
|
15127
|
-
const manifest = JSON.parse(
|
|
15235
|
+
const manifest = JSON.parse(readFileSync15(manifestPath, "utf8"));
|
|
15128
15236
|
const name = manifest.name;
|
|
15129
15237
|
const version = manifest.version;
|
|
15130
15238
|
if (!name || !version) throw new CliError('pack.json must declare both "name" and "version".');
|
|
@@ -15137,11 +15245,11 @@ async function runPacksPublish(ctx, argv) {
|
|
|
15137
15245
|
let privateKey;
|
|
15138
15246
|
let ephemeralPublicB64 = null;
|
|
15139
15247
|
if (options.key) {
|
|
15140
|
-
privateKey = createPrivateKey({ key:
|
|
15248
|
+
privateKey = createPrivateKey({ key: readFileSync15(resolvePath8(ctx.cwd, options.key), "utf8"), format: "pem" });
|
|
15141
15249
|
} else {
|
|
15142
15250
|
const conventional = join6(homedir3(), ".openwop-keys", `${keyId}.private.pem`);
|
|
15143
15251
|
if (existsSync6(conventional)) {
|
|
15144
|
-
privateKey = createPrivateKey({ key:
|
|
15252
|
+
privateKey = createPrivateKey({ key: readFileSync15(conventional, "utf8"), format: "pem" });
|
|
15145
15253
|
} else {
|
|
15146
15254
|
const kp = generateKeyPairSync("ed25519");
|
|
15147
15255
|
privateKey = kp.privateKey;
|
|
@@ -15220,7 +15328,7 @@ async function runPacksYank(ctx, argv) {
|
|
|
15220
15328
|
throw new CliError(`Version manifest not found: ${manifestPath}
|
|
15221
15329
|
(packs yank edits a local registry checkout; the published change lands via PR.)`);
|
|
15222
15330
|
}
|
|
15223
|
-
const manifest = JSON.parse(
|
|
15331
|
+
const manifest = JSON.parse(readFileSync15(manifestPath, "utf8"));
|
|
15224
15332
|
const newValue = !options.undo;
|
|
15225
15333
|
if (Boolean(manifest.yanked) === newValue) {
|
|
15226
15334
|
writeLine(ctx.io.stdout, `${name}@${version} is already ${newValue ? "yanked" : "un-yanked"}; no change.`);
|
|
@@ -15277,11 +15385,11 @@ function walkPackDir(packDir) {
|
|
|
15277
15385
|
const st = statSync(full);
|
|
15278
15386
|
if (st.isFile()) {
|
|
15279
15387
|
if (!ALLOWED_TOPS.has(entry)) continue;
|
|
15280
|
-
entries2.push({ name: entry, content:
|
|
15388
|
+
entries2.push({ name: entry, content: readFileSync15(full) });
|
|
15281
15389
|
} else if (st.isDirectory() && ALLOWED_DIRS.has(entry)) {
|
|
15282
15390
|
for (const f of readdirSync(full).sort()) {
|
|
15283
15391
|
if (!f.endsWith(".json") && !f.endsWith(".pem") && !f.endsWith(".sig")) continue;
|
|
15284
|
-
entries2.push({ name: `${entry}/${f}`, content:
|
|
15392
|
+
entries2.push({ name: `${entry}/${f}`, content: readFileSync15(join6(full, f)) });
|
|
15285
15393
|
}
|
|
15286
15394
|
}
|
|
15287
15395
|
}
|
|
@@ -15450,6 +15558,8 @@ async function protocolChecks(ctx) {
|
|
|
15450
15558
|
} else if (floor.required !== void 0) {
|
|
15451
15559
|
rows.push(warn("min client", `host minClientVersion ${floor.required} is not a <major>.<minor> version`));
|
|
15452
15560
|
}
|
|
15561
|
+
const stream = streamOrigin(ctx);
|
|
15562
|
+
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
15563
|
return rows;
|
|
15454
15564
|
}
|
|
15455
15565
|
|
|
@@ -15841,7 +15951,7 @@ async function runGovernanceEgress(ctx, argv) {
|
|
|
15841
15951
|
|
|
15842
15952
|
// src/cli/demo.ts
|
|
15843
15953
|
import { spawn as spawn3, spawnSync as spawnSync5 } from "node:child_process";
|
|
15844
|
-
import { existsSync as existsSync8, readFileSync as
|
|
15954
|
+
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
15955
|
import { join as join7, dirname as dirname7, relative as relative2 } from "node:path";
|
|
15846
15956
|
var DEMO_HELP = `Usage:
|
|
15847
15957
|
openwop demo status [--json]
|
|
@@ -16208,7 +16318,7 @@ async function runDemoLogs(ctx, argv) {
|
|
|
16208
16318
|
return 2;
|
|
16209
16319
|
}
|
|
16210
16320
|
const lineCount = Number(options.lines ?? 50);
|
|
16211
|
-
const existing =
|
|
16321
|
+
const existing = readFileSync16(logPath, "utf8");
|
|
16212
16322
|
const lines = existing.split("\n");
|
|
16213
16323
|
const tail = lines.slice(Math.max(0, lines.length - lineCount - 1));
|
|
16214
16324
|
write(ctx.io.stdout, tail.join("\n"));
|
|
@@ -18873,7 +18983,7 @@ async function runWfList(ctx, argv) {
|
|
|
18873
18983
|
writeLine(ctx.io.stdout, formatTable(rows, ["workforceId", "name", "businessFunction", "status", "autonomy", "agents"]));
|
|
18874
18984
|
return 0;
|
|
18875
18985
|
}
|
|
18876
|
-
async function runWfReadOne(ctx, argv, suffix,
|
|
18986
|
+
async function runWfReadOne(ctx, argv, suffix, render2) {
|
|
18877
18987
|
const { options, positionals } = parseOptions(argv, { bool: ["--help"] });
|
|
18878
18988
|
if (options.help || positionals.length !== 1) {
|
|
18879
18989
|
write(ctx.io.stdout, WORKFORCES_HELP);
|
|
@@ -18890,7 +19000,7 @@ async function runWfReadOne(ctx, argv, suffix, render3) {
|
|
|
18890
19000
|
writeJson(ctx.io.stdout, res.body);
|
|
18891
19001
|
return 0;
|
|
18892
19002
|
}
|
|
18893
|
-
|
|
19003
|
+
render2(ctx, res.body ?? {}, positionals[0]);
|
|
18894
19004
|
return 0;
|
|
18895
19005
|
}
|
|
18896
19006
|
function renderWorkforce(ctx, w, id) {
|
|
@@ -20164,7 +20274,7 @@ function truncate4(s, n) {
|
|
|
20164
20274
|
}
|
|
20165
20275
|
|
|
20166
20276
|
// src/cli/portability.ts
|
|
20167
|
-
import { readFileSync as
|
|
20277
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "node:fs";
|
|
20168
20278
|
import { resolve as resolve4 } from "node:path";
|
|
20169
20279
|
var EXPORT_HELP = `Usage:
|
|
20170
20280
|
openwop export [--kinds <kind>]... [--out <file>] [--json]
|
|
@@ -20302,7 +20412,7 @@ async function runImport(ctx, argv) {
|
|
|
20302
20412
|
}
|
|
20303
20413
|
let bundle;
|
|
20304
20414
|
try {
|
|
20305
|
-
const raw =
|
|
20415
|
+
const raw = readFileSync17(resolve4(ctx.cwd, positionals[0]), "utf8");
|
|
20306
20416
|
bundle = JSON.parse(raw);
|
|
20307
20417
|
} catch (err) {
|
|
20308
20418
|
throw new CliError(`import: could not read bundle file ${positionals[0]} \u2014 ${err instanceof Error ? err.message : "invalid JSON"}`, 2);
|
|
@@ -21074,7 +21184,7 @@ Run \`openwop knowledge-sync --help\` for usage.`);
|
|
|
21074
21184
|
}
|
|
21075
21185
|
|
|
21076
21186
|
// src/cli/entities.ts
|
|
21077
|
-
import { readFileSync as
|
|
21187
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "node:fs";
|
|
21078
21188
|
import { resolve as resolvePath9 } from "node:path";
|
|
21079
21189
|
var BASE13 = "/v1/host/openwop-app/entities";
|
|
21080
21190
|
var PUBLIC_BASE2 = "/v1/host/openwop-app/public-entities";
|
|
@@ -21310,7 +21420,7 @@ async function types(ctx, sub, pos, o, project4, q) {
|
|
|
21310
21420
|
if (!name || !o.file) return usage5(ctx, "types import <type> --file <file.ndjson|file.json>");
|
|
21311
21421
|
let raw;
|
|
21312
21422
|
try {
|
|
21313
|
-
raw =
|
|
21423
|
+
raw = readFileSync18(resolvePath9(ctx.cwd, String(o.file)), "utf8");
|
|
21314
21424
|
} catch (err) {
|
|
21315
21425
|
throw new CliError(`Cannot read --file ${String(o.file)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
21316
21426
|
}
|
|
@@ -22744,8 +22854,7 @@ async function followEvents(ctx, requestedPath, options) {
|
|
|
22744
22854
|
const max = options.max !== void 0 ? Number(options.max) : Infinity;
|
|
22745
22855
|
const timeoutMs = options.timeout !== void 0 ? Number(options.timeout) * 1e3 : Infinity;
|
|
22746
22856
|
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}/`);
|
|
22857
|
+
const { url, headers } = await resolveStreamRequest(ctx, requestedPath, {});
|
|
22749
22858
|
const controller = new AbortController();
|
|
22750
22859
|
const timer = Number.isFinite(timeoutMs) ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
|
|
22751
22860
|
let seen = 0;
|
|
@@ -26552,7 +26661,7 @@ function renderKnowledge(ctx, agentId, k) {
|
|
|
26552
26661
|
}
|
|
26553
26662
|
|
|
26554
26663
|
// src/cli/compatEndpoints.ts
|
|
26555
|
-
import { readFileSync as
|
|
26664
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
26556
26665
|
var BASE32 = "/v1/host/openwop-app/compat-endpoints";
|
|
26557
26666
|
var COMPAT_ENDPOINTS_HELP = `Usage:
|
|
26558
26667
|
openwop compat-endpoints list --org <orgId> [--json]
|
|
@@ -26666,7 +26775,7 @@ function readApiKey(ctx, options) {
|
|
|
26666
26775
|
if (options.apiKeyFile && options.apiKeyEnv) throw new CliError("Pass only one of --api-key-file / --api-key-env.", 2);
|
|
26667
26776
|
if (options.apiKeyFile) {
|
|
26668
26777
|
try {
|
|
26669
|
-
return
|
|
26778
|
+
return readFileSync19(String(options.apiKeyFile), "utf8").trim() || void 0;
|
|
26670
26779
|
} catch (err) {
|
|
26671
26780
|
throw new CliError(`Could not read ${options.apiKeyFile}: ${err instanceof Error ? err.message : String(err)}`, 2);
|
|
26672
26781
|
}
|
|
@@ -27868,7 +27977,7 @@ Run \`openwop scheduled-chats --help\` for usage.`, 2);
|
|
|
27868
27977
|
}
|
|
27869
27978
|
|
|
27870
27979
|
// src/cli/voice.ts
|
|
27871
|
-
import { readFileSync as
|
|
27980
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync19 } from "node:fs";
|
|
27872
27981
|
var SESSION = "/v1/host/openwop-app/voice/session";
|
|
27873
27982
|
var REALTIME = "/v1/host/openwop-app/voice/realtime";
|
|
27874
27983
|
var AUDIO_CHUNK_BYTES = 192 * 1024;
|
|
@@ -27973,7 +28082,7 @@ Run \`openwop voice --help\` for usage.`, 2);
|
|
|
27973
28082
|
if (options.file) {
|
|
27974
28083
|
let bytes;
|
|
27975
28084
|
try {
|
|
27976
|
-
bytes =
|
|
28085
|
+
bytes = readFileSync20(String(options.file));
|
|
27977
28086
|
} catch (err) {
|
|
27978
28087
|
throw new CliError(`--file: cannot read ${options.file} (${err instanceof Error ? err.message : String(err)})`, 2);
|
|
27979
28088
|
}
|
|
@@ -28115,7 +28224,7 @@ async function runRealtime(ctx, argv) {
|
|
|
28115
28224
|
if (!options.sdpFile) throw new CliError("voice realtime connect requires --sdp-file <offer.sdp>.", 2);
|
|
28116
28225
|
let sdp;
|
|
28117
28226
|
try {
|
|
28118
|
-
sdp =
|
|
28227
|
+
sdp = readFileSync20(String(options.sdpFile), "utf8");
|
|
28119
28228
|
} catch (err) {
|
|
28120
28229
|
throw new CliError(`--sdp-file: cannot read ${options.sdpFile} (${err instanceof Error ? err.message : String(err)})`, 2);
|
|
28121
28230
|
}
|
|
@@ -28170,10 +28279,10 @@ async function runRealtimeConfig(ctx, argv) {
|
|
|
28170
28279
|
write(ctx.io.stdout, VOICE_HELP);
|
|
28171
28280
|
return 0;
|
|
28172
28281
|
}
|
|
28173
|
-
const
|
|
28282
|
+
const render2 = (b) => writeFields(ctx, [["provider", b?.provider], ["credentialRef", b?.credentialRef], ["model", b?.model]]);
|
|
28174
28283
|
if (sub === "get") {
|
|
28175
28284
|
const res = await requestJson(ctx, `${REALTIME}/config`);
|
|
28176
|
-
return emit(ctx, res.body, () =>
|
|
28285
|
+
return emit(ctx, res.body, () => render2(res.body));
|
|
28177
28286
|
}
|
|
28178
28287
|
if (sub === "set") {
|
|
28179
28288
|
if (!options.provider && options.credentialRef === void 0 && options.model === void 0) {
|
|
@@ -28188,7 +28297,7 @@ async function runRealtimeConfig(ctx, argv) {
|
|
|
28188
28297
|
const res = await requestJson(ctx, `${REALTIME}/config`, { method: "PUT", body });
|
|
28189
28298
|
return emit(ctx, res.body, () => {
|
|
28190
28299
|
writeLine(ctx.io.stdout, "Updated realtime voice config.");
|
|
28191
|
-
|
|
28300
|
+
render2(res.body);
|
|
28192
28301
|
});
|
|
28193
28302
|
}
|
|
28194
28303
|
throw new CliError(`Unknown voice realtime config command: ${sub}
|
|
@@ -28196,7 +28305,7 @@ Run \`openwop voice --help\` for usage.`, 2);
|
|
|
28196
28305
|
}
|
|
28197
28306
|
|
|
28198
28307
|
// src/cli/ai.ts
|
|
28199
|
-
import { readFileSync as
|
|
28308
|
+
import { readFileSync as readFileSync21 } from "node:fs";
|
|
28200
28309
|
var AI = "/v1/host/openwop-app/ai";
|
|
28201
28310
|
var AI_HELP = `Usage:
|
|
28202
28311
|
openwop ai call (--message <text> | --messages-json <json>) [--provider p] [--model m] [--json]
|
|
@@ -28294,7 +28403,7 @@ async function runAi(ctx, argv) {
|
|
|
28294
28403
|
let value;
|
|
28295
28404
|
if (options.valueFile) {
|
|
28296
28405
|
try {
|
|
28297
|
-
value =
|
|
28406
|
+
value = readFileSync21(String(options.valueFile), "utf8").trim();
|
|
28298
28407
|
} catch (err) {
|
|
28299
28408
|
throw new CliError(`--value-file: cannot read ${options.valueFile} (${err instanceof Error ? err.message : String(err)})`, 2);
|
|
28300
28409
|
}
|
|
@@ -29501,6 +29610,7 @@ async function runCli(argv, options = {}) {
|
|
|
29501
29610
|
parsed.globals.baseUrl ?? env.OPENWOP_BASE_URL ?? savedConfig?.host?.baseUrl ?? DEFAULT_BASE_URL
|
|
29502
29611
|
),
|
|
29503
29612
|
apiKey: parsed.globals.apiKey ?? env.OPENWOP_API_KEY ?? savedConfig?.host?.apiKey,
|
|
29613
|
+
streamBaseUrl: explicitStreamBase(parsed.globals.streamBaseUrl ?? env.OPENWOP_STREAM_BASE_URL ?? savedConfig?.host?.streamBaseUrl),
|
|
29504
29614
|
json: parsed.globals.json,
|
|
29505
29615
|
quiet: parsed.globals.quiet,
|
|
29506
29616
|
verbose: parsed.globals.verbose,
|
|
@@ -30061,6 +30171,9 @@ Usage:
|
|
|
30061
30171
|
|
|
30062
30172
|
Global options:
|
|
30063
30173
|
--base-url <url> Host base URL (default: --base-url > OPENWOP_BASE_URL > saved config > http://localhost:8080)
|
|
30174
|
+
--stream-base-url <url>
|
|
30175
|
+
Where to read event streams (default: OPENWOP_STREAM_BASE_URL >
|
|
30176
|
+
saved config > the host's advertised https streamBase > --base-url)
|
|
30064
30177
|
--api-key <key> Bearer API key (default: OPENWOP_API_KEY > saved config > sample-token for localhost)
|
|
30065
30178
|
--profile <name> Use a named config profile (~/.openwop-<name>); also OPENWOP_PROFILE
|
|
30066
30179
|
--json Print machine-readable JSON where supported
|
|
@@ -30353,6 +30466,7 @@ export {
|
|
|
30353
30466
|
describeHttpError,
|
|
30354
30467
|
detectChannelAvailability,
|
|
30355
30468
|
errorEnvelope,
|
|
30469
|
+
explicitStreamBase,
|
|
30356
30470
|
extractAssistantText,
|
|
30357
30471
|
extractGlobalOptions,
|
|
30358
30472
|
findRepoRoot,
|
|
@@ -30361,6 +30475,7 @@ export {
|
|
|
30361
30475
|
hostRootFor,
|
|
30362
30476
|
hostRootsFrom,
|
|
30363
30477
|
idempotencyHeaders,
|
|
30478
|
+
isNoCredentialChallenge,
|
|
30364
30479
|
isTerminalRunEvent,
|
|
30365
30480
|
openwopHomeDir,
|
|
30366
30481
|
parseDiscordMessage,
|
|
@@ -30374,9 +30489,12 @@ export {
|
|
|
30374
30489
|
readConfigSafe,
|
|
30375
30490
|
readDaemonRecord,
|
|
30376
30491
|
renderEvent,
|
|
30492
|
+
requestNormativeOrHost,
|
|
30493
|
+
resolveStreamRequest,
|
|
30377
30494
|
runCli,
|
|
30378
30495
|
saveConfig,
|
|
30379
30496
|
startInboundReceive,
|
|
30497
|
+
streamBaseFrom,
|
|
30380
30498
|
streamRunEvents,
|
|
30381
30499
|
submitTurn,
|
|
30382
30500
|
summarizeCapabilities
|