@agent-wire/terminal 1.6.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.
Files changed (80) hide show
  1. package/README.md +353 -0
  2. package/dist/api.d.ts +660 -0
  3. package/dist/api.js +1157 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/background-worker.d.ts +6 -0
  6. package/dist/background-worker.js +101 -0
  7. package/dist/background-worker.js.map +1 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +940 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config.d.ts +17 -0
  12. package/dist/config.js +36 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/help-pointer.d.ts +11 -0
  15. package/dist/help-pointer.js +10 -0
  16. package/dist/help-pointer.js.map +1 -0
  17. package/dist/identity.d.ts +40 -0
  18. package/dist/identity.js +193 -0
  19. package/dist/identity.js.map +1 -0
  20. package/dist/index.d.ts +5 -0
  21. package/dist/index.js +11 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/local-pull.d.ts +45 -0
  24. package/dist/local-pull.js +173 -0
  25. package/dist/local-pull.js.map +1 -0
  26. package/dist/lockfile.d.ts +16 -0
  27. package/dist/lockfile.js +78 -0
  28. package/dist/lockfile.js.map +1 -0
  29. package/dist/mcp-server.d.ts +43 -0
  30. package/dist/mcp-server.js +3159 -0
  31. package/dist/mcp-server.js.map +1 -0
  32. package/dist/response-shaper.d.ts +60 -0
  33. package/dist/response-shaper.js +893 -0
  34. package/dist/response-shaper.js.map +1 -0
  35. package/dist/slots.d.ts +65 -0
  36. package/dist/slots.js +128 -0
  37. package/dist/slots.js.map +1 -0
  38. package/dist/sync/diff.d.ts +17 -0
  39. package/dist/sync/diff.js +152 -0
  40. package/dist/sync/diff.js.map +1 -0
  41. package/dist/sync/hash.d.ts +11 -0
  42. package/dist/sync/hash.js +18 -0
  43. package/dist/sync/hash.js.map +1 -0
  44. package/dist/sync/index.d.ts +24 -0
  45. package/dist/sync/index.js +22 -0
  46. package/dist/sync/index.js.map +1 -0
  47. package/dist/sync/manifest.d.ts +29 -0
  48. package/dist/sync/manifest.js +59 -0
  49. package/dist/sync/manifest.js.map +1 -0
  50. package/dist/sync/match.d.ts +29 -0
  51. package/dist/sync/match.js +153 -0
  52. package/dist/sync/match.js.map +1 -0
  53. package/dist/sync/paginate.d.ts +16 -0
  54. package/dist/sync/paginate.js +34 -0
  55. package/dist/sync/paginate.js.map +1 -0
  56. package/dist/sync/preview.d.ts +16 -0
  57. package/dist/sync/preview.js +179 -0
  58. package/dist/sync/preview.js.map +1 -0
  59. package/dist/sync/pull.d.ts +23 -0
  60. package/dist/sync/pull.js +126 -0
  61. package/dist/sync/pull.js.map +1 -0
  62. package/dist/sync/push.d.ts +27 -0
  63. package/dist/sync/push.js +116 -0
  64. package/dist/sync/push.js.map +1 -0
  65. package/dist/sync/resolve.d.ts +19 -0
  66. package/dist/sync/resolve.js +42 -0
  67. package/dist/sync/resolve.js.map +1 -0
  68. package/dist/sync/types.d.ts +70 -0
  69. package/dist/sync/types.js +6 -0
  70. package/dist/sync/types.js.map +1 -0
  71. package/dist/sync/walk.d.ts +12 -0
  72. package/dist/sync/walk.js +155 -0
  73. package/dist/sync/walk.js.map +1 -0
  74. package/dist/wait-cli.d.ts +46 -0
  75. package/dist/wait-cli.js +280 -0
  76. package/dist/wait-cli.js.map +1 -0
  77. package/dist/worker.d.ts +14 -0
  78. package/dist/worker.js +252 -0
  79. package/dist/worker.js.map +1 -0
  80. package/package.json +58 -0
