infinicode 2.8.22 → 2.8.23

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.
@@ -0,0 +1,6 @@
1
+ export interface StopResult {
2
+ killedPids: number[];
3
+ removedUnits: string[];
4
+ errors: string[];
5
+ }
6
+ export declare function stopAll(): Promise<StopResult>;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * RoboPark / infinicode — stop everything on this machine, including whatever
3
+ * would respawn it.
4
+ *
5
+ * `--supervised` nodes (systemd/Task Scheduler/launchd, `Restart=always`) come
6
+ * back on their own after a plain kill — that's the class of bug that leaves a
7
+ * stale hub process answering on a port after someone thought they killed it.
8
+ * This kills every matching PID directly (never a blanket `killall node`) AND
9
+ * disables/removes the supervisor unit so it does not restart.
10
+ */
11
+ import { spawnSync } from 'node:child_process';
12
+ // Long-running daemons only — never a short-lived CLI invocation like
13
+ // `robopark setup` or `robopark stop` itself, which would otherwise match
14
+ // its own (or an ancestor shell's) command line and self-terminate mid-run.
15
+ const PATTERNS = [
16
+ 'infinicode serve',
17
+ 'infinicode.js serve',
18
+ 'cli.js serve', // source/dev invocation: `node dist/cli.js serve ...`
19
+ 'robopark serve',
20
+ 'robopark-cli.js serve',
21
+ 'preview_agent.py',
22
+ 'app_pi_clean.py',
23
+ ];
24
+ export async function stopAll() {
25
+ const platform = process.platform;
26
+ if (platform === 'win32')
27
+ return stopAllWindows();
28
+ return stopAllUnix();
29
+ }
30
+ function stopAllWindows() {
31
+ const killedPids = [];
32
+ const removedUnits = [];
33
+ const errors = [];
34
+ // Find every process whose command line matches, by PID — not by image
35
+ // name, so we never touch an unrelated node.exe (e.g. an MCP server).
36
+ const ps = spawnSync('powershell.exe', [
37
+ '-NoProfile', '-NonInteractive', '-Command',
38
+ `Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -and ($_.CommandLine -match '${PATTERNS.join('|').replace(/'/g, "''")}') } | Select-Object -ExpandProperty ProcessId`,
39
+ ], { encoding: 'utf8' });
40
+ const pids = (ps.stdout ?? '').split(/\r?\n/).map(s => s.trim()).filter(Boolean).map(Number).filter(Number.isFinite);
41
+ const self = process.pid;
42
+ for (const pid of pids) {
43
+ if (pid === self)
44
+ continue; // never kill the process running this command
45
+ const kill = spawnSync('taskkill', ['/PID', String(pid), '/F', '/T'], { encoding: 'utf8' });
46
+ if (kill.status === 0)
47
+ killedPids.push(pid);
48
+ else
49
+ errors.push(`taskkill ${pid}: ${(kill.stderr || kill.stdout || '').trim()}`);
50
+ }
51
+ // Remove any Scheduled Tasks registered by `robopark setup --auto-start`
52
+ // (auto-start.ts names them "RoboPark-<role>-<name>") so they don't relaunch
53
+ // on next login/boot.
54
+ const query = spawnSync('schtasks', ['/Query', '/FO', 'CSV', '/NH'], { encoding: 'utf8' });
55
+ const taskNames = (query.stdout ?? '')
56
+ .split(/\r?\n/)
57
+ .map(line => line.split(',')[0]?.replace(/^"|"$/g, ''))
58
+ .filter((n) => !!n && n.startsWith('\\RoboPark-'));
59
+ for (const name of taskNames) {
60
+ const del = spawnSync('schtasks', ['/Delete', '/TN', name, '/F'], { encoding: 'utf8' });
61
+ if (del.status === 0)
62
+ removedUnits.push(name);
63
+ else
64
+ errors.push(`schtasks /Delete ${name}: ${(del.stderr || del.stdout || '').trim()}`);
65
+ }
66
+ return { killedPids, removedUnits, errors };
67
+ }
68
+ function stopAllUnix() {
69
+ const killedPids = [];
70
+ const removedUnits = [];
71
+ const errors = [];
72
+ const self = process.pid;
73
+ const ps = spawnSync('ps', ['ax', '-o', 'pid=,command='], { encoding: 'utf8' });
74
+ const lines = (ps.stdout ?? '').split('\n');
75
+ const re = new RegExp(PATTERNS.map(p => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'));
76
+ for (const line of lines) {
77
+ const m = line.match(/^\s*(\d+)\s+(.*)$/);
78
+ if (!m)
79
+ continue;
80
+ const pid = Number(m[1]);
81
+ const cmd = m[2];
82
+ if (pid === self || !re.test(cmd))
83
+ continue;
84
+ const kill = spawnSync('kill', ['-9', String(pid)], { encoding: 'utf8' });
85
+ if (kill.status === 0)
86
+ killedPids.push(pid);
87
+ else
88
+ errors.push(`kill -9 ${pid}: ${(kill.stderr || '').trim()}`);
89
+ }
90
+ // Also free the well-known RoboPark ports directly — belt and suspenders
91
+ // against a supervised process whose command line didn't match a pattern.
92
+ for (const port of [47913, 47921, 47922, 8080, 5000, 5057]) {
93
+ const lsof = spawnSync('lsof', ['-t', `-i:${port}`], { encoding: 'utf8' });
94
+ const pids = (lsof.stdout ?? '').split('\n').map(s => s.trim()).filter(Boolean).map(Number);
95
+ for (const pid of pids) {
96
+ if (pid === self || killedPids.includes(pid))
97
+ continue;
98
+ const kill = spawnSync('kill', ['-9', String(pid)], { encoding: 'utf8' });
99
+ if (kill.status === 0)
100
+ killedPids.push(pid);
101
+ }
102
+ }
103
+ if (process.platform === 'linux') {
104
+ const list = spawnSync('systemctl', ['list-units', '--all', '--plain', '--no-legend', 'robopark-*'], { encoding: 'utf8' });
105
+ const units = (list.stdout ?? '')
106
+ .split('\n')
107
+ .map(l => l.trim().split(/\s+/)[0])
108
+ .filter((u) => !!u && u.endsWith('.service'));
109
+ for (const unit of units) {
110
+ spawnSync('systemctl', ['stop', unit], { encoding: 'utf8' });
111
+ const disable = spawnSync('systemctl', ['disable', unit], { encoding: 'utf8' });
112
+ if (disable.status === 0)
113
+ removedUnits.push(unit);
114
+ else
115
+ errors.push(`systemctl disable ${unit}: ${(disable.stderr || '').trim()}`);
116
+ }
117
+ if (units.length)
118
+ spawnSync('systemctl', ['daemon-reload'], { encoding: 'utf8' });
119
+ }
120
+ return { killedPids, removedUnits, errors };
121
+ }
@@ -15,6 +15,7 @@ import { attachRoboparkSubcommands as attachLegacy } from './commands/robopark.j
15
15
  import { roboparkScan } from './robopark/scan.js';
16
16
  import { roboparkServe } from './robopark/serve.js';
17
17
  import { roboparkSetup } from './robopark/setup.js';
18
+ import { stopAll } from './robopark/stop-all.js';
18
19
  // Read the real version from package.json so `--version` never drifts from
19
20
  // what is actually installed (this was a hardcoded constant that went stale
20
21
  // across several releases — see the identical fix + comment in cli.ts).
@@ -135,6 +136,30 @@ program
135
136
  const { roboparkVisionAgent } = await import('./robopark/vision-agent-launcher.js');
136
137
  await roboparkVisionAgent(opts);
137
138
  });
