cicy-code 2.1.47 → 2.1.48

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 (2) hide show
  1. package/bin/cicy-code.js +84 -13
  2. package/package.json +1 -1
package/bin/cicy-code.js CHANGED
@@ -1,18 +1,20 @@
1
1
  #!/usr/bin/env node
2
- // Thin launcher: resolves the prebuilt binary that ships in the
3
- // platform-specific optionalDependency (cicy-code-<os>-<cpu>) and execs it.
4
- // No network, no postinstall download — npm installs only the sub-package
5
- // matching the current os/cpu (the others are skipped via their os/cpu
6
- // fields), so a CN user pulls just their ~30MB slice from npmmirror.
7
- const { spawn } = require('child_process');
2
+ // Launcher for the npm distribution. Resolves the prebuilt binary that ships
3
+ // in the platform-specific optionalDependency (cicy-code-<os>-<cpu>) and execs
4
+ // it — no network, no postinstall download.
5
+ //
6
+ // Before exec it mirrors dev.py's startup: if PORT (default 8008) is already
7
+ // held by a *cicy-code* process it kills it and waits, so a stale instance
8
+ // never blocks the new one. A non-cicy occupant is left alone and we abort
9
+ // with a clear message (don't clobber someone else's server).
10
+ const { spawn, execSync } = require('child_process');
8
11
  const fs = require('fs');
9
12
 
10
- const platformPkg = `cicy-code-${process.platform}-${process.arch}`;
13
+ const PORT = process.env.PORT || '8008';
11
14
 
15
+ const platformPkg = `cicy-code-${process.platform}-${process.arch}`;
12
16
  let binPath;
13
17
  try {
14
- // require.resolve finds the binary inside the installed sub-package,
15
- // wherever the package manager hoisted it.
16
18
  binPath = require.resolve(`${platformPkg}/cicy-code`);
17
19
  } catch {
18
20
  console.error(`cicy-code: no prebuilt binary for ${process.platform}-${process.arch}.`);
@@ -22,12 +24,81 @@ try {
22
24
  ` (in China add --registry=https://registry.npmmirror.com)`);
23
25
  process.exit(1);
24
26
  }
25
-
26
- // npm restores the 0755 mode from the tarball, but chmod defensively in case
27
- // a mirror or extraction stripped the exec bit.
28
27
  try { fs.chmodSync(binPath, 0o755); } catch {}
29
28
 
30
- const child = spawn(binPath, process.argv.slice(2), { stdio: 'inherit', env: process.env });
29
+ // --- dev.py-style port hygiene on PORT (default 8008) ----------------------
30
+
31
+ function pidOnPort(port) {
32
+ // lsof first (macOS + Linux), then ss (Linux without lsof).
33
+ try {
34
+ const out = execSync(`lsof -ti TCP:${port} -sTCP:LISTEN`, {
35
+ encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
36
+ }).trim();
37
+ if (out) return out.split('\n')[0].trim();
38
+ } catch {}
39
+ try {
40
+ const out = execSync(`ss -tlnp 'sport = :${port}'`, {
41
+ encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
42
+ });
43
+ const m = out.match(/pid=(\d+)/);
44
+ if (m) return m[1];
45
+ } catch {}
46
+ return null;
47
+ }
48
+
49
+ function processCommand(pid) {
50
+ try {
51
+ return execSync(`ps -p ${pid} -o command=`, {
52
+ encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
53
+ }).trim();
54
+ } catch { return ''; }
55
+ }
56
+
57
+ function isAlive(pid) {
58
+ try { process.kill(Number(pid), 0); return true; }
59
+ catch (e) { return e.code === 'EPERM'; }
60
+ }
61
+
62
+ function waitExit(pid, timeoutMs) {
63
+ const start = Date.now();
64
+ while (Date.now() - start < timeoutMs) {
65
+ if (!isAlive(pid)) return true;
66
+ try { execSync('sleep 0.2'); } catch {}
67
+ }
68
+ return !isAlive(pid);
69
+ }
70
+
71
+ function killPid(pid) {
72
+ try { process.kill(Number(pid), 'SIGTERM'); }
73
+ catch (e) { if (e.code === 'ESRCH') return true; }
74
+ if (waitExit(pid, 6000)) return true;
75
+ try { process.kill(Number(pid), 'SIGKILL'); }
76
+ catch (e) { if (e.code === 'ESRCH') return true; }
77
+ return waitExit(pid, 2000);
78
+ }
79
+
80
+ const existing = pidOnPort(PORT);
81
+ if (existing) {
82
+ const cmd = processCommand(existing);
83
+ if (/cicy-code/.test(cmd)) {
84
+ console.log(`cicy-code: stopping existing instance on :${PORT} (pid=${existing})`);
85
+ if (!killPid(existing) || pidOnPort(PORT)) {
86
+ console.error(`cicy-code: port ${PORT} still in use after kill — aborting`);
87
+ process.exit(1);
88
+ }
89
+ } else {
90
+ console.error(`cicy-code: port ${PORT} is held by a non-cicy process (pid=${existing}): ${cmd}`);
91
+ console.error(`cicy-code: free it or set PORT=<other> — aborting`);
92
+ process.exit(1);
93
+ }
94
+ }
95
+
96
+ // --- launch ----------------------------------------------------------------
97
+
98
+ const child = spawn(binPath, process.argv.slice(2), {
99
+ stdio: 'inherit',
100
+ env: { ...process.env, PORT },
101
+ });
31
102
  child.on('exit', (code, signal) => {
32
103
  if (signal) process.kill(process.pid, signal);
33
104
  else process.exit(code == null ? 0 : code);
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "2.1.47",
6
+ "version": "2.1.48",
7
7
  "description": "CiCy Code - AI-powered development environment",
8
8
  "author": {
9
9
  "name": "cicybot",