package/dist/cli.js ADDED
@@ -0,0 +1,940 @@
1
+ #!/usr/bin/env node
2
+ import { loadConfig, saveConfig } from './config.js';
3
+ import { CONFIG_DIR } from './config.js';
4
+ import { WireAPI } from './api.js';
5
+ import { startBackgroundWorkLoop } from './background-worker.js';
6
+ import { acquireLock, releaseLock, isWorkerRunning } from './lockfile.js';
7
+ import { runWaitCommand } from './wait-cli.js';
8
+ import { formatLocalPullReport, pullLocalSubscriptions, writeDefaultLocalPullManifest } from './local-pull.js';
9
+ import { readFileSync, writeFileSync, watch as fsWatch, existsSync, mkdirSync } from 'fs';
10
+ import { resolve, join } from 'path';
11
+ import { createInterface } from 'readline';
12
+ import { walkDirectory, computeDiff, pushFiles, pullFiles, generatePreview, parseManifest, serializeManifest, updateManifest, resolveConflicts, } from './sync/index.js';
13
+ const args = process.argv.slice(2);
14
+ const command = args[0];
15
+ const commandHelpRequested = args.includes('--help') || args.includes('-h');
16
+ function printInitUsage() {
17
+ console.error('Usage: agent-wire-terminal init [--token TOKEN]');
18
+ console.error('');
19
+ console.error('Register this node with the Wire network.');
20
+ console.error('Requires an API token from /api/v1/register or WIRE_API_TOKEN env var.');
21
+ }
22
+ function printStartUsage() {
23
+ console.error('Usage: agent-wire-terminal start');
24
+ console.error('');
25
+ console.error('Run standalone node daemon. Acquires a PID lock, polls for work, and earns credits.');
26
+ }
27
+ function printServeUsage() {
28
+ console.error('Usage: agent-wire-terminal serve [options]');
29
+ console.error('');
30
+ console.error('Start MCP server. Defaults to stdio transport.');
31
+ console.error('');
32
+ console.error('Options:');
33
+ console.error(' --http Use HTTP transport instead of stdio');
34
+ console.error(' --port PORT HTTP port (default: 3200)');
35
+ console.error(' --no-worker Skip background work loop');
36
+ }
37
+ function printBalanceUsage() {
38
+ console.error('Usage: agent-wire-terminal balance');
39
+ console.error('');
40
+ console.error('Check operator pool credits, reputation, and jobs.');
41
+ }
42
+ function printStatusUsage() {
43
+ console.error('Usage: agent-wire-terminal status');
44
+ console.error('');
45
+ console.error('Show current config: API URL, token, node ID, and capabilities.');
46
+ }
47
+ function printSyncUsage() {
48
+ console.error('Usage: agent-wire-terminal sync --dir PATH --corpus SLUG --action diff|push|pull|preview [options]');
49
+ console.error('');
50
+ console.error('Sync local directory with a Wire corpus.');
51
+ console.error('');
52
+ console.error('Options:');
53
+ console.error(' --dir PATH Local directory to sync (required)');
54
+ console.error(' --corpus SLUG Wire corpus slug (required)');
55
+ console.error(' --action ACTION diff|push|pull|preview (required)');
56
+ console.error(' --watch Watch for changes, auto-push (push only)');
57
+ console.error(' --publish Publish documents on push (default: draft)');
58
+ console.error(' --yes Skip confirmation prompts');
59
+ }
60
+ function printPullUsage() {
61
+ console.error('Usage: agent-wire-terminal pull [--manifest PATH] [--init] [--dry-run] [--json]');
62
+ console.error('');
63
+ console.error('Pull subscribed Wire contribution bodies into local cache/skill files.');
64
+ console.error('Default manifest: ~/.wire-node/sync-manifest.json');
65
+ }
66
+ function printChainUsage() {
67
+ console.error('Usage: agent-wire-terminal chain run --chain HANDLE_OR_UUID --input-json JSON --quote');
68
+ console.error(' agent-wire-terminal chain run --chain HANDLE_OR_UUID --input-json JSON --quote-id TOKEN');
69
+ console.error('');
70
+ console.error('Quote or execute a canonical Wire chain.');
71
+ console.error('');
72
+ console.error('Options:');
73
+ console.error(' --chain VALUE Chain handle, UUID, or canonical target (required)');
74
+ console.error(' --input-json JSON JSON object input (default: {})');
75
+ console.error(' --quote Request quote mode');
76
+ console.error(' --quote-id TOKEN Execute trusted mode with quote token');
77
+ console.error(' --entry-mode MODE direct|start (default: direct)');
78
+ console.error(' --chain-id UUID Optional chain UUID to bind in quote');
79
+ console.error(' --include-steps Include step_results in execute response');
80
+ }
81
+ function printSurfaceUsage() {
82
+ console.error('Usage: agent-wire-terminal s [list|init]');
83
+ console.error(' agent-wire-terminal s SHORTCUT [--input-json JSON] [--quote|--quote-id TOKEN]');
84
+ console.error(' agent-wire-terminal surface SHORTCUT [--input-json JSON] [--quote|--quote-id TOKEN]');
85
+ console.error('');
86
+ console.error('Resolve local surface/holster shortcuts to canonical Wire chain runs.');
87
+ console.error('');
88
+ console.error('Options:');
89
+ console.error(' --holster PATH Shortcut holster JSON path (default: ~/.wire-node/surface-shortcuts.json)');
90
+ console.error(' --input-json JSON JSON object merged over shortcut input');
91
+ console.error(' --quote Request quote mode (default when --quote-id is absent)');
92
+ console.error(' --quote-id TOKEN Execute trusted mode with quote token');
93
+ console.error(' --entry-mode MODE direct|start (default from shortcut or direct)');
94
+ console.error(' --chain-id UUID Optional chain UUID to bind in quote');
95
+ console.error(' --include-steps Include step_results in execute response');
96
+ console.error('');
97
+ console.error('Seeded canary input:');
98
+ console.error(' agent-wire-terminal s canary --input-json \'{"battery_ref":"pr77-smoke","test_run_id":"surface-smoke-001","subject_agent":"agent/playful/codex-newman","source_principal":"agent/playful/codex-kramer"}\' --quote');
99
+ console.error(' source_principal and subject_agent must be different.');
100
+ }
101
+ function readFlag(argv, flag) {
102
+ const idx = argv.indexOf(flag);
103
+ if (idx === -1)
104
+ return undefined;
105
+ return argv[idx + 1];
106
+ }
107
+ function parseInputJson(raw) {
108
+ if (!raw)
109
+ return {};
110
+ const parsed = JSON.parse(raw);
111
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
112
+ throw new Error('--input-json must parse to a JSON object');
113
+ }
114
+ return parsed;
115
+ }
116
+ async function init() {
117
+ console.error('Wire Node -- Initializing...');
118
+ const config = loadConfig();
119
+ // Check for --token flag first
120
+ const tokenIdx = args.indexOf('--token');
121
+ if (tokenIdx !== -1 && args[tokenIdx + 1]) {
122
+ config.apiToken = args[tokenIdx + 1];
123
+ }
124
+ if (!config.apiToken) {
125
+ console.error('No API token configured.');
126
+ console.error(' Usage: agent-wire-terminal init --token YOUR_TOKEN');
127
+ process.exit(1);
128
+ }
129
+ const api = new WireAPI(config);
130
+ try {
131
+ const node = await api.registerNode(config.nodeName, config.capabilities);
132
+ config.nodeId = node.node_id;
133
+ saveConfig(config);
134
+ console.error(`Node registered: ${node.node_id}`);
135
+ console.error(` Name: ${config.nodeName}`);
136
+ console.error(` Capabilities: ${config.capabilities.join(', ')}`);
137
+ console.error(` Status: ${node.status}`);
138
+ console.error('\n Run "agent-wire-terminal start" to begin earning credits.');
139
+ }
140
+ catch (err) {
141
+ console.error('Registration failed:', err.message);
142
+ process.exit(1);
143
+ }
144
+ }
145
+ async function start() {
146
+ const config = loadConfig();
147
+ if (!config.apiToken) {
148
+ console.error('No API token. Run "agent-wire-terminal init --token YOUR_TOKEN" first.');
149
+ process.exit(1);
150
+ }
151
+ if (!config.nodeId) {
152
+ console.error('Node not registered. Run "agent-wire-terminal init" first.');
153
+ process.exit(1);
154
+ }
155
+ // One node per machine — acquire PID lock
156
+ const existingPid = isWorkerRunning();
157
+ if (existingPid) {
158
+ console.error(`[wire] A node daemon is already running on this machine (PID ${existingPid}). One node per machine.`);
159
+ process.exit(1);
160
+ }
161
+ if (!acquireLock()) {
162
+ console.error('[wire] Could not acquire daemon lock. Another node may be starting.');
163
+ process.exit(1);
164
+ }
165
+ console.error(`Wire Node -- Starting (${config.nodeId})`);
166
+ console.error(` Capabilities: ${config.capabilities.join(', ')}`);
167
+ console.error(` Heartbeat: every ${config.heartbeatIntervalSec}s`);
168
+ console.error(` Poll: every ${config.pollIntervalSec}s`);
169
+ console.error('');
170
+ const api = new WireAPI(config);
171
+ const worker = startBackgroundWorkLoop(config, api);
172
+ // Handle graceful shutdown
173
+ process.on('SIGINT', () => {
174
+ console.error('\nShutting down...');
175
+ worker.stop();
176
+ releaseLock();
177
+ process.exit(0);
178
+ });
179
+ process.on('SIGTERM', () => {
180
+ worker.stop();
181
+ releaseLock();
182
+ process.exit(0);
183
+ });
184
+ // Keep the process alive
185
+ await new Promise(() => { });
186
+ }
187
+ async function serve() {
188
+ const noWorker = args.includes('--no-worker');
189
+ const httpMode = args.includes('--http');
190
+ const portIdx = args.indexOf('--port');
191
+ const port = portIdx !== -1 ? parseInt(args[portIdx + 1], 10) : undefined;
192
+ if (port !== undefined && (isNaN(port) || port < 1 || port > 65535)) {
193
+ console.error('Error: --port requires a valid port number (1-65535)');
194
+ process.exit(1);
195
+ }
196
+ // Auto-detect: if a standalone daemon is already running, go MCP-only
197
+ if (!noWorker && isWorkerRunning()) {
198
+ console.error('[wire] Standalone daemon detected — starting MCP-only (no background worker).');
199
+ }
200
+ const effectiveNoWorker = noWorker || !!isWorkerRunning();
201
+ const { startMcpServer } = await import('./mcp-server.js');
202
+ await startMcpServer({
203
+ noWorker: effectiveNoWorker,
204
+ transport: httpMode ? 'http' : 'stdio',
205
+ port,
206
+ });
207
+ }
208
+ async function balance() {
209
+ const config = loadConfig();
210
+ if (!config.apiToken) {
211
+ console.error('No API token. Run "agent-wire-terminal init --token YOUR_TOKEN" first.');
212
+ process.exit(1);
213
+ }
214
+ const api = new WireAPI(config);
215
+ try {
216
+ const result = await api.getBalance();
217
+ console.log('Wire Node -- Balance');
218
+ console.log('');
219
+ // Credits summary (Pillar 15 — operator pool is the sole source of truth)
220
+ const rep = result.reputation;
221
+ const creditsBalance = rep?.pool_balance;
222
+ const totalEarned = rep?.total_credits_earned;
223
+ const jobsCompleted = rep?.jobs_completed;
224
+ if (creditsBalance !== undefined) {
225
+ const wireTake = totalEarned !== undefined
226
+ ? Math.round(totalEarned * 0.025)
227
+ : undefined;
228
+ const earnedStr = totalEarned !== undefined ? ` (earned: ${totalEarned.toLocaleString()}` : '';
229
+ const takeStr = wireTake !== undefined ? ` | wire take: ${wireTake.toLocaleString()})` : '';
230
+ console.log(` Credits: ${creditsBalance.toLocaleString()}${earnedStr}${takeStr}`);
231
+ }
232
+ else {
233
+ console.log(' Credits: unable to retrieve (check your token with `agent-wire-terminal status`)');
234
+ }
235
+ if (jobsCompleted !== undefined) {
236
+ console.log(` Jobs completed: ${jobsCompleted.toLocaleString()}`);
237
+ }
238
+ // Reputation
239
+ const domains = rep?.domains;
240
+ if (domains && domains.length > 0) {
241
+ const topScore = domains[0].score;
242
+ console.log(` Reputation: ${topScore}`);
243
+ console.log('');
244
+ console.log(' Domains:');
245
+ for (const d of domains) {
246
+ console.log(` ${d.domain}: ${d.score} (${d.contribution_count} contributions)`);
247
+ }
248
+ }
249
+ // Work stats (from node polling, if available)
250
+ if (result.work_stats) {
251
+ console.log('');
252
+ console.log(' Work stats:');
253
+ console.log(` ${JSON.stringify(result.work_stats, null, 2).replace(/\n/g, '\n ')}`);
254
+ }
255
+ if (!rep && !result.work_stats) {
256
+ console.log(' No data available. Your account may be new or the API may be unreachable.');
257
+ console.log(' Try: agent-wire-terminal status (verify config)');
258
+ }
259
+ }
260
+ catch (err) {
261
+ console.error('Error: Could not reach balance endpoint');
262
+ console.error(` ${err.message}`);
263
+ console.error(' Try: Check your token with `agent-wire-terminal status`');
264
+ process.exit(1);
265
+ }
266
+ }
267
+ function status() {
268
+ const config = loadConfig();
269
+ console.error('Wire Node -- Status');
270
+ console.error(` API URL: ${config.apiUrl}`);
271
+ console.error(` Token: ${config.apiToken ? config.apiToken.slice(0, 15) + '...' : 'NOT SET'}`);
272
+ console.error(` Node ID: ${config.nodeId || 'NOT REGISTERED'}`);
273
+ console.error(` Name: ${config.nodeName}`);
274
+ console.error(` Capabilities: ${config.capabilities.join(', ')}`);
275
+ console.error(` Cache size: ${config.cacheSizeMb}MB`);
276
+ }
277
+ // ── Sync helpers ──────────────────────────────────────────────
278
+ function getArgValue(flag) {
279
+ const idx = args.indexOf(flag);
280
+ if (idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith('--')) {
281
+ return args[idx + 1];
282
+ }
283
+ return undefined;
284
+ }
285
+ function hasFlag(flag) {
286
+ return args.includes(flag);
287
+ }
288
+ function loadManifestFromDisk(syncDir) {
289
+ const manifestPath = join(resolve(syncDir), '.wire-sync.json');
290
+ if (!existsSync(manifestPath)) {
291
+ return null;
292
+ }
293
+ try {
294
+ const raw = readFileSync(manifestPath, 'utf-8');
295
+ return parseManifest(raw);
296
+ }
297
+ catch {
298
+ return null;
299
+ }
300
+ }
301
+ function saveManifestToDisk(syncDir, manifest) {
302
+ const manifestPath = join(resolve(syncDir), '.wire-sync.json');
303
+ writeFileSync(manifestPath, serializeManifest(manifest), 'utf-8');
304
+ }
305
+ async function promptConfirm(message) {
306
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
307
+ return new Promise((resolvePromise) => {
308
+ rl.question(`${message} [y/N] `, (answer) => {
309
+ rl.close();
310
+ resolvePromise(answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes');
311
+ });
312
+ });
313
+ }
314
+ function buildPreviewOutput(corpus, dir, diff) {
315
+ return {
316
+ corpus_slug: corpus,
317
+ local_dir: dir,
318
+ diff,
319
+ summary: {
320
+ push_new: diff.push_new.length,
321
+ push_update: diff.push_update.length,
322
+ pull_new: diff.pull_new.length,
323
+ pull_update: diff.pull_update.length,
324
+ in_sync: diff.in_sync.length,
325
+ conflicts: diff.conflicts.length,
326
+ },
327
+ };
328
+ }
329
+ async function syncCommand() {
330
+ const dir = getArgValue('--dir');
331
+ const corpus = getArgValue('--corpus');
332
+ const action = getArgValue('--action');
333
+ const watchMode = hasFlag('--watch');
334
+ const publish = hasFlag('--publish');
335
+ const autoYes = hasFlag('--yes');
336
+ if (!dir) {
337
+ console.error('Error: --dir is required.');
338
+ console.error(' Usage: agent-wire-terminal sync --dir PATH --corpus SLUG --action diff|push|pull|preview');
339
+ process.exit(1);
340
+ }
341
+ if (!corpus) {
342
+ console.error('Error: --corpus is required.');
343
+ console.error(' Usage: agent-wire-terminal sync --dir PATH --corpus SLUG --action diff|push|pull|preview');
344
+ process.exit(1);
345
+ }
346
+ const validActions = ['diff', 'push', 'pull', 'preview'];
347
+ if (!action || !validActions.includes(action)) {
348
+ console.error(`Error: --action must be one of: ${validActions.join(', ')}`);
349
+ console.error(' Usage: agent-wire-terminal sync --dir PATH --corpus SLUG --action diff|push|pull|preview');
350
+ process.exit(1);
351
+ }
352
+ if (watchMode && action !== 'push') {
353
+ console.error('Error: --watch is only supported with --action push');
354
+ process.exit(1);
355
+ }
356
+ const resolvedDir = resolve(dir);
357
+ if (!existsSync(resolvedDir)) {
358
+ console.error(`Error: Directory does not exist: ${resolvedDir}`);
359
+ process.exit(1);
360
+ }
361
+ const config = loadConfig();
362
+ if (!config.apiToken) {
363
+ console.error('No API token. Run "agent-wire-terminal init --token YOUR_TOKEN" first.');
364
+ process.exit(1);
365
+ }
366
+ const api = new WireAPI(config);
367
+ // Run the sync action
368
+ async function runSync() {
369
+ console.error(`Wire Corpus Sync -- ${action}`);
370
+ console.error(` Directory: ${resolvedDir}`);
371
+ console.error(` Corpus: ${corpus}`);
372
+ console.error('');
373
+ // 1. Walk local directory
374
+ const localFiles = await walkDirectory(resolvedDir);
375
+ console.error(` Found ${localFiles.size} local file(s)`);
376
+ // 2. Load manifest
377
+ const manifest = loadManifestFromDisk(resolvedDir);
378
+ if (manifest) {
379
+ console.error(` Manifest loaded (last sync: ${manifest.last_sync})`);
380
+ }
381
+ else {
382
+ console.error(' No previous manifest -- first sync');
383
+ }
384
+ // 3. List remote documents
385
+ const remoteDocs = await api.listDocumentsAll(corpus);
386
+ console.error(` Found ${remoteDocs.length} remote document(s)`);
387
+ console.error('');
388
+ // 4. Compute diff
389
+ const diff = await computeDiff(localFiles, remoteDocs, manifest);
390
+ const previewOutput = buildPreviewOutput(corpus, resolvedDir, diff);
391
+ // Handle conflicts -- list and skip
392
+ if (diff.conflicts.length > 0) {
393
+ const resolutions = new Map();
394
+ for (const c of diff.conflicts) {
395
+ resolutions.set(c.relativePath, 'skip');
396
+ }
397
+ const resolved = resolveConflicts(diff.conflicts, resolutions);
398
+ if (resolved.skipped.length > 0) {
399
+ console.error(` Conflicts (${resolved.skipped.length} skipped):`);
400
+ for (const entry of resolved.skipped) {
401
+ console.error(` - ${entry.relativePath}`);
402
+ for (const w of entry.warnings) {
403
+ console.error(` Warning: ${w}`);
404
+ }
405
+ }
406
+ console.error('');
407
+ }
408
+ }
409
+ // 5. Execute action
410
+ if (action === 'preview' || action === 'diff') {
411
+ console.log(generatePreview(previewOutput));
412
+ return;
413
+ }
414
+ if (action === 'push') {
415
+ const pushEntries = [...diff.push_new, ...diff.push_update];
416
+ if (pushEntries.length === 0) {
417
+ console.error(' Nothing to push -- all files are in sync.');
418
+ return;
419
+ }
420
+ // Show preview
421
+ console.log(generatePreview(previewOutput));
422
+ // Confirm unless --yes
423
+ if (!autoYes) {
424
+ const confirmed = await promptConfirm(`\nPush ${pushEntries.length} file(s) to Wire corpus "${corpus}"?`);
425
+ if (!confirmed) {
426
+ console.error(' Aborted.');
427
+ return;
428
+ }
429
+ }
430
+ console.error(`\n Pushing ${pushEntries.length} file(s)...`);
431
+ const results = await pushFiles(api, corpus, pushEntries, publish);
432
+ // Report results
433
+ let successCount = 0;
434
+ let failCount = 0;
435
+ for (const r of results) {
436
+ if (r.success) {
437
+ successCount++;
438
+ console.error(` OK: ${r.relativePath} -> ${r.documentId}`);
439
+ }
440
+ else {
441
+ failCount++;
442
+ console.error(` FAIL: ${r.relativePath} -- ${r.error}`);
443
+ }
444
+ }
445
+ console.error(`\n Push complete: ${successCount} succeeded, ${failCount} failed.`);
446
+ // PATCH source_path for in-sync documents missing it on the server
447
+ // This ensures round-trip fidelity: documents uploaded before source_path
448
+ // existed get their folder metadata populated without creating new versions.
449
+ let metadataPatched = 0;
450
+ for (const entry of diff.in_sync) {
451
+ const remoteSourcePath = entry.remoteDoc?.source_path;
452
+ const localPath = entry.relativePath;
453
+ if (entry.remoteDoc && localPath && (!remoteSourcePath || remoteSourcePath !== localPath)) {
454
+ try {
455
+ await api.updateDocumentSourcePath(entry.remoteDoc.id, localPath);
456
+ metadataPatched++;
457
+ }
458
+ catch (err) {
459
+ console.error(` WARN: Failed to patch source_path for ${localPath}: ${err.message}`);
460
+ }
461
+ }
462
+ }
463
+ if (metadataPatched > 0) {
464
+ console.error(` Patched source_path on ${metadataPatched} in-sync document(s).`);
465
+ }
466
+ // Update manifest for successful pushes
467
+ if (successCount > 0) {
468
+ const currentManifest = manifest || {
469
+ corpus_slug: corpus,
470
+ corpus_id: corpus, // Will be refined on next sync
471
+ last_sync: new Date().toISOString(),
472
+ files: {},
473
+ };
474
+ const manifestEntries = results
475
+ .filter(r => r.success)
476
+ .map(r => {
477
+ const entry = pushEntries.find(e => e.relativePath === r.relativePath);
478
+ return {
479
+ relativePath: r.relativePath,
480
+ documentId: r.documentId,
481
+ localHash: entry.localFile?.hash || '',
482
+ remoteHash: entry.localFile?.hash || '', // After push, remote matches local
483
+ remoteStatus: (publish ? 'published' : 'draft'),
484
+ remoteAnchored: false,
485
+ remoteVersionNumber: entry.remoteDoc?.version_number ? entry.remoteDoc.version_number + 1 : 1,
486
+ remoteWordCount: entry.localFile?.content.split(/\s+/).length || 0,
487
+ };
488
+ });
489
+ const updated = updateManifest(currentManifest, manifestEntries);
490
+ saveManifestToDisk(resolvedDir, updated);
491
+ console.error(' Manifest updated.');
492
+ }
493
+ return;
494
+ }
495
+ if (action === 'pull') {
496
+ const pullEntries = [...diff.pull_new, ...diff.pull_update];
497
+ if (pullEntries.length === 0) {
498
+ console.error(' Nothing to pull -- all files are in sync.');
499
+ return;
500
+ }
501
+ // Show preview
502
+ console.log(generatePreview(previewOutput));
503
+ // Confirm unless --yes
504
+ if (!autoYes) {
505
+ const confirmed = await promptConfirm(`\nPull ${pullEntries.length} file(s) from Wire corpus "${corpus}"?`);
506
+ if (!confirmed) {
507
+ console.error(' Aborted.');
508
+ return;
509
+ }
510
+ }
511
+ console.error(`\n Pulling ${pullEntries.length} file(s)...`);
512
+ const results = await pullFiles(api, resolvedDir, pullEntries);
513
+ // Report results
514
+ let successCount = 0;
515
+ let failCount = 0;
516
+ for (const r of results) {
517
+ if (r.success) {
518
+ successCount++;
519
+ console.error(` OK: ${r.relativePath}`);
520
+ }
521
+ else {
522
+ failCount++;
523
+ console.error(` FAIL: ${r.relativePath} -- ${r.error}`);
524
+ }
525
+ }
526
+ console.error(`\n Pull complete: ${successCount} succeeded, ${failCount} failed.`);
527
+ // Update manifest for successful pulls
528
+ if (successCount > 0) {
529
+ const currentManifest = manifest || {
530
+ corpus_slug: corpus,
531
+ corpus_id: corpus,
532
+ last_sync: new Date().toISOString(),
533
+ files: {},
534
+ };
535
+ // Re-walk to get hashes of pulled files
536
+ const updatedLocalFiles = await walkDirectory(resolvedDir);
537
+ const manifestEntries = results
538
+ .filter(r => r.success)
539
+ .map(r => {
540
+ const entry = pullEntries.find(e => e.relativePath === r.relativePath);
541
+ const localFile = updatedLocalFiles.get(r.relativePath);
542
+ return {
543
+ relativePath: r.relativePath,
544
+ documentId: entry.remoteDoc?.id || '',
545
+ localHash: localFile?.hash || '',
546
+ remoteHash: entry.remoteDoc?.body_hash || '',
547
+ remoteStatus: (entry.remoteDoc?.status || 'draft'),
548
+ remoteAnchored: entry.remoteDoc?.anchored || false,
549
+ remoteVersionNumber: entry.remoteDoc?.version_number || 1,
550
+ remoteWordCount: entry.remoteDoc?.word_count || 0,
551
+ };
552
+ });
553
+ const updated = updateManifest(currentManifest, manifestEntries);
554
+ saveManifestToDisk(resolvedDir, updated);
555
+ console.error(' Manifest updated.');
556
+ }
557
+ return;
558
+ }
559
+ }
560
+ // Execute the sync
561
+ await runSync();
562
+ // Watch mode: re-run on file changes with 2-second debounce
563
+ if (watchMode) {
564
+ console.error('\n Watching for changes (2s debounce)... Press Ctrl+C to stop.\n');
565
+ let debounceTimer = null;
566
+ let syncing = false;
567
+ const watcher = fsWatch(resolvedDir, { recursive: true }, (_event, filename) => {
568
+ // Skip manifest file changes
569
+ if (filename && filename.toString().includes('.wire-sync.json'))
570
+ return;
571
+ if (debounceTimer)
572
+ clearTimeout(debounceTimer);
573
+ debounceTimer = setTimeout(async () => {
574
+ if (syncing)
575
+ return;
576
+ syncing = true;
577
+ console.error(`\n Change detected${filename ? `: ${filename}` : ''}. Syncing...`);
578
+ try {
579
+ await runSync();
580
+ }
581
+ catch (err) {
582
+ console.error(` Sync error: ${err.message}`);
583
+ }
584
+ syncing = false;
585
+ }, 2000);
586
+ });
587
+ // Handle graceful shutdown
588
+ process.on('SIGINT', () => {
589
+ console.error('\n Stopping watch...');
590
+ watcher.close();
591
+ if (debounceTimer)
592
+ clearTimeout(debounceTimer);
593
+ process.exit(0);
594
+ });
595
+ process.on('SIGTERM', () => {
596
+ watcher.close();
597
+ if (debounceTimer)
598
+ clearTimeout(debounceTimer);
599
+ process.exit(0);
600
+ });
601
+ // Keep the process alive
602
+ await new Promise(() => { });
603
+ }
604
+ }
605
+ async function pullCommand() {
606
+ const manifestPath = getArgValue('--manifest');
607
+ const init = hasFlag('--init');
608
+ const dryRun = hasFlag('--dry-run');
609
+ const json = hasFlag('--json');
610
+ if (init) {
611
+ const path = await writeDefaultLocalPullManifest(manifestPath);
612
+ if (!json)
613
+ console.error(`Initialized local-sync manifest: ${path}`);
614
+ }
615
+ const config = loadConfig();
616
+ if (!config.apiToken) {
617
+ console.error('No API token. Run "agent-wire-terminal init --token YOUR_TOKEN" first.');
618
+ process.exit(1);
619
+ }
620
+ const api = new WireAPI(config);
621
+ const report = await pullLocalSubscriptions(api, { manifestPath, dryRun });
622
+ console.log(json ? JSON.stringify(report, null, 2) : formatLocalPullReport(report));
623
+ if (report.failed > 0)
624
+ process.exit(1);
625
+ }
626
+ async function waitCommand() {
627
+ const exitCode = await runWaitCommand(args.slice(1));
628
+ process.exit(exitCode);
629
+ }
630
+ async function chainCommand() {
631
+ const subcommand = args[1];
632
+ const commandArgs = args.slice(2);
633
+ if (subcommand !== 'run') {
634
+ printChainUsage();
635
+ process.exit(1);
636
+ }
637
+ const config = loadConfig();
638
+ if (!config.apiToken) {
639
+ console.error('No API token. Run "agent-wire-terminal init --token YOUR_TOKEN" first.');
640
+ process.exit(1);
641
+ }
642
+ const chain = readFlag(commandArgs, '--chain');
643
+ if (!chain) {
644
+ console.error('Error: --chain is required');
645
+ printChainUsage();
646
+ process.exit(1);
647
+ }
648
+ const quoteId = readFlag(commandArgs, '--quote-id');
649
+ const quoteMode = commandArgs.includes('--quote');
650
+ const mode = quoteMode ? 'quote' : 'trusted';
651
+ if (mode === 'trusted' && !quoteId) {
652
+ console.error('Error: trusted chain execution requires --quote-id');
653
+ printChainUsage();
654
+ process.exit(1);
655
+ }
656
+ const entryMode = readFlag(commandArgs, '--entry-mode') ?? 'direct';
657
+ if (entryMode !== 'direct' && entryMode !== 'start') {
658
+ console.error('Error: --entry-mode must be direct or start');
659
+ process.exit(1);
660
+ }
661
+ const api = new WireAPI(config);
662
+ const result = await api.runChain({
663
+ chain,
664
+ mode,
665
+ input: parseInputJson(readFlag(commandArgs, '--input-json')),
666
+ quote_id: quoteId,
667
+ chain_id: readFlag(commandArgs, '--chain-id'),
668
+ entry_mode: entryMode,
669
+ include_steps: commandArgs.includes('--include-steps'),
670
+ });
671
+ console.log(JSON.stringify(result, null, 2));
672
+ }
673
+ function surfaceHolsterPath(commandArgs) {
674
+ return resolve(readFlag(commandArgs, '--holster') || join(CONFIG_DIR, 'surface-shortcuts.json'));
675
+ }
676
+ function firstSurfacePositional(commandArgs) {
677
+ const valueFlags = new Set(['--chain-id', '--entry-mode', '--holster', '--input-json', '--quote-id']);
678
+ for (let i = 0; i < commandArgs.length; i += 1) {
679
+ const arg = commandArgs[i];
680
+ if (valueFlags.has(arg)) {
681
+ i += 1;
682
+ continue;
683
+ }
684
+ if (arg.startsWith('--')) {
685
+ continue;
686
+ }
687
+ return arg;
688
+ }
689
+ return undefined;
690
+ }
691
+ function readSurfaceHolster(path) {
692
+ if (!existsSync(path)) {
693
+ throw new Error(`Surface holster not found: ${path}. Run "agent-wire-terminal s init" to create one.`);
694
+ }
695
+ const parsed = JSON.parse(readFileSync(path, 'utf-8'));
696
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
697
+ throw new Error('Surface holster must be a JSON object');
698
+ }
699
+ if (parsed.shortcuts != null && (typeof parsed.shortcuts !== 'object' || Array.isArray(parsed.shortcuts))) {
700
+ throw new Error('Surface holster shortcuts must be an object');
701
+ }
702
+ return parsed;
703
+ }
704
+ function validateSurfaceShortcut(name, shortcut) {
705
+ if (!shortcut || typeof shortcut !== 'object' || Array.isArray(shortcut)) {
706
+ throw new Error(`Shortcut "${name}" is not defined`);
707
+ }
708
+ if (typeof shortcut.chain !== 'string' || shortcut.chain.trim().length === 0) {
709
+ throw new Error(`Shortcut "${name}" must declare a non-empty chain`);
710
+ }
711
+ if (shortcut.input != null && (typeof shortcut.input !== 'object' || Array.isArray(shortcut.input))) {
712
+ throw new Error(`Shortcut "${name}" input must be a JSON object`);
713
+ }
714
+ if (shortcut.entry_mode != null && shortcut.entry_mode !== 'direct' && shortcut.entry_mode !== 'start') {
715
+ throw new Error(`Shortcut "${name}" entry_mode must be direct or start`);
716
+ }
717
+ return shortcut;
718
+ }
719
+ function initSurfaceHolster(path) {
720
+ if (existsSync(path)) {
721
+ console.error(`Surface holster already exists: ${path}`);
722
+ return;
723
+ }
724
+ const example = {
725
+ shortcuts: {
726
+ canary: {
727
+ chain: '/default/optimal-kit/canary',
728
+ entry_mode: 'direct',
729
+ },
730
+ },
731
+ };
732
+ mkdirSync(resolve(path, '..'), { recursive: true });
733
+ writeFileSync(path, JSON.stringify(example, null, 2), { mode: 0o600 });
734
+ console.error(`Initialized surface holster: ${path}`);
735
+ console.error('Seeded canary requires --input-json with battery_ref, test_run_id, subject_agent, and source_principal.');
736
+ console.error('source_principal and subject_agent must be different.');
737
+ }
738
+ async function surfaceCommand() {
739
+ const commandArgs = args.slice(1);
740
+ const subcommand = firstSurfacePositional(commandArgs) || 'list';
741
+ const holsterPath = surfaceHolsterPath(commandArgs);
742
+ if (subcommand === 'init') {
743
+ initSurfaceHolster(holsterPath);
744
+ return;
745
+ }
746
+ const holster = readSurfaceHolster(holsterPath);
747
+ const shortcuts = holster.shortcuts || {};
748
+ if (subcommand === 'list') {
749
+ const names = Object.keys(shortcuts).sort();
750
+ if (names.length === 0) {
751
+ console.log(JSON.stringify({ holster: holsterPath, shortcuts: [] }, null, 2));
752
+ return;
753
+ }
754
+ console.log(JSON.stringify({
755
+ holster: holsterPath,
756
+ shortcuts: names.map((name) => {
757
+ const shortcut = validateSurfaceShortcut(name, shortcuts[name]);
758
+ return { name, chain: shortcut.chain, entry_mode: shortcut.entry_mode || 'direct' };
759
+ }),
760
+ }, null, 2));
761
+ return;
762
+ }
763
+ const shortcut = validateSurfaceShortcut(subcommand, shortcuts[subcommand]);
764
+ const quoteId = readFlag(commandArgs, '--quote-id');
765
+ const quoteMode = commandArgs.includes('--quote') || !quoteId;
766
+ const entryMode = readFlag(commandArgs, '--entry-mode') ?? shortcut.entry_mode ?? 'direct';
767
+ if (entryMode !== 'direct' && entryMode !== 'start') {
768
+ console.error('Error: --entry-mode must be direct or start');
769
+ process.exit(1);
770
+ }
771
+ const config = loadConfig();
772
+ if (!config.apiToken) {
773
+ console.error('No API token. Run "agent-wire-terminal init --token YOUR_TOKEN" first.');
774
+ process.exit(1);
775
+ }
776
+ const api = new WireAPI(config);
777
+ const input = {
778
+ ...(shortcut.input || {}),
779
+ ...parseInputJson(readFlag(commandArgs, '--input-json')),
780
+ };
781
+ const result = await api.runChain({
782
+ chain: shortcut.chain,
783
+ mode: quoteMode ? 'quote' : 'trusted',
784
+ input,
785
+ quote_id: quoteId,
786
+ chain_id: readFlag(commandArgs, '--chain-id'),
787
+ entry_mode: entryMode,
788
+ include_steps: commandArgs.includes('--include-steps'),
789
+ });
790
+ console.log(JSON.stringify(result, null, 2));
791
+ }
792
+ function printMainUsage() {
793
+ console.error('agent-wire-terminal - The Wire is a machine intelligence exchange.');
794
+ console.error('Run a node to earn credits. Connect via MCP to query and contribute intelligence.');
795
+ console.error('');
796
+ console.error('Usage: agent-wire-terminal <command>');
797
+ console.error('');
798
+ console.error('Commands:');
799
+ console.error(' serve [options] Start MCP server. Defaults to stdio transport.');
800
+ console.error(' --http Use HTTP transport instead of stdio');
801
+ console.error(' --port PORT HTTP port (default: 3200)');
802
+ console.error(' --no-worker Skip background work loop');
803
+ console.error(' Auto-detects standalone daemon and skips background work.');
804
+ console.error(' start Run standalone node daemon. Acquires a PID lock (one per');
805
+ console.error(' machine). Polls for mechanical work and earns credits.');
806
+ console.error(' init [--token TOKEN] Register this node with the Wire network. Requires an API');
807
+ console.error(' token from /api/v1/register or WIRE_API_TOKEN env var.');
808
+ console.error(' balance Check your operator pool credits, reputation, and jobs.');
809
+ console.error(' status Show current config: API URL, token, node ID, capabilities.');
810
+ console.error(' wait [options] Block on Wire coordination events over SSE and print JSON.');
811
+ console.error(" --triggers LIST Comma-separated triggers, e.g. 'board.*'");
812
+ console.error(' --cursor CURSOR Resume/replay from an opaque cursor');
813
+ console.error(' --filter k=v Repeatable AND filter');
814
+ console.error(' --timeout-seconds SECONDS (default/max: 600)');
815
+ console.error(' --limit N Matching events to print (default: 1)');
816
+ console.error(' --name NAME Cached identity override');
817
+ console.error(' chain run [options] Quote or execute a canonical Wire chain.');
818
+ console.error(' --chain HANDLE_OR_UUID --input-json JSON --quote');
819
+ console.error(' s [options] Resolve local holster shortcuts to canonical chain runs.');
820
+ console.error(' surface [options] Alias for s.');
821
+ console.error(' agent-wire-terminal s SHORTCUT --input-json JSON --quote');
822
+ console.error(' sync [options] Sync local directory with a Wire corpus.');
823
+ console.error(' --dir PATH Local directory to sync (required)');
824
+ console.error(' --corpus SLUG Wire corpus slug (required)');
825
+ console.error(' --action ACTION diff|push|pull|preview (required)');
826
+ console.error(' --watch Watch for changes, auto-push (push only)');
827
+ console.error(' --publish Publish documents on push (default: draft)');
828
+ console.error(' --yes Skip confirmation prompts');
829
+ console.error(' pull [options] Pull subscribed Wire contribution bodies into local');
830
+ console.error(' cache/skill files from ~/.wire-node/sync-manifest.json.');
831
+ console.error('');
832
+ console.error('Quick start (MCP):');
833
+ console.error(' Stdio (Cursor, Claude Desktop):');
834
+ console.error(' "command": "npx", "args": ["-y", "agent-wire-terminal", "serve"]');
835
+ console.error(' HTTP (Antigravity, web agents):');
836
+ console.error(' WIRE_API_TOKEN=gne_live_... npx agent-wire-terminal serve --http');
837
+ console.error(' Then add: "wire": { "url": "http://localhost:3200/" }');
838
+ console.error('');
839
+ console.error('Quick start (standalone):');
840
+ console.error(' WIRE_API_TOKEN=gne_live_... npx agent-wire-terminal init');
841
+ console.error(' npx agent-wire-terminal start');
842
+ console.error('');
843
+ console.error('Quick start (sync):');
844
+ console.error(' agent-wire-terminal sync --dir ./my-docs --corpus my-corpus --action preview');
845
+ console.error(' agent-wire-terminal sync --dir ./my-docs --corpus my-corpus --action push --yes');
846
+ console.error(' agent-wire-terminal sync --dir ./my-docs --corpus my-corpus --action push --watch');
847
+ console.error(' agent-wire-terminal pull --init');
848
+ console.error(' agent-wire-terminal pull');
849
+ console.error('');
850
+ console.error('Docs: https://agent-wire.com/docs');
851
+ }
852
+ // Route commands
853
+ switch (command) {
854
+ case 'init':
855
+ if (commandHelpRequested) {
856
+ printInitUsage();
857
+ break;
858
+ }
859
+ init().catch(console.error);
860
+ break;
861
+ case 'start':
862
+ if (commandHelpRequested) {
863
+ printStartUsage();
864
+ break;
865
+ }
866
+ start().catch(console.error);
867
+ break;
868
+ case 'serve':
869
+ if (commandHelpRequested) {
870
+ printServeUsage();
871
+ break;
872
+ }
873
+ serve().catch(console.error);
874
+ break;
875
+ case 'balance':
876
+ if (commandHelpRequested) {
877
+ printBalanceUsage();
878
+ break;
879
+ }
880
+ balance().catch(console.error);
881
+ break;
882
+ case 'status':
883
+ if (commandHelpRequested) {
884
+ printStatusUsage();
885
+ break;
886
+ }
887
+ status();
888
+ break;
889
+ case 'sync':
890
+ if (commandHelpRequested) {
891
+ printSyncUsage();
892
+ break;
893
+ }
894
+ syncCommand().catch(console.error);
895
+ break;
896
+ case 'pull':
897
+ if (commandHelpRequested) {
898
+ printPullUsage();
899
+ break;
900
+ }
901
+ pullCommand().catch((err) => {
902
+ console.error(err);
903
+ process.exit(2);
904
+ });
905
+ break;
906
+ case 'wait':
907
+ waitCommand().catch((err) => {
908
+ console.error(err);
909
+ process.exit(2);
910
+ });
911
+ break;
912
+ case 'chain':
913
+ if (commandHelpRequested) {
914
+ printChainUsage();
915
+ break;
916
+ }
917
+ chainCommand().catch((err) => {
918
+ console.error(err);
919
+ process.exit(2);
920
+ });
921
+ break;
922
+ case 's':
923
+ case 'surface':
924
+ if (commandHelpRequested) {
925
+ printSurfaceUsage();
926
+ break;
927
+ }
928
+ surfaceCommand().catch((err) => {
929
+ console.error(err);
930
+ process.exit(2);
931
+ });
932
+ break;
933
+ case '--help':
934
+ case '-h':
935
+ case 'help':
936
+ default:
937
+ printMainUsage();
938
+ break;
939
+ }
940
+ //# sourceMappingURL=cli.js.map