@claw-link/gateway-host 0.2.9 → 0.2.11
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/bin/cli.js +22 -0
- package/package.json +1 -1
- package/scripts/install.js +113 -3
package/bin/cli.js
CHANGED
|
@@ -48,6 +48,24 @@ async function main() {
|
|
|
48
48
|
case 'status':
|
|
49
49
|
return require('../scripts/install').status();
|
|
50
50
|
|
|
51
|
+
case 'version':
|
|
52
|
+
case '--version':
|
|
53
|
+
case '-v':
|
|
54
|
+
return require('../scripts/install').versionCmd();
|
|
55
|
+
|
|
56
|
+
case 'doctor':
|
|
57
|
+
case 'diagnose':
|
|
58
|
+
case 'check':
|
|
59
|
+
return require('../scripts/install').doctor();
|
|
60
|
+
|
|
61
|
+
case 'logs':
|
|
62
|
+
case 'log':
|
|
63
|
+
return require('../scripts/install').logs();
|
|
64
|
+
|
|
65
|
+
case 'update':
|
|
66
|
+
case 'upgrade':
|
|
67
|
+
return require('../scripts/install').update();
|
|
68
|
+
|
|
51
69
|
case 'transport-test':
|
|
52
70
|
case 'transport':
|
|
53
71
|
return require('../scripts/transport-test').run();
|
|
@@ -76,10 +94,14 @@ async function main() {
|
|
|
76
94
|
' retoken Re-enter a new Host Token for an existing agent (clhost retoken <id>)',
|
|
77
95
|
' agents List mapped agents',
|
|
78
96
|
' status Show bridge, service state, and mapped agents',
|
|
97
|
+
' doctor Diagnose config, runtime binaries, bridge + token/machine binding',
|
|
98
|
+
' version Show CLI version + the version the service is running',
|
|
99
|
+
' logs Tail the background service log (clhost logs [N])',
|
|
79
100
|
' transport-test Verify content-addressed Capsule relay (host ⇄ central sync + integrity)',
|
|
80
101
|
' start Start the background service',
|
|
81
102
|
' stop Stop the background service',
|
|
82
103
|
' restart Restart the background service (apply config changes)',
|
|
104
|
+
' update Refresh the background service to this CLI’s version',
|
|
83
105
|
' setup install + add-agent in one flow',
|
|
84
106
|
' rotate How to rotate a Host Token / move to a new machine',
|
|
85
107
|
' uninstall Stop + remove the background service',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claw-link/gateway-host",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "ClawLink Host Gateway — a secure, outbound-only worker that bridges a local agent CLI (OpenClaw, Hermes, Claude, Codex, Cursor) to your ClawLink agents. No inbound ports; authenticated per-agent by a Host Token.",
|
|
5
5
|
"homepage": "https://claw-link.co",
|
|
6
6
|
"bin": {
|
package/scripts/install.js
CHANGED
|
@@ -96,12 +96,27 @@ function saveAgent(cfg, agent) {
|
|
|
96
96
|
|
|
97
97
|
// `setup` — install the service (if needed) + add one or more agents.
|
|
98
98
|
async function run() {
|
|
99
|
-
console.log('\n ClawLink Host Gateway — setup\n ─────────────────────────────\n');
|
|
100
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
101
99
|
const cfg = config.load() || config.defaultConfig();
|
|
102
100
|
ensureBridgeUrl(cfg);
|
|
103
101
|
config.save(cfg);
|
|
104
102
|
|
|
103
|
+
// Backward-compat: a bare `npx @claw-link/gateway-host@latest` on an ALREADY-configured
|
|
104
|
+
// host is an UPDATE, not first-run setup — don't re-prompt for agents. Refresh the
|
|
105
|
+
// background service so the new version takes effect, and show status.
|
|
106
|
+
const existing = (cfg.agents || []).length;
|
|
107
|
+
if (existing > 0) {
|
|
108
|
+
console.log(`\n ClawLink Host Gateway v${VERSION} — already set up (${existing} agent${existing > 1 ? 's' : ''}).`);
|
|
109
|
+
const installed = installService();
|
|
110
|
+
console.log(installed
|
|
111
|
+
? ' ✓ Service refreshed to the latest version.'
|
|
112
|
+
: ' ⚠ Could not refresh the service automatically — run: clhost restart');
|
|
113
|
+
console.log('\n Commands: clhost status · clhost agents · clhost add-agent · clhost transport-test\n');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log('\n ClawLink Host Gateway — setup\n ─────────────────────────────\n');
|
|
118
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
119
|
+
|
|
105
120
|
let added = 0, more = true;
|
|
106
121
|
while (more) {
|
|
107
122
|
console.log('\n Add an agent:');
|
|
@@ -258,11 +273,37 @@ function serviceControlCmd(action) {
|
|
|
258
273
|
}
|
|
259
274
|
|
|
260
275
|
function nodeBin() { return process.execPath; }
|
|
261
|
-
function
|
|
276
|
+
function pkgRoot() { return path.join(__dirname, '..'); }
|
|
277
|
+
function appDir() { return path.join(config.HOME_DIR, 'app'); }
|
|
278
|
+
|
|
279
|
+
// The background service must run from a STABLE path — not the version-pinned, GC-able npx
|
|
280
|
+
// cache (`__dirname`), which is why republishing never changed the running version. Copy the
|
|
281
|
+
// currently-running package into ~/.clawlink-host/app on every install/update so a newer
|
|
282
|
+
// `npx @claw-link/gateway-host@latest` actually swaps the code the service runs.
|
|
283
|
+
function syncAppDir() {
|
|
284
|
+
const src = pkgRoot();
|
|
285
|
+
const dest = appDir();
|
|
286
|
+
if (path.resolve(src) === path.resolve(dest)) return; // already running from the stable dir
|
|
287
|
+
try {
|
|
288
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
289
|
+
fs.cpSync(src, dest, {
|
|
290
|
+
recursive: true,
|
|
291
|
+
filter: (s) => { const b = path.basename(s); return b !== 'node_modules' && b !== '.git'; },
|
|
292
|
+
});
|
|
293
|
+
try { fs.chmodSync(path.join(dest, 'bin', 'cli.js'), 0o755); } catch { /* best effort */ }
|
|
294
|
+
} catch { /* non-fatal — cliEntry() falls back to the current path */ }
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function cliEntry() {
|
|
298
|
+
const stable = path.join(appDir(), 'bin', 'cli.js');
|
|
299
|
+
try { if (fs.existsSync(stable)) return stable; } catch { /* */ }
|
|
300
|
+
return path.join(pkgRoot(), 'bin', 'cli.js'); // fallback if the stable copy failed
|
|
301
|
+
}
|
|
262
302
|
|
|
263
303
|
function installService() {
|
|
264
304
|
const platform = process.platform;
|
|
265
305
|
try {
|
|
306
|
+
syncAppDir(); // refresh the stable app dir so the service runs the just-updated version
|
|
266
307
|
if (platform === 'darwin') {
|
|
267
308
|
const plistDir = path.join(os.homedir(), 'Library', 'LaunchAgents');
|
|
268
309
|
fs.mkdirSync(plistDir, { recursive: true });
|
|
@@ -383,7 +424,76 @@ function status() {
|
|
|
383
424
|
printAgents(cfg);
|
|
384
425
|
}
|
|
385
426
|
|
|
427
|
+
// `version` — what the CLI is vs what the background service actually runs (these can
|
|
428
|
+
// differ: `npm i -g` updates the CLI; `npx ...@latest` / `clhost update` updates the service).
|
|
429
|
+
function versionCmd() {
|
|
430
|
+
console.log(`@claw-link/gateway-host (clhost) v${VERSION}`);
|
|
431
|
+
console.log(` CLI runs from: ${pkgRoot()}`);
|
|
432
|
+
let svcV = '(service not installed)';
|
|
433
|
+
try { const p = path.join(appDir(), 'package.json'); if (fs.existsSync(p)) svcV = 'v' + require(p).version; } catch { /* */ }
|
|
434
|
+
console.log(` Service runs: ${svcV} ${serviceRunning() ? '● running' : '○ stopped'} (${appDir()})`);
|
|
435
|
+
if (svcV !== '(service not installed)' && svcV !== 'v' + VERSION) {
|
|
436
|
+
console.log(` ⚠ Service is on ${svcV} but this CLI is v${VERSION} — run \`clhost update\` to sync the service.`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// `doctor` — one-shot health check covering every failure we’ve actually hit:
|
|
441
|
+
// config present, service up, each agent’s runtime binary installed, and the bridge
|
|
442
|
+
// reachable + token/machine-binding valid (via a live heartbeat).
|
|
443
|
+
async function doctor() {
|
|
444
|
+
const mark = (b) => (b ? '✓' : '✗');
|
|
445
|
+
console.log('\n ClawLink Host Gateway — doctor\n ──────────────────────────────');
|
|
446
|
+
const cfg = config.load();
|
|
447
|
+
console.log(` ${mark(!!cfg)} config ${cfg ? config.CONFIG_PATH : 'missing — run: clhost setup'}`);
|
|
448
|
+
if (!cfg) return;
|
|
449
|
+
versionCmd();
|
|
450
|
+
console.log(` ${mark(serviceRunning())} service ${serviceRunning() ? 'running' : 'stopped — run: clhost start'}`);
|
|
451
|
+
|
|
452
|
+
const { which, RUNTIME_BINARIES } = require('../src/detect');
|
|
453
|
+
const { Bridge } = require('../src/bridge');
|
|
454
|
+
const agents = cfg.agents || [];
|
|
455
|
+
if (!agents.length) console.log(' ✗ agents none mapped — run: clhost add-agent');
|
|
456
|
+
for (const a of agents) {
|
|
457
|
+
const id = String(a.agent_id || a.host_token || '????').slice(0, 8);
|
|
458
|
+
const bins = RUNTIME_BINARIES[a.runtime] || [];
|
|
459
|
+
const usesLocalGateway = a.runtime === 'openclaw' || !!a.local_gateway_url;
|
|
460
|
+
const binOk = usesLocalGateway || bins.length === 0 || bins.some((b) => which(b));
|
|
461
|
+
let beat = false, msg = '';
|
|
462
|
+
try {
|
|
463
|
+
const r = await new Bridge(cfg.bridge_url, a, cfg.instance_id).heartbeat();
|
|
464
|
+
beat = !!(r && r.ok !== false && !r.error);
|
|
465
|
+
if (!beat) msg = (r && r.error) ? String(r.error) : 'rejected';
|
|
466
|
+
} catch (e) { msg = e && e.message ? e.message : String(e); }
|
|
467
|
+
console.log(` ${mark(binOk && beat)} agent ${id} runtime=${a.runtime}`
|
|
468
|
+
+ (binOk ? '' : ` ✗ binary not found (${bins.join('/')})`)
|
|
469
|
+
+ (beat ? ' · bridge+token+binding OK' : ` ✗ bridge: ${msg}`));
|
|
470
|
+
}
|
|
471
|
+
console.log('\n Next: `clhost transport-test` (verify the content-addressed relay)\n');
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// `logs` — tail the background service log.
|
|
475
|
+
function logs() {
|
|
476
|
+
const logPath = path.join(config.HOME_DIR, 'host.log');
|
|
477
|
+
if (!fs.existsSync(logPath)) { console.log(` No log yet at ${logPath} (start the service first).`); return; }
|
|
478
|
+
const n = Number(process.argv[3]) || 60;
|
|
479
|
+
const lines = fs.readFileSync(logPath, 'utf8').split('\n');
|
|
480
|
+
console.log(lines.slice(-n).join('\n'));
|
|
481
|
+
console.log(`\n ── last ${n} lines of ${logPath} ──`);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// `update` — refresh the background service to the version of THIS CLI (syncs the stable
|
|
485
|
+
// app dir + restarts). To update the CLI itself: npm i -g @claw-link/gateway-host@latest.
|
|
486
|
+
function update() {
|
|
487
|
+
console.log(` Refreshing the background service to v${VERSION}…`);
|
|
488
|
+
const ok = installService();
|
|
489
|
+
console.log(ok
|
|
490
|
+
? ` ✓ Service now runs v${VERSION} from ${appDir()}.`
|
|
491
|
+
: ' ⚠ Could not refresh automatically — run: clhost restart');
|
|
492
|
+
console.log(' (To upgrade the `clhost` CLI you type: npm i -g @claw-link/gateway-host@latest)\n');
|
|
493
|
+
}
|
|
494
|
+
|
|
386
495
|
module.exports = {
|
|
387
496
|
run, installOnly, addAgentCmd, removeAgentCmd, retokenCmd, listAgents, status,
|
|
388
497
|
serviceControl, serviceControlCmd, installService, SERVICE_LABEL,
|
|
498
|
+
versionCmd, doctor, logs, update,
|
|
389
499
|
};
|