@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.
- package/README.md +353 -0
- package/dist/api.d.ts +660 -0
- package/dist/api.js +1157 -0
- package/dist/api.js.map +1 -0
- package/dist/background-worker.d.ts +6 -0
- package/dist/background-worker.js +101 -0
- package/dist/background-worker.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +940 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +36 -0
- package/dist/config.js.map +1 -0
- package/dist/help-pointer.d.ts +11 -0
- package/dist/help-pointer.js +10 -0
- package/dist/help-pointer.js.map +1 -0
- package/dist/identity.d.ts +40 -0
- package/dist/identity.js +193 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/local-pull.d.ts +45 -0
- package/dist/local-pull.js +173 -0
- package/dist/local-pull.js.map +1 -0
- package/dist/lockfile.d.ts +16 -0
- package/dist/lockfile.js +78 -0
- package/dist/lockfile.js.map +1 -0
- package/dist/mcp-server.d.ts +43 -0
- package/dist/mcp-server.js +3159 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/response-shaper.d.ts +60 -0
- package/dist/response-shaper.js +893 -0
- package/dist/response-shaper.js.map +1 -0
- package/dist/slots.d.ts +65 -0
- package/dist/slots.js +128 -0
- package/dist/slots.js.map +1 -0
- package/dist/sync/diff.d.ts +17 -0
- package/dist/sync/diff.js +152 -0
- package/dist/sync/diff.js.map +1 -0
- package/dist/sync/hash.d.ts +11 -0
- package/dist/sync/hash.js +18 -0
- package/dist/sync/hash.js.map +1 -0
- package/dist/sync/index.d.ts +24 -0
- package/dist/sync/index.js +22 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/manifest.d.ts +29 -0
- package/dist/sync/manifest.js +59 -0
- package/dist/sync/manifest.js.map +1 -0
- package/dist/sync/match.d.ts +29 -0
- package/dist/sync/match.js +153 -0
- package/dist/sync/match.js.map +1 -0
- package/dist/sync/paginate.d.ts +16 -0
- package/dist/sync/paginate.js +34 -0
- package/dist/sync/paginate.js.map +1 -0
- package/dist/sync/preview.d.ts +16 -0
- package/dist/sync/preview.js +179 -0
- package/dist/sync/preview.js.map +1 -0
- package/dist/sync/pull.d.ts +23 -0
- package/dist/sync/pull.js +126 -0
- package/dist/sync/pull.js.map +1 -0
- package/dist/sync/push.d.ts +27 -0
- package/dist/sync/push.js +116 -0
- package/dist/sync/push.js.map +1 -0
- package/dist/sync/resolve.d.ts +19 -0
- package/dist/sync/resolve.js +42 -0
- package/dist/sync/resolve.js.map +1 -0
- package/dist/sync/types.d.ts +70 -0
- package/dist/sync/types.js +6 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/sync/walk.d.ts +12 -0
- package/dist/sync/walk.js +155 -0
- package/dist/sync/walk.js.map +1 -0
- package/dist/wait-cli.d.ts +46 -0
- package/dist/wait-cli.js +280 -0
- package/dist/wait-cli.js.map +1 -0
- package/dist/worker.d.ts +14 -0
- package/dist/worker.js +252 -0
- package/dist/worker.js.map +1 -0
- 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
|