troxy-cli 1.6.0 → 1.7.1
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/troxy.js +38 -10
- package/package.json +1 -1
- package/src/daemon.js +33 -0
- package/src/init.js +3 -3
package/bin/troxy.js
CHANGED
|
@@ -147,6 +147,13 @@ switch (command) {
|
|
|
147
147
|
await runMcp();
|
|
148
148
|
break;
|
|
149
149
|
|
|
150
|
+
// ── Heartbeat daemon (background service) ─────────────────────
|
|
151
|
+
case 'daemon': {
|
|
152
|
+
const { runDaemon } = await import('../src/daemon.js');
|
|
153
|
+
await runDaemon();
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
|
|
150
157
|
// ── Pause / resume payment evaluations ───────────────────────
|
|
151
158
|
case 'pause':
|
|
152
159
|
await runPause();
|
|
@@ -358,21 +365,42 @@ switch (command) {
|
|
|
358
365
|
// ── Restart MCP service ───────────────────────────────────────
|
|
359
366
|
case 'restart': {
|
|
360
367
|
const { execSync: _execSyncRestart } = await import('child_process');
|
|
361
|
-
const
|
|
362
|
-
|
|
368
|
+
const fsR = await import('fs');
|
|
369
|
+
const osR = (await import('os')).default;
|
|
370
|
+
console.log('\n Restarting Troxy service...');
|
|
363
371
|
try {
|
|
364
|
-
if (
|
|
372
|
+
if (osR.platform() === 'linux') {
|
|
373
|
+
// Migrate service file from old "troxy mcp" to "troxy daemon" if needed
|
|
374
|
+
const svcPath = '/etc/systemd/system/troxy-mcp.service';
|
|
375
|
+
if (fsR.existsSync(svcPath)) {
|
|
376
|
+
const svc = fsR.readFileSync(svcPath, 'utf8');
|
|
377
|
+
if (svc.includes('ExecStart=') && svc.includes(' mcp') && !svc.includes(' daemon')) {
|
|
378
|
+
const fixed = svc.replace(/(ExecStart=\S+) mcp/, '$1 daemon');
|
|
379
|
+
fsR.writeFileSync('/tmp/troxy-mcp.service', fixed);
|
|
380
|
+
_execSyncRestart('sudo mv /tmp/troxy-mcp.service /etc/systemd/system/troxy-mcp.service');
|
|
381
|
+
_execSyncRestart('sudo systemctl daemon-reload');
|
|
382
|
+
process.stdout.write(' Migrated service to daemon mode ✓\n');
|
|
383
|
+
}
|
|
384
|
+
}
|
|
365
385
|
_execSyncRestart('sudo systemctl restart troxy-mcp', { stdio: 'inherit' });
|
|
366
|
-
} else if (
|
|
367
|
-
const
|
|
368
|
-
|
|
386
|
+
} else if (osR.platform() === 'darwin') {
|
|
387
|
+
const plistPath = osR.homedir() + '/Library/LaunchAgents/ai.troxy.mcp.plist';
|
|
388
|
+
if (fsR.existsSync(plistPath)) {
|
|
389
|
+
const plist = fsR.readFileSync(plistPath, 'utf8');
|
|
390
|
+
if (plist.includes('<string>mcp</string>')) {
|
|
391
|
+
const fixed = plist.replace('<string>mcp</string>', '<string>daemon</string>');
|
|
392
|
+
fsR.writeFileSync(plistPath, fixed);
|
|
393
|
+
process.stdout.write(' Migrated service to daemon mode ✓\n');
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
_execSyncRestart(`launchctl unload ${plistPath} 2>/dev/null; launchctl load ${plistPath}`, { shell: true, stdio: 'inherit' });
|
|
369
397
|
} else {
|
|
370
|
-
console.error('\n Restart is only supported on Linux and macOS.\n
|
|
398
|
+
console.error('\n Restart is only supported on Linux and macOS.\n');
|
|
371
399
|
process.exit(1);
|
|
372
400
|
}
|
|
373
|
-
console.log('
|
|
374
|
-
} catch {
|
|
375
|
-
console.error('\n Could not restart service. Try manually:\n Linux: sudo systemctl restart troxy-mcp\n macOS: launchctl unload/load ~/Library/LaunchAgents/
|
|
401
|
+
console.log(' Service restarted ✓\n');
|
|
402
|
+
} catch (e) {
|
|
403
|
+
console.error('\n Could not restart service. Try manually:\n Linux: sudo systemctl restart troxy-mcp\n macOS: launchctl unload/load ~/Library/LaunchAgents/ai.troxy.mcp.plist\n');
|
|
376
404
|
process.exit(1);
|
|
377
405
|
}
|
|
378
406
|
break;
|
package/package.json
CHANGED
package/src/daemon.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Troxy heartbeat daemon.
|
|
3
|
+
* Runs as a background service (systemd / launchd). Sends a heartbeat to the
|
|
4
|
+
* Troxy API every 60 seconds so the dashboard shows this agent as connected.
|
|
5
|
+
* Does NOT start an MCP stdio server — that only makes sense when an MCP client
|
|
6
|
+
* (Claude Desktop, Cursor, etc.) is present.
|
|
7
|
+
*/
|
|
8
|
+
import { loadConfig } from './config.js';
|
|
9
|
+
import * as api from './api.js';
|
|
10
|
+
|
|
11
|
+
const INTERVAL_MS = 60_000;
|
|
12
|
+
|
|
13
|
+
export async function runDaemon() {
|
|
14
|
+
const cfg = loadConfig();
|
|
15
|
+
if (!cfg?.apiKey) {
|
|
16
|
+
process.stderr.write('[troxy-daemon] No API key found. Run troxy init first.\n');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { apiKey, agentName } = cfg;
|
|
21
|
+
|
|
22
|
+
const beat = () =>
|
|
23
|
+
api.mcpHeartbeat(apiKey, agentName)
|
|
24
|
+
.then(() => process.stderr.write(`[troxy-daemon] heartbeat ok\n`))
|
|
25
|
+
.catch(err => process.stderr.write(`[troxy-daemon] heartbeat failed: ${err.message}\n`));
|
|
26
|
+
|
|
27
|
+
process.stderr.write(`[troxy-daemon] starting (agent: ${agentName || 'unnamed'})\n`);
|
|
28
|
+
await beat();
|
|
29
|
+
setInterval(beat, INTERVAL_MS);
|
|
30
|
+
|
|
31
|
+
// Keep process alive indefinitely
|
|
32
|
+
process.stdin.resume();
|
|
33
|
+
}
|
package/src/init.js
CHANGED
|
@@ -154,7 +154,7 @@ export async function runInit({ key } = {}) {
|
|
|
154
154
|
console.log(' Background service installed ✓');
|
|
155
155
|
} catch (err) {
|
|
156
156
|
console.log(` Background service ✗ (${err.message})`);
|
|
157
|
-
console.log(' You can start it manually with: troxy
|
|
157
|
+
console.log(' You can start it manually with: troxy daemon &');
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
console.log('\n Your payments are now protected.');
|
|
@@ -179,7 +179,7 @@ Description=Troxy MCP Server
|
|
|
179
179
|
After=network.target
|
|
180
180
|
|
|
181
181
|
[Service]
|
|
182
|
-
ExecStart=${troxy}
|
|
182
|
+
ExecStart=${troxy} daemon
|
|
183
183
|
Restart=always
|
|
184
184
|
RestartSec=10
|
|
185
185
|
User=${os.userInfo().username}
|
|
@@ -205,7 +205,7 @@ WantedBy=multi-user.target
|
|
|
205
205
|
<key>ProgramArguments</key>
|
|
206
206
|
<array>
|
|
207
207
|
<string>${troxy}</string>
|
|
208
|
-
<string>
|
|
208
|
+
<string>daemon</string>
|
|
209
209
|
</array>
|
|
210
210
|
<key>EnvironmentVariables</key>
|
|
211
211
|
<dict>
|