139
+ program
140
+ .command('stop')
141
+ .description('Stop every infinicode/robopark process on this device AND remove any supervisor (systemd/Task Scheduler/launchd) that would respawn it')
142
+ .action(async () => {
143
+ console.log(chalk.bold('\n robopark stop\n ' + '─'.repeat(48)));
144
+ try {
145
+ const result = await stopAll();
146
+ if (result.killedPids.length)
147
+ console.log(chalk.green(` ✓ killed ${result.killedPids.length} process(es): ${result.killedPids.join(', ')}`));
148
+ else
149
+ console.log(chalk.dim(' no matching processes were running'));
150
+ if (result.removedUnits.length)
151
+ console.log(chalk.green(` ✓ removed ${result.removedUnits.length} supervisor unit(s): ${result.removedUnits.join(', ')}`));
152
+ else
153
+ console.log(chalk.dim(' no supervisor units registered'));
154
+ for (const err of result.errors)
155
+ console.log(chalk.yellow(` ! ${err}`));
156
+ console.log('');
157
+ }
158
+ catch (e) {
159
+ console.error('stop failed:', e instanceof Error ? (e.stack ?? e.message) : e);
160
+ process.exitCode = 1;
161
+ }
162
+ });
138
163
  program
139
164
  .command('setup')
140
165
  .description('Set up this device in one pass')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infinicode",
3
- "version": "2.8.22",
3
+ "version": "2.8.23",
4
4
  "description": "OpenKernel — provider-agnostic AI execution kernel. Native coding agent + mission-driven execution runtime.",
5
5
  "type": "module",
6
6
  "main": "./dist/kernel/index.js",