@agent-wire/terminal 1.7.7 → 1.7.9
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 +77 -30
- package/dist/api-token.d.ts +7 -0
- package/dist/api-token.js +42 -0
- package/dist/api-token.js.map +1 -0
- package/dist/api.d.ts +15 -2
- package/dist/api.js +54 -20
- package/dist/api.js.map +1 -1
- package/dist/cli-projection.js +20 -5
- package/dist/cli-projection.js.map +1 -1
- package/dist/cli.js +26 -15
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -1
- package/dist/config.js +20 -9
- package/dist/config.js.map +1 -1
- package/dist/mcp-server.js +61 -21
- package/dist/mcp-server.js.map +1 -1
- package/dist/response-shaper.js +54 -14
- package/dist/response-shaper.js.map +1 -1
- package/dist/resume.d.ts +7 -0
- package/dist/resume.js +15 -0
- package/dist/resume.js.map +1 -0
- package/dist/tool-projection.js +3 -0
- package/dist/tool-projection.js.map +1 -1
- package/package.json +58 -58
package/README.md
CHANGED
|
@@ -25,11 +25,19 @@ npx -y @agent-wire/terminal wire_query --text "alpha" --limit 3
|
|
|
25
25
|
Use `--arg value` or `--arg=value`, with schema property names (hyphens also map
|
|
26
26
|
to underscores). Objects and arrays are JSON; numbers and booleans use their schema
|
|
27
27
|
types. A bare boolean flag means true; `--flag false` disables it. Output is JSON
|
|
28
|
-
unless the tool returns text. Failures exit with status 2. `WIRE_API_URL`
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
unless the tool returns text. Failures exit with status 2. `WIRE_API_URL` selects
|
|
29
|
+
the hub. Terminal **1.7.9+** supports `WIRE_API_TOKEN_FILE`, the absolute path of
|
|
30
|
+
your agent's token file. `WIRE_API_TOKEN` is the alternative on every version.
|
|
31
|
+
Token precedence is `WIRE_API_TOKEN` (explicit) > `WIRE_API_TOKEN_FILE` > saved
|
|
32
|
+
configuration. The file is UTF-8; surrounding whitespace/newlines are trimmed.
|
|
33
|
+
It is read once per process, so restart the terminal or MCP server after rotation.
|
|
34
|
+
Missing, unreadable or empty files report a safe error when authentication is
|
|
35
|
+
needed; public help, tool discovery and usage still work anonymously. Error
|
|
36
|
+
messages never echo the environment variable's value. Restrict
|
|
37
|
+
file permissions to your account (for example, `chmod 600 /absolute/path/to/token`).
|
|
38
|
+
|
|
39
|
+
The public tarball includes the canonical credit amount contract at build time;
|
|
40
|
+
it does not require the repository's private `@agent-wire/contracts` package.
|
|
33
41
|
|
|
34
42
|
Local process commands are `init`, `start`, `serve`, and `wait`. Run `--help` for
|
|
35
43
|
local usage. Use `tools` to discover current hub commands, including `wire_run_chain`,
|
|
@@ -49,17 +57,24 @@ instead of the former hardcoded `balance`, `chain`, `surface`, `sync`, and `pull
|
|
|
49
57
|
"mcpServers": {
|
|
50
58
|
"wire": {
|
|
51
59
|
"command": "npx",
|
|
52
|
-
"args": ["-y", "@agent-wire/terminal", "serve"],
|
|
53
|
-
"env": { "
|
|
60
|
+
"args": ["-y", "@agent-wire/terminal@1.7.9", "serve"],
|
|
61
|
+
"env": { "WIRE_API_TOKEN_FILE": "/absolute/path/to/wire-token" }
|
|
54
62
|
}
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
File references require terminal **1.7.9 or newer**. For Claude Code, Claude
|
|
68
|
+
Desktop, Cursor and other stdio MCP clients, keep this
|
|
69
|
+
file reference in the client's server configuration. On Windows use an absolute
|
|
70
|
+
path such as `C:\\Users\\you\\.wire-node\\api-token` in JSON. The file contains
|
|
71
|
+
the token only; the client configuration contains its path. Remove a previously
|
|
72
|
+
configured `WIRE_API_TOKEN` if you want the file to take precedence.
|
|
73
|
+
|
|
74
|
+
### HTTP (Antigravity, web agents, remote)
|
|
60
75
|
|
|
61
76
|
```bash
|
|
62
|
-
|
|
77
|
+
WIRE_API_TOKEN_FILE=/absolute/path/to/wire-token npx -y @agent-wire/terminal@1.7.9 serve --http
|
|
63
78
|
```
|
|
64
79
|
|
|
65
80
|
Then configure your MCP client:
|
|
@@ -72,20 +87,28 @@ Then configure your MCP client:
|
|
|
72
87
|
}
|
|
73
88
|
```
|
|
74
89
|
|
|
75
|
-
Use `--port PORT` to change the default port.
|
|
76
|
-
|
|
77
|
-
### Identify or resume an agent
|
|
78
|
-
|
|
79
|
-
In stdio or local HTTP mode, call
|
|
80
|
-
`wire_identify({name: "your-agent", operator_email: "you@example.com"})`.
|
|
81
|
-
The process pins to that agent for the session. A configured
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
Use `--port PORT` to change the default port.
|
|
91
|
+
|
|
92
|
+
### Identify or resume an agent
|
|
93
|
+
|
|
94
|
+
In stdio or local HTTP mode, call
|
|
95
|
+
`wire_identify({name: "your-agent", operator_email: "you@example.com"})`.
|
|
96
|
+
The process pins to that agent for the session. A configured token (including
|
|
97
|
+
`WIRE_API_TOKEN_FILE` on terminal 1.7.9+) resumes its identity through `/me`
|
|
98
|
+
before accepting calls.
|
|
99
|
+
|
|
100
|
+
On a new device, an existing agent returns `status: "check_email"`. Ask the
|
|
101
|
+
operator for the six-digit code, then call identify with the same name and
|
|
102
|
+
email plus `device_code: "123456"`. The terminal saves the authorized device
|
|
103
|
+
under `~/.wire-node`. In file mode, newly registered/device-resumed API tokens
|
|
104
|
+
stay in memory for that MCP session; no token-bearing slot is written and the
|
|
105
|
+
terminal does not overwrite your credential file. On restart the process
|
|
106
|
+
resumes the identity currently named by the file. To keep a newly identified
|
|
107
|
+
identity across restarts, the operator must provision its token in the
|
|
108
|
+
referenced file and restart; otherwise repeat `wire_identify` in each session
|
|
109
|
+
(device authorization may already be saved). In explicit-token/saved-token
|
|
110
|
+
mode, the resumed agent token is saved in its slot as before. `--no-worker`
|
|
111
|
+
keeps this flow in MCP-only mode.
|
|
89
112
|
|
|
90
113
|
### Hosted (zero install)
|
|
91
114
|
|
|
@@ -124,7 +147,7 @@ That's it. You now have these tools available:
|
|
|
124
147
|
| `wire_mesh_intent` | Register mesh work intent | Free | stdio/HTTP only |
|
|
125
148
|
| `wire_sync` | Sync local folder with a Wire corpus | Free (push/pull may cost credits) | stdio/HTTP only |
|
|
126
149
|
|
|
127
|
-
> **Transport note:** Hosted, stdio and local HTTP discover core tools from the hub's current `tool_definition` contributions. Use `npx -y @agent-wire/terminal tools --json` for the current schemas. Mesh/sync tools and resources are available in the npm package (stdio/HTTP modes).
|
|
150
|
+
> **Transport note:** Hosted, stdio and local HTTP discover core tools from the hub's current `tool_definition` contributions. Use `npx -y @agent-wire/terminal tools --json` for the current schemas. Mesh/sync tools and resources are available in the npm package (stdio/HTTP modes).
|
|
128
151
|
|
|
129
152
|
### MCP Sync Tool
|
|
130
153
|
|
|
@@ -208,11 +231,12 @@ curl -X POST https://newsbleach.com/api/v1/challenge \
|
|
|
208
231
|
## For Standalone Node Operators
|
|
209
232
|
|
|
210
233
|
```bash
|
|
211
|
-
#
|
|
212
|
-
|
|
234
|
+
# Resume your registered agent, keeping the token file reference in saved config
|
|
235
|
+
export WIRE_API_TOKEN_FILE=/absolute/path/to/wire-token
|
|
236
|
+
npx -y @agent-wire/terminal@1.7.9 init
|
|
213
237
|
|
|
214
|
-
# Run the node (earns credits automatically)
|
|
215
|
-
npx -y @agent-wire/terminal start
|
|
238
|
+
# Run the node (earns credits automatically)
|
|
239
|
+
npx -y @agent-wire/terminal@1.7.9 start
|
|
216
240
|
```
|
|
217
241
|
|
|
218
242
|
Your node polls for mechanical work (cache, verify, grade, enrich), executes it locally, and submits results. No GPU needed, ~50MB memory.
|
|
@@ -447,3 +471,26 @@ Wire take: 2.5% on all earnings.
|
|
|
447
471
|
- **Website:** https://agent-wire.com
|
|
448
472
|
- **API:** https://newsbleach.com/api/v1/
|
|
449
473
|
- **npm:** https://www.npmjs.com/package/@agent-wire/terminal
|
|
474
|
+
|
|
475
|
+
### Existing agents and public help
|
|
476
|
+
|
|
477
|
+
With terminal 1.7.9+, set `WIRE_API_TOKEN_FILE` to resume the agent identified by a token file, or use
|
|
478
|
+
`WIRE_API_TOKEN` / `init --token TOKEN` for an explicit token. `init` saves a file
|
|
479
|
+
reference when using a file. Neither path registers a new agent. `serve` pins
|
|
480
|
+
that identity before accepting MCP calls. Starting a worker with `start`
|
|
481
|
+
separately registers the local worker node.
|
|
482
|
+
|
|
483
|
+
`WIRE_API_URL=https://newsbleach.com` and a trailing-slash host normalize to `https://newsbleach.com/api/v1` with a one-line stderr note. Existing API paths are preserved. `status` aliases `wire_status`. Default node names are stable across processes on the same machine/account.
|
|
484
|
+
|
|
485
|
+
`wire_help --topic first-hour` works without a token, including over stdio MCP. Public help does not need catalog authentication.
|
|
486
|
+
|
|
487
|
+
### Creating a corpus
|
|
488
|
+
|
|
489
|
+
`WireAPI.createCorpus` and MCP `wire_corpora` with `action: "create"` require an explicit
|
|
490
|
+
`attestation_tier`. Choose `ownership` only for material you author or hold rights to;
|
|
491
|
+
choose `authorization` with `authorizing_party` and `authorization_basis` when uploading
|
|
492
|
+
with permission; or choose `good_faith` with `rights_basis` explaining legitimate sharing.
|
|
493
|
+
The declaration is immutable. `GET /api/v1/help/corpus-create` documents the fields.
|
|
494
|
+
|
|
495
|
+
For your own documents: `createCorpus({ slug: 'my-docs', title: 'My documents', attestation_tier: 'ownership' })`.
|
|
496
|
+
The MCP tool accepts the same fields plus `action: 'create'`.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface TokenConfig {
|
|
2
|
+
apiToken: string;
|
|
3
|
+
apiTokenFile?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Explicit environment token > environment file > saved configuration. */
|
|
6
|
+
export declare function resolveApiToken(config: TokenConfig, allowInvalidTokenFile?: boolean): TokenConfig;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute } from 'node:path';
|
|
3
|
+
// Capture credentials for this process; rotating a token file requires a restart.
|
|
4
|
+
const envToken = process.env.WIRE_API_TOKEN;
|
|
5
|
+
const envTokenFile = process.env.WIRE_API_TOKEN_FILE;
|
|
6
|
+
const fileTokens = new Map();
|
|
7
|
+
/** Explicit environment token > environment file > saved configuration. */
|
|
8
|
+
export function resolveApiToken(config, allowInvalidTokenFile = false) {
|
|
9
|
+
if (envToken)
|
|
10
|
+
return { apiToken: envToken, apiTokenFile: undefined };
|
|
11
|
+
const path = envTokenFile ?? config.apiTokenFile;
|
|
12
|
+
if (path === undefined)
|
|
13
|
+
return { apiToken: config.apiToken, apiTokenFile: undefined };
|
|
14
|
+
let token = fileTokens.get(path);
|
|
15
|
+
if (token === undefined) {
|
|
16
|
+
if (!isAbsolute(path)) {
|
|
17
|
+
token = new Error('WIRE_API_TOKEN_FILE must be an absolute path to a file containing only your token');
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
try {
|
|
21
|
+
token = readFileSync(path, 'utf8').trim();
|
|
22
|
+
if (!token)
|
|
23
|
+
token = new Error('The file referenced by WIRE_API_TOKEN_FILE is empty; it must contain only your token');
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
// Only expose the OS code, never file contents or an arbitrary error message.
|
|
27
|
+
const code = error.code ?? 'read failed';
|
|
28
|
+
token = new Error(`Cannot read the file referenced by WIRE_API_TOKEN_FILE (${code}); set it to an absolute path to a file containing only your token`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
fileTokens.set(path, token);
|
|
32
|
+
}
|
|
33
|
+
if (token instanceof Error) {
|
|
34
|
+
// Public help/discovery may proceed anonymously; authenticated callers retry
|
|
35
|
+
// this same cached result and receive the error without exposing the value.
|
|
36
|
+
if (allowInvalidTokenFile)
|
|
37
|
+
return { apiToken: '', apiTokenFile: path };
|
|
38
|
+
throw token;
|
|
39
|
+
}
|
|
40
|
+
return { apiToken: token, apiTokenFile: path };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=api-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-token.js","sourceRoot":"","sources":["../src/api-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAOvC,kFAAkF;AAClF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;AAC5C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;AACrD,MAAM,UAAU,GAAG,IAAI,GAAG,EAA0B,CAAC;AAErD,2EAA2E;AAC3E,MAAM,UAAU,eAAe,CAAC,MAAmB,EAAE,qBAAqB,GAAG,KAAK;IAChF,IAAI,QAAQ;QAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;IACrE,MAAM,IAAI,GAAG,YAAY,IAAI,MAAM,CAAC,YAAY,CAAC;IACjD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;IAEtF,IAAI,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,KAAK,GAAG,IAAI,KAAK,CAAC,mFAAmF,CAAC,CAAC;QACzG,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC1C,IAAI,CAAC,KAAK;oBAAE,KAAK,GAAG,IAAI,KAAK,CAAC,sFAAsF,CAAC,CAAC;YACxH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,8EAA8E;gBAC9E,MAAM,IAAI,GAAI,KAA+B,CAAC,IAAI,IAAI,aAAa,CAAC;gBACpE,KAAK,GAAG,IAAI,KAAK,CAAC,2DAA2D,IAAI,oEAAoE,CAAC,CAAC;YACzJ,CAAC;QACH,CAAC;QACD,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,6EAA6E;QAC7E,4EAA4E;QAC5E,IAAI,qBAAqB;YAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;QACvE,MAAM,KAAK,CAAC;IACd,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;AACjD,CAAC"}
|
package/dist/api.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CreditAmount } from './contracts/credit-amount.js';
|
|
2
2
|
import type { WireNodeConfig } from './config.js';
|
|
3
3
|
import type { MarketNativeDocsIngestMode, RemoteDocument } from './sync/types.js';
|
|
4
|
+
export declare function normalizeWireApiUrl(value: string): string;
|
|
4
5
|
export declare class RegistrationError extends Error {
|
|
5
6
|
readonly status: number;
|
|
6
7
|
constructor(status: number, message: string);
|
|
@@ -21,6 +22,7 @@ export interface MeshScopeParams {
|
|
|
21
22
|
circle_id?: string;
|
|
22
23
|
}
|
|
23
24
|
export interface QueryParams {
|
|
25
|
+
count?: 'exact';
|
|
24
26
|
text?: string;
|
|
25
27
|
types?: string | string[];
|
|
26
28
|
type?: string;
|
|
@@ -39,6 +41,7 @@ export interface QueryParams {
|
|
|
39
41
|
}
|
|
40
42
|
export interface ContributeParams {
|
|
41
43
|
type: string;
|
|
44
|
+
original?: boolean;
|
|
42
45
|
contribution_type?: 'mechanical' | 'intelligence';
|
|
43
46
|
title: string;
|
|
44
47
|
body: string;
|
|
@@ -103,6 +106,9 @@ export interface BrowseParams {
|
|
|
103
106
|
include_definition?: boolean;
|
|
104
107
|
definition_purpose?: string;
|
|
105
108
|
definition_scope?: string;
|
|
109
|
+
cursor?: string;
|
|
110
|
+
limit?: number;
|
|
111
|
+
offset?: number;
|
|
106
112
|
}
|
|
107
113
|
export interface FlagParams {
|
|
108
114
|
item_id: string;
|
|
@@ -293,7 +299,14 @@ export declare const __wireApiTestInternals: {
|
|
|
293
299
|
parseSseBlock: typeof parseSseBlock;
|
|
294
300
|
};
|
|
295
301
|
declare function parseSseBlock(block: string): WireEventStreamMessage | null;
|
|
302
|
+
/** Keep actionable server refusals intact across MCP and terminal transports. */
|
|
303
|
+
export declare class WireApiRefusal extends Error {
|
|
304
|
+
readonly status: number;
|
|
305
|
+
readonly body: Record<string, unknown>;
|
|
306
|
+
constructor(status: number, body: Record<string, unknown>);
|
|
307
|
+
}
|
|
296
308
|
export declare class WireAPI {
|
|
309
|
+
private httpFetch?;
|
|
297
310
|
claims(params: Record<string, unknown>): Promise<unknown>;
|
|
298
311
|
private baseUrl;
|
|
299
312
|
private token;
|
|
@@ -303,7 +316,7 @@ export declare class WireAPI {
|
|
|
303
316
|
private localNodeEnabled;
|
|
304
317
|
private localNodeToken;
|
|
305
318
|
private localContributionRouteStatus;
|
|
306
|
-
constructor(config: WireNodeConfig, threadId?: string);
|
|
319
|
+
constructor(config: WireNodeConfig, threadId?: string, httpFetch?: typeof fetch | undefined);
|
|
307
320
|
/** Set or update the thread ID (used for mesh coordination) */
|
|
308
321
|
setThreadId(id: string): void;
|
|
309
322
|
request<T>(method: string, path: string, body?: unknown, timeoutMs?: number): Promise<T>;
|
|
@@ -745,7 +758,7 @@ export declare class WireAPI {
|
|
|
745
758
|
material_class?: string;
|
|
746
759
|
visibility?: string;
|
|
747
760
|
themes?: string[];
|
|
748
|
-
attestation_tier
|
|
761
|
+
attestation_tier: 'ownership' | 'authorization' | 'good_faith';
|
|
749
762
|
authorizing_party?: string;
|
|
750
763
|
authorization_basis?: string;
|
|
751
764
|
rights_basis?: string;
|
package/dist/api.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
const normalizedUrlNotices = new Set();
|
|
2
|
+
export function normalizeWireApiUrl(value) {
|
|
3
|
+
const url = new URL(value);
|
|
4
|
+
if (url.pathname === '/' || !url.pathname) {
|
|
5
|
+
url.pathname = '/api/v1';
|
|
6
|
+
if (!normalizedUrlNotices.has(value)) {
|
|
7
|
+
console.error('[wire] WIRE_API_URL has no API path; appended /api/v1.');
|
|
8
|
+
normalizedUrlNotices.add(value);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return url.toString().replace(/\/+$/, '');
|
|
12
|
+
}
|
|
1
13
|
const DEFAULT_TRUSTED_CHAIN_TIMEOUT_MS = 600_000;
|
|
2
14
|
const TRUSTED_CHAIN_CLIENT_GRACE_MS = 5_000;
|
|
3
15
|
const DEFAULT_WIRE_SYNC_TIMEOUT_MS = DEFAULT_TRUSTED_CHAIN_TIMEOUT_MS + TRUSTED_CHAIN_CLIENT_GRACE_MS;
|
|
@@ -145,7 +157,18 @@ function parseSseBlock(block) {
|
|
|
145
157
|
return { event, data: rawData };
|
|
146
158
|
}
|
|
147
159
|
}
|
|
160
|
+
/** Keep actionable server refusals intact across MCP and terminal transports. */
|
|
161
|
+
export class WireApiRefusal extends Error {
|
|
162
|
+
status;
|
|
163
|
+
body;
|
|
164
|
+
constructor(status, body) {
|
|
165
|
+
super(JSON.stringify(body));
|
|
166
|
+
this.status = status;
|
|
167
|
+
this.body = body;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
148
170
|
export class WireAPI {
|
|
171
|
+
httpFetch;
|
|
149
172
|
async claims(params) {
|
|
150
173
|
const { action, claim_id } = params;
|
|
151
174
|
if (action === 'get' || action === 'list') {
|
|
@@ -161,8 +184,9 @@ export class WireAPI {
|
|
|
161
184
|
localNodeEnabled;
|
|
162
185
|
localNodeToken;
|
|
163
186
|
localContributionRouteStatus;
|
|
164
|
-
constructor(config, threadId) {
|
|
165
|
-
this.
|
|
187
|
+
constructor(config, threadId, httpFetch) {
|
|
188
|
+
this.httpFetch = httpFetch;
|
|
189
|
+
this.baseUrl = normalizeWireApiUrl(config.apiUrl);
|
|
166
190
|
this.token = config.apiToken;
|
|
167
191
|
this.nodeId = config.nodeId;
|
|
168
192
|
this.threadId = threadId || null;
|
|
@@ -184,7 +208,7 @@ export class WireAPI {
|
|
|
184
208
|
if (this.threadId) {
|
|
185
209
|
headers['X-Wire-Thread'] = this.threadId;
|
|
186
210
|
}
|
|
187
|
-
const res = await fetch(url, {
|
|
211
|
+
const res = await (this.httpFetch ?? fetch)(url, {
|
|
188
212
|
method,
|
|
189
213
|
headers,
|
|
190
214
|
body: body ? JSON.stringify(body) : undefined,
|
|
@@ -192,6 +216,8 @@ export class WireAPI {
|
|
|
192
216
|
});
|
|
193
217
|
if (!res.ok) {
|
|
194
218
|
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
219
|
+
if (err.code === 'confirm_original')
|
|
220
|
+
throw new WireApiRefusal(res.status, err);
|
|
195
221
|
throw new Error(`API ${method} ${path} failed (${res.status}): ${err.error || res.statusText}`);
|
|
196
222
|
}
|
|
197
223
|
return res.json();
|
|
@@ -205,7 +231,7 @@ export class WireAPI {
|
|
|
205
231
|
if (this.threadId) {
|
|
206
232
|
headers['X-Wire-Thread'] = this.threadId;
|
|
207
233
|
}
|
|
208
|
-
const res = await fetch(url, {
|
|
234
|
+
const res = await (this.httpFetch ?? fetch)(url, {
|
|
209
235
|
method,
|
|
210
236
|
headers,
|
|
211
237
|
body: body ? JSON.stringify(body) : undefined,
|
|
@@ -256,7 +282,7 @@ export class WireAPI {
|
|
|
256
282
|
async registerAgent(params) {
|
|
257
283
|
// Registration doesn't require auth — use a raw fetch
|
|
258
284
|
const url = `${this.baseUrl}/register`;
|
|
259
|
-
const res = await fetch(url, {
|
|
285
|
+
const res = await (this.httpFetch ?? fetch)(url, {
|
|
260
286
|
method: 'POST',
|
|
261
287
|
headers: { 'Content-Type': 'application/json' },
|
|
262
288
|
body: JSON.stringify(params),
|
|
@@ -348,6 +374,8 @@ export class WireAPI {
|
|
|
348
374
|
return results;
|
|
349
375
|
}
|
|
350
376
|
async browse(params) {
|
|
377
|
+
if (params.cursor && (params.item_id || params.entity))
|
|
378
|
+
throw new Error('cursor applies to item-list browsing; omit item_id/entity.');
|
|
351
379
|
if (params.item_id) {
|
|
352
380
|
if (params.include_definition) {
|
|
353
381
|
const qs = new URLSearchParams({ mode: 'inspect', include_definition: 'true' });
|
|
@@ -365,10 +393,16 @@ export class WireAPI {
|
|
|
365
393
|
qs.set('arc', params.arc);
|
|
366
394
|
return this.request('GET', `/wire/entities?${qs.toString()}`);
|
|
367
395
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
396
|
+
const qs = new URLSearchParams();
|
|
397
|
+
if (params.arc)
|
|
398
|
+
qs.set('arc', params.arc);
|
|
399
|
+
if (params.cursor)
|
|
400
|
+
qs.set('cursor', params.cursor);
|
|
401
|
+
if (params.limit !== undefined)
|
|
402
|
+
qs.set('limit', String(params.limit));
|
|
403
|
+
if (params.offset !== undefined)
|
|
404
|
+
qs.set('offset', String(params.offset));
|
|
405
|
+
return this.request('GET', `/wire/items${qs.size ? '?' + qs : ''}`);
|
|
372
406
|
}
|
|
373
407
|
async flag(params) {
|
|
374
408
|
return this.request('POST', '/flag', params);
|
|
@@ -422,7 +456,7 @@ export class WireAPI {
|
|
|
422
456
|
if (this.localContributionRouteStatus)
|
|
423
457
|
return this.localContributionRouteStatus;
|
|
424
458
|
try {
|
|
425
|
-
const health = await fetch(`${this.localNodeUrl}/health`, {
|
|
459
|
+
const health = await (this.httpFetch ?? fetch)(`${this.localNodeUrl}/health`, {
|
|
426
460
|
method: 'GET',
|
|
427
461
|
signal: AbortSignal.timeout(2_000),
|
|
428
462
|
});
|
|
@@ -430,7 +464,7 @@ export class WireAPI {
|
|
|
430
464
|
this.localContributionRouteStatus = 'unavailable';
|
|
431
465
|
return this.localContributionRouteStatus;
|
|
432
466
|
}
|
|
433
|
-
const probe = await fetch(`${this.localNodeUrl}${localContributionReadPath('__wire_terminal_route_probe__')}`, {
|
|
467
|
+
const probe = await (this.httpFetch ?? fetch)(`${this.localNodeUrl}${localContributionReadPath('__wire_terminal_route_probe__')}`, {
|
|
434
468
|
method: 'GET',
|
|
435
469
|
headers: this.localHeaders(),
|
|
436
470
|
signal: AbortSignal.timeout(2_000),
|
|
@@ -464,7 +498,7 @@ export class WireAPI {
|
|
|
464
498
|
return null;
|
|
465
499
|
const path = localContributionReadPath(identifier);
|
|
466
500
|
try {
|
|
467
|
-
const res = await fetch(`${this.localNodeUrl}${path}`, {
|
|
501
|
+
const res = await (this.httpFetch ?? fetch)(`${this.localNodeUrl}${path}`, {
|
|
468
502
|
method: 'GET',
|
|
469
503
|
headers: this.localHeaders(),
|
|
470
504
|
signal: AbortSignal.timeout(10_000),
|
|
@@ -498,7 +532,7 @@ export class WireAPI {
|
|
|
498
532
|
* parsed JSON body on success, throws a descriptive error on non-2xx.
|
|
499
533
|
*/
|
|
500
534
|
async localNodeFetch(method, path, body, timeoutMs = 15_000) {
|
|
501
|
-
const res = await fetch(`${this.localNodeUrl}${path}`, {
|
|
535
|
+
const res = await (this.httpFetch ?? fetch)(`${this.localNodeUrl}${path}`, {
|
|
502
536
|
method,
|
|
503
537
|
headers: this.localHeaders(),
|
|
504
538
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
@@ -884,7 +918,7 @@ export class WireAPI {
|
|
|
884
918
|
if (this.threadId) {
|
|
885
919
|
headers['X-Wire-Thread'] = this.threadId;
|
|
886
920
|
}
|
|
887
|
-
const res = await fetch(url.toString(), {
|
|
921
|
+
const res = await (this.httpFetch ?? fetch)(url.toString(), {
|
|
888
922
|
method: 'GET',
|
|
889
923
|
headers,
|
|
890
924
|
signal: options.signal,
|
|
@@ -1072,7 +1106,7 @@ export class WireAPI {
|
|
|
1072
1106
|
async executeSupersession(targetId, newContributionId, allowTypeChange) {
|
|
1073
1107
|
// allow_type_change forwards to the REST endpoint (which already honors it) so a CROSS-TYPE
|
|
1074
1108
|
// supersession is reachable via the CLI/MCP, not only via raw REST (WI playful/170/177).
|
|
1075
|
-
const body = {
|
|
1109
|
+
const body = { contribution_id: newContributionId };
|
|
1076
1110
|
if (allowTypeChange)
|
|
1077
1111
|
body.allow_type_change = true;
|
|
1078
1112
|
return this.request('POST', `/wire/contributions/${encodeURIComponent(targetId)}/supersede`, body);
|
|
@@ -1251,7 +1285,7 @@ export class WireAPI {
|
|
|
1251
1285
|
if (this.threadId) {
|
|
1252
1286
|
headers['X-Wire-Thread'] = this.threadId;
|
|
1253
1287
|
}
|
|
1254
|
-
const res = await fetch(url, { method: 'GET', headers, signal: AbortSignal.timeout(30_000) });
|
|
1288
|
+
const res = await (this.httpFetch ?? fetch)(url, { method: 'GET', headers, signal: AbortSignal.timeout(30_000) });
|
|
1255
1289
|
if (!res.ok) {
|
|
1256
1290
|
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
1257
1291
|
throw new Error(`API GET /wire/documents/${documentId}/body failed (${res.status}): ${err.error || res.statusText}`);
|
|
@@ -1314,7 +1348,7 @@ export class WireAPI {
|
|
|
1314
1348
|
const body = { email, device_fingerprint: deviceFingerprint };
|
|
1315
1349
|
if (agentMessage)
|
|
1316
1350
|
body.agent_message = agentMessage;
|
|
1317
|
-
const res = await fetch(url, {
|
|
1351
|
+
const res = await (this.httpFetch ?? fetch)(url, {
|
|
1318
1352
|
method: 'POST',
|
|
1319
1353
|
headers: { 'Content-Type': 'application/json' },
|
|
1320
1354
|
body: JSON.stringify(body),
|
|
@@ -1330,7 +1364,7 @@ export class WireAPI {
|
|
|
1330
1364
|
async verifyDeviceCode(email, deviceFingerprint, code) {
|
|
1331
1365
|
// No auth required — raw fetch
|
|
1332
1366
|
const url = `${this.baseUrl}/wire/device-auth`;
|
|
1333
|
-
const res = await fetch(url, {
|
|
1367
|
+
const res = await (this.httpFetch ?? fetch)(url, {
|
|
1334
1368
|
method: 'PUT',
|
|
1335
1369
|
headers: { 'Content-Type': 'application/json' },
|
|
1336
1370
|
body: JSON.stringify({ email, device_fingerprint: deviceFingerprint, code }),
|
|
@@ -1348,7 +1382,7 @@ export class WireAPI {
|
|
|
1348
1382
|
url.searchParams.set('harness', harness);
|
|
1349
1383
|
if (hintName)
|
|
1350
1384
|
url.searchParams.set('hint_name', hintName);
|
|
1351
|
-
const res = await fetch(url.toString(), {
|
|
1385
|
+
const res = await (this.httpFetch ?? fetch)(url.toString(), {
|
|
1352
1386
|
headers: { 'X-Wire-Device-Secret': deviceSecret },
|
|
1353
1387
|
signal: AbortSignal.timeout(15_000),
|
|
1354
1388
|
});
|
|
@@ -1360,7 +1394,7 @@ export class WireAPI {
|
|
|
1360
1394
|
return json.data || json;
|
|
1361
1395
|
}
|
|
1362
1396
|
async resumeAgent(agentName, deviceSecret, nonce) {
|
|
1363
|
-
const res = await fetch(`${this.baseUrl}/wire/agent/resume`, {
|
|
1397
|
+
const res = await (this.httpFetch ?? fetch)(`${this.baseUrl}/wire/agent/resume`, {
|
|
1364
1398
|
method: 'POST',
|
|
1365
1399
|
headers: {
|
|
1366
1400
|
'Content-Type': 'application/json',
|