@iicp/client 0.7.85 → 0.7.87
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 +31 -2
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +136 -2
- package/dist/cli.js.map +1 -1
- package/dist/node.d.ts +1 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +3 -0
- package/dist/node.js.map +1 -1
- package/dist/policy_manifest.d.ts +4 -0
- package/dist/policy_manifest.d.ts.map +1 -0
- package/dist/policy_manifest.js +56 -0
- package/dist/policy_manifest.js.map +1 -0
- package/dist/relay_session.js +3 -3
- package/dist/relay_session.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ What good looks like:
|
|
|
39
39
|
```bash
|
|
40
40
|
iicp-node --help # shows query, serve, proxy, mcp-gateway, credits, ...
|
|
41
41
|
which iicp-node # points to your Node/npm environment
|
|
42
|
-
iicp-node --version # prints iicp-node 0.7.
|
|
42
|
+
iicp-node --version # prints iicp-node 0.7.87 or newer
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
The query command contacts the public directory, discovers a matching live node,
|
|
@@ -461,7 +461,10 @@ const node = new IicpNode({
|
|
|
461
461
|
});
|
|
462
462
|
```
|
|
463
463
|
|
|
464
|
-
|
|
464
|
+
Relay workers request short-lived directory-signed bind tickets when they have a saved node
|
|
465
|
+
token. Relay operators can enforce them with `IICP_RELAY_REQUIRE_BIND_TICKET=1` and the
|
|
466
|
+
directory's Ed25519 verification key in `IICP_RELAY_BIND_TICKET_PUBLIC_KEY`. Keep strict mode
|
|
467
|
+
enabled on public relays; unsigned compatibility mode is intended only for staged migration.
|
|
465
468
|
|
|
466
469
|
### Opt-out / override
|
|
467
470
|
|
|
@@ -477,6 +480,22 @@ IICP_AUTO_UPDATE_INTERVAL_S=3600 # update cadence in seconds; minimum 30
|
|
|
477
480
|
IICP_RELAY_WORKER_ENDPOINT=host:9485 # specific relay instead of auto-elect
|
|
478
481
|
```
|
|
479
482
|
|
|
483
|
+
### Publish a signed node policy
|
|
484
|
+
|
|
485
|
+
Operators can describe public handling rules in a local JSON file and have the client sign it
|
|
486
|
+
with their existing operator identity before registration:
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
iicp-node serve --node my-node --policy-manifest ~/.iicp/node-policy.json
|
|
490
|
+
# or: IICP_POLICY_MANIFEST_FILE=~/.iicp/node-policy.json
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
The source file stays local. The registration contains the public policy document, its public
|
|
494
|
+
operator key, timestamps, and detached Ed25519 signature—never the operator secret. The same
|
|
495
|
+
signed document is reused during recovery re-registration, so policy does not disappear when
|
|
496
|
+
a tunnel rotates. A signed declaration is tamper-evident operator evidence, not a legal or
|
|
497
|
+
privacy certification.
|
|
498
|
+
|
|
480
499
|
---
|
|
481
500
|
|
|
482
501
|
## Operator identity
|
|
@@ -501,6 +520,16 @@ and the identity, with its founder ordinal, is gone.
|
|
|
501
520
|
|
|
502
521
|
Full guide: **[iicp.network/docs/operator-identity](https://iicp.network/docs/operator-identity)**
|
|
503
522
|
|
|
523
|
+
### Operator data rights
|
|
524
|
+
|
|
525
|
+
You can request a portable, redacted record of the operator metadata held by a compatible directory without uploading your private identity key:
|
|
526
|
+
|
|
527
|
+
```bash
|
|
528
|
+
iicp-node operator dsr export --output ~/iicp-operator-export.json
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
The client obtains a short-lived challenge and signs it locally. The receipt excludes the private key, node tokens, prompt content, and contact details; it is saved owner-only on Unix. `restrict` and `anonymize` are explicit, confirmed requests and do not erase retention records that a directory must keep for security, fraud prevention, or legal obligations. See the [operator rights guide](https://iicp.network/operator/rights).
|
|
532
|
+
|
|
504
533
|
---
|
|
505
534
|
|
|
506
535
|
## SDK conformance
|
package/dist/cli.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface ProviderAutoUpdateOptions {
|
|
|
17
17
|
export declare function startProviderAutoUpdate(options?: ProviderAutoUpdateOptions): () => void;
|
|
18
18
|
export interface ServeOpts {
|
|
19
19
|
backendUrl: string;
|
|
20
|
+
policyManifest?: string;
|
|
20
21
|
backendType: string;
|
|
21
22
|
/** #5 — Bearer key for an auth-requiring OpenAI-compat backend (LM Studio, hosted). Empty = none. */
|
|
22
23
|
backendApiKey: string;
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAiDA,OAAO,EAeL,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAIvB,KAAK,aAAa,GAAG,cAAc,cAAc,CAAC,CAAC;AAEnD,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3C,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,yBAA8B,GAAG,MAAM,IAAI,CA+C3F;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,qGAAqG;IACrG,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,OAAO,CAAC;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iGAAiG;IACjG,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAoBD,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AAE/D,wBAAgB,uBAAuB,IAAI,gBAAgB,CAM1D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,EAAE,UAAU,EAAE,OAAO,GAAG,kBAAkB,CAGpG;AAED,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAKrF;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -164,6 +164,7 @@ function printHelp() {
|
|
|
164
164
|
` service Generate/install OS supervisor units for unattended node serving\n` +
|
|
165
165
|
` update Check whether a newer @iicp/client release is available\n` +
|
|
166
166
|
` operator rename <name> Change your public display_name (signed by your operator key)\n` +
|
|
167
|
+
` operator dsr <action> Export, restrict, or anonymize your directory records\n` +
|
|
167
168
|
` operator encrypt Password-encrypt the operator secret at rest ($IICP_OPERATOR_PASSPHRASE)\n` +
|
|
168
169
|
` operator decrypt Remove at-rest encryption of the operator secret\n\n` +
|
|
169
170
|
`Run an IICP provider node backed by an OpenAI-compatible server.\n\n` +
|
|
@@ -174,6 +175,7 @@ function printHelp() {
|
|
|
174
175
|
` --backend-url URL IICP_BACKEND_URL — Ollama / vLLM / LM Studio (default http://localhost:11434; anthropic → https://api.anthropic.com)\n` +
|
|
175
176
|
` --backend-type TYPE IICP_BACKEND_TYPE — openai_compat | vllm | llamacpp | anthropic (default openai_compat)\n` +
|
|
176
177
|
` --backend-api-key KEY IICP_BACKEND_API_KEY — Bearer key for an auth'd backend (LM Studio, hosted); anthropic uses it as x-api-key\n` +
|
|
178
|
+
` --policy-manifest FILE IICP_POLICY_MANIFEST_FILE — sign and advertise a public node-policy JSON document\n` +
|
|
177
179
|
` --public-endpoint URL IICP_PUBLIC_ENDPOINT — externally reachable URL of this node\n` +
|
|
178
180
|
` --directory-url URL IICP_DIRECTORY_URL (default https://iicp.network/api)\n` +
|
|
179
181
|
` --region REGION IICP_REGION (e.g. us-east; unknown if unset)\n` +
|
|
@@ -196,8 +198,8 @@ function printHelp() {
|
|
|
196
198
|
` --relay-worker-endpoint H IICP_RELAY_WORKER_ENDPOINT — <host>:<port> of a relay node (R2 last-resort)\n` +
|
|
197
199
|
` --relay-capable IICP_RELAY_CAPABLE — advertise as relay server for CGNAT/tier-4 operators\n` +
|
|
198
200
|
` --relay-accept-port N IICP_RELAY_ACCEPT_PORT — TCP port for relay accept server (default 9485).\n` +
|
|
199
|
-
`
|
|
200
|
-
`
|
|
201
|
+
` Public relays should set IICP_RELAY_REQUIRE_BIND_TICKET=1 and\n` +
|
|
202
|
+
` IICP_RELAY_BIND_TICKET_PUBLIC_KEY for directory-signed one-use binds.\n` +
|
|
201
203
|
` --log-dir DIR IICP_LOG_DIR — directory for persistent log files (<node_id>.log + events.jsonl)\n` +
|
|
202
204
|
` --with-proxy IICP_WITH_PROXY — also run the loopback compat proxy (127.0.0.1:9483) in-process\n\n` +
|
|
203
205
|
`query optional:\n` +
|
|
@@ -240,6 +242,7 @@ function printServeHelp() {
|
|
|
240
242
|
` --backend-url URL IICP_BACKEND_URL — Ollama / vLLM / LM Studio (default http://localhost:11434; anthropic → https://api.anthropic.com)\n` +
|
|
241
243
|
` --backend-type TYPE IICP_BACKEND_TYPE — openai_compat | vllm | llamacpp | anthropic\n` +
|
|
242
244
|
` --backend-api-key KEY IICP_BACKEND_API_KEY — Bearer key for auth'd backends\n` +
|
|
245
|
+
` --policy-manifest FILE IICP_POLICY_MANIFEST_FILE — sign and advertise a public node-policy JSON document\n` +
|
|
243
246
|
` --public-endpoint URL IICP_PUBLIC_ENDPOINT — externally reachable URL of this node\n` +
|
|
244
247
|
` --directory-url URL IICP_DIRECTORY_URL (default https://iicp.network/api)\n` +
|
|
245
248
|
` --region REGION IICP_REGION (e.g. eu-central)\n` +
|
|
@@ -935,6 +938,7 @@ async function runServe(opts) {
|
|
|
935
938
|
let _opDisplayName;
|
|
936
939
|
let _opCreatedAt;
|
|
937
940
|
let _opIntegrityHash;
|
|
941
|
+
let policyManifest;
|
|
938
942
|
const _identityNotice = (0, identity_js_1.noIdentityNotice)(_op);
|
|
939
943
|
if (_identityNotice !== null) {
|
|
940
944
|
// #503 — anonymous registration accrues no founder/recognition standing;
|
|
@@ -946,7 +950,13 @@ async function runServe(opts) {
|
|
|
946
950
|
_opDisplayName = _op.display_name || undefined;
|
|
947
951
|
_opCreatedAt = _op.created_at;
|
|
948
952
|
_opIntegrityHash = _op.operator_integrity_hash || undefined;
|
|
953
|
+
if (opts.policyManifest) {
|
|
954
|
+
const { loadAndSignPolicyManifest } = await import("./policy_manifest.js");
|
|
955
|
+
policyManifest = loadAndSignPolicyManifest(opts.policyManifest, _op);
|
|
956
|
+
}
|
|
949
957
|
}
|
|
958
|
+
if (opts.policyManifest && !policyManifest)
|
|
959
|
+
throw new Error("--policy-manifest requires a key-backed operator identity");
|
|
950
960
|
const node = new node_js_1.IicpNode({
|
|
951
961
|
nodeId,
|
|
952
962
|
endpoint: publicEndpoint,
|
|
@@ -964,6 +974,7 @@ async function runServe(opts) {
|
|
|
964
974
|
operatorDisplayName: _opDisplayName,
|
|
965
975
|
operatorCreatedAt: _opCreatedAt,
|
|
966
976
|
operatorIntegrityHash: _opIntegrityHash,
|
|
977
|
+
policyManifest,
|
|
967
978
|
// #494 — wire backend URL for live health_models probing in heartbeats.
|
|
968
979
|
backendUrl: opts.backendUrl || undefined,
|
|
969
980
|
backendApiKey: opts.backendApiKey || undefined,
|
|
@@ -1974,12 +1985,131 @@ function printOperatorHelp() {
|
|
|
1974
1985
|
`Manage your operator identity.\n\n` +
|
|
1975
1986
|
`subcommands:\n` +
|
|
1976
1987
|
` rename <name> Change your public display_name (signed by your operator key)\n` +
|
|
1988
|
+
` dsr <action> Export, restrict, or anonymize your directory records\n` +
|
|
1977
1989
|
` encrypt Password-encrypt the operator secret at rest ($IICP_OPERATOR_PASSPHRASE)\n` +
|
|
1978
1990
|
` decrypt Remove at-rest encryption of the operator secret\n\n` +
|
|
1979
1991
|
`operator rename options:\n` +
|
|
1980
1992
|
` --directory-url URL IICP directory base URL (defaults to env / iicp.network)\n` +
|
|
1981
1993
|
` -h, --help Show this help and exit\n`);
|
|
1982
1994
|
}
|
|
1995
|
+
async function runOperatorDsr(argv) {
|
|
1996
|
+
const { values, positionals } = safeParseArgs({
|
|
1997
|
+
args: argv,
|
|
1998
|
+
options: {
|
|
1999
|
+
"directory-url": { type: "string" },
|
|
2000
|
+
"tracking-id": { type: "string" },
|
|
2001
|
+
output: { type: "string" },
|
|
2002
|
+
yes: { type: "boolean" },
|
|
2003
|
+
help: { type: "boolean", short: "h" },
|
|
2004
|
+
},
|
|
2005
|
+
allowPositionals: true,
|
|
2006
|
+
});
|
|
2007
|
+
if (values.help) {
|
|
2008
|
+
process.stdout.write("usage: iicp-node operator dsr <export|restrict|anonymize> [options]\n\n" +
|
|
2009
|
+
"The local operator key signs a one-use directory challenge. It never enters the browser or request body.\n\n" +
|
|
2010
|
+
" --output FILE Required for export; creates a new mode-0600 local file\n" +
|
|
2011
|
+
" --yes Confirm restrict or anonymize after the retained-record notice\n" +
|
|
2012
|
+
" --tracking-id ID Optional local reference\n" +
|
|
2013
|
+
" --directory-url URL IICP directory base URL\n");
|
|
2014
|
+
return 0;
|
|
2015
|
+
}
|
|
2016
|
+
const action = positionals[0];
|
|
2017
|
+
if (action !== "export" && action !== "restrict" && action !== "anonymize") {
|
|
2018
|
+
process.stderr.write("ERROR: usage: iicp-node operator dsr <export|restrict|anonymize> [options]\n");
|
|
2019
|
+
return 2;
|
|
2020
|
+
}
|
|
2021
|
+
const outputPath = values.output;
|
|
2022
|
+
if (action === "export" && !outputPath) {
|
|
2023
|
+
process.stderr.write("ERROR: export requires --output <new-file>; it is not printed to the terminal.\n");
|
|
2024
|
+
return 2;
|
|
2025
|
+
}
|
|
2026
|
+
if (outputPath && fs.existsSync(outputPath)) {
|
|
2027
|
+
process.stderr.write("ERROR: --output already exists; choose a new path to avoid replacing an export.\n");
|
|
2028
|
+
return 2;
|
|
2029
|
+
}
|
|
2030
|
+
if (action !== "export") {
|
|
2031
|
+
process.stderr.write("This action restricts or anonymizes directory records. Minimal signed ledger, security and accounting records may be retained and are explained in the receipt.\n");
|
|
2032
|
+
if (!values.yes) {
|
|
2033
|
+
process.stderr.write("Re-run with --yes to confirm.\n");
|
|
2034
|
+
return 2;
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
const op = (0, identity_js_1.loadOperator)();
|
|
2038
|
+
if (!op) {
|
|
2039
|
+
process.stderr.write("ERROR: no operator identity — run `iicp-node init` first.\n");
|
|
2040
|
+
return 1;
|
|
2041
|
+
}
|
|
2042
|
+
if (!(0, identity_js_1.operatorIsKeyBacked)(op)) {
|
|
2043
|
+
process.stderr.write("ERROR: this legacy operator identity cannot sign a rights request; regenerate a key-backed identity.\n");
|
|
2044
|
+
return 1;
|
|
2045
|
+
}
|
|
2046
|
+
const directoryUrl = values["directory-url"] ?? process.env["IICP_DIRECTORY_URL"] ?? "https://iicp.network/api";
|
|
2047
|
+
const base = `${directoryUrl.replace(/\/+$/, "")}/v1/operator`;
|
|
2048
|
+
let response;
|
|
2049
|
+
try {
|
|
2050
|
+
const challenge = await fetch(`${base}/challenge`, {
|
|
2051
|
+
method: "POST",
|
|
2052
|
+
headers: { "Content-Type": "application/json" },
|
|
2053
|
+
body: JSON.stringify({ operator_pub: op.operator_id }),
|
|
2054
|
+
signal: AbortSignal.timeout(15000),
|
|
2055
|
+
});
|
|
2056
|
+
const challengeBody = (await challenge.json());
|
|
2057
|
+
if (!challenge.ok || typeof challengeBody.nonce !== "string") {
|
|
2058
|
+
throw new Error(challengeBody.error?.message ?? "directory challenge was incomplete");
|
|
2059
|
+
}
|
|
2060
|
+
const payload = {
|
|
2061
|
+
operator_pub: op.operator_id,
|
|
2062
|
+
nonce: challengeBody.nonce,
|
|
2063
|
+
ts: Math.floor(Date.now() / 1000),
|
|
2064
|
+
tracking_id: values["tracking-id"] ?? `dsr-${(0, node_crypto_1.randomUUID)()}`,
|
|
2065
|
+
};
|
|
2066
|
+
if (action !== "export")
|
|
2067
|
+
payload.confirm = true;
|
|
2068
|
+
payload.sig = (0, delegation_js_1.signOperatorSelfService)((0, identity_js_1.operatorSigningKey)(op), `dsr_${action}`, payload);
|
|
2069
|
+
response = await fetch(`${base}/dsr/${action}`, {
|
|
2070
|
+
method: "POST",
|
|
2071
|
+
headers: { "Content-Type": "application/json" },
|
|
2072
|
+
body: JSON.stringify(payload),
|
|
2073
|
+
signal: AbortSignal.timeout(15000),
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
2076
|
+
catch (err) {
|
|
2077
|
+
process.stderr.write(`ERROR: rights request failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
2078
|
+
return 1;
|
|
2079
|
+
}
|
|
2080
|
+
let body;
|
|
2081
|
+
try {
|
|
2082
|
+
body = (await response.json());
|
|
2083
|
+
}
|
|
2084
|
+
catch {
|
|
2085
|
+
body = {};
|
|
2086
|
+
}
|
|
2087
|
+
if (!response.ok) {
|
|
2088
|
+
const err = body.error;
|
|
2089
|
+
process.stderr.write(`ERROR: directory rejected rights request: ${err?.message ?? response.status}\n`);
|
|
2090
|
+
return 1;
|
|
2091
|
+
}
|
|
2092
|
+
if (action === "export" && outputPath) {
|
|
2093
|
+
try {
|
|
2094
|
+
fs.writeFileSync(outputPath, `${JSON.stringify(body, null, 2)}\n`, { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
2095
|
+
fs.chmodSync(outputPath, 0o600);
|
|
2096
|
+
}
|
|
2097
|
+
catch (err) {
|
|
2098
|
+
process.stderr.write(`ERROR: unable to save export safely: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
2099
|
+
return 1;
|
|
2100
|
+
}
|
|
2101
|
+
process.stdout.write(`Saved redacted directory export to ${outputPath} (mode 0600).\n`);
|
|
2102
|
+
}
|
|
2103
|
+
else {
|
|
2104
|
+
process.stdout.write(`Directory ${action} request completed.\n`);
|
|
2105
|
+
}
|
|
2106
|
+
if (typeof body.tracking_id === "string")
|
|
2107
|
+
process.stdout.write(`Receipt reference: ${body.tracking_id}\n`);
|
|
2108
|
+
const retention = body.retention_notice ?? body.retention_reason;
|
|
2109
|
+
if (typeof retention === "string")
|
|
2110
|
+
process.stdout.write(`Retained records: ${retention}\n`);
|
|
2111
|
+
return 0;
|
|
2112
|
+
}
|
|
1983
2113
|
function printOperatorEncryptHelp() {
|
|
1984
2114
|
process.stdout.write(`usage: iicp-node operator encrypt\n\n` +
|
|
1985
2115
|
`Password-encrypt the operator secret at rest. The command prompts for a passphrase\n` +
|
|
@@ -2020,6 +2150,8 @@ async function runOperator(argv) {
|
|
|
2020
2150
|
throw new CliError(`unknown operator decrypt option '${rest[0]}'`);
|
|
2021
2151
|
return runOperatorDecrypt();
|
|
2022
2152
|
}
|
|
2153
|
+
if (sub === "dsr")
|
|
2154
|
+
return runOperatorDsr(argv.slice(1));
|
|
2023
2155
|
if (sub !== "rename") {
|
|
2024
2156
|
process.stderr.write(`unknown operator subcommand: ${sub}\n`);
|
|
2025
2157
|
printOperatorHelp();
|
|
@@ -2500,6 +2632,7 @@ async function dispatch(argv) {
|
|
|
2500
2632
|
options: {
|
|
2501
2633
|
node: { type: "string" },
|
|
2502
2634
|
"backend-url": { type: "string" },
|
|
2635
|
+
"policy-manifest": { type: "string" },
|
|
2503
2636
|
"backend-type": { type: "string" },
|
|
2504
2637
|
"backend-api-key": { type: "string" },
|
|
2505
2638
|
model: { type: "string" },
|
|
@@ -2536,6 +2669,7 @@ async function dispatch(argv) {
|
|
|
2536
2669
|
const opts = {
|
|
2537
2670
|
node: values.node ?? envOr("IICP_NODE_NAME") ?? "",
|
|
2538
2671
|
backendUrl: values["backend-url"] ?? envOr("IICP_BACKEND_URL") ?? "",
|
|
2672
|
+
policyManifest: values["policy-manifest"] ?? envOr("IICP_POLICY_MANIFEST_FILE") ?? "",
|
|
2539
2673
|
backendType: values["backend-type"] ??
|
|
2540
2674
|
envOr("IICP_BACKEND_TYPE", "openai_compat"),
|
|
2541
2675
|
backendApiKey: values["backend-api-key"] ?? envOr("IICP_BACKEND_API_KEY") ?? "",
|