@lark-codem/codem-core 0.8.9 → 0.8.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/LICENSE +6 -6
- package/bin/lib/control-fd.js +63 -63
- package/bin/lib/detect-target.js +65 -65
- package/bin/lib/launch.js +106 -106
- package/bin/lib/resolve-binary.js +64 -64
- package/bin/linco +74 -74
- package/package.json +7 -7
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
© Lark Technologies Pte. Ltd. All rights reserved. Use is subject to the Legal Agreements outlined here:
|
|
2
|
-
|
|
3
|
-
Feishu User Terms of Service [https://www.feishu.cn/terms]
|
|
4
|
-
Feishu Privacy Policy [https://www.feishu.cn/privacy]
|
|
5
|
-
|
|
6
|
-
Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
1
|
+
© Lark Technologies Pte. Ltd. All rights reserved. Use is subject to the Legal Agreements outlined here:
|
|
2
|
+
|
|
3
|
+
Feishu User Terms of Service [https://www.feishu.cn/terms]
|
|
4
|
+
Feishu Privacy Policy [https://www.feishu.cn/privacy]
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
package/bin/lib/control-fd.js
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// codem-bridge launches linco with FOUR stdio slots, not three:
|
|
4
|
-
//
|
|
5
|
-
// stdio: ["inherit", "inherit", "inherit", "pipe"] (codem-bridge linco-runner.js)
|
|
6
|
-
// \______ the terminal ______/ \_ the /space control channel
|
|
7
|
-
//
|
|
8
|
-
// fd 3 is an out-of-band channel: on a `/space` selection linco writes one NDJSON
|
|
9
|
-
// line to it so codem-bridge can persist the chosen project without polluting the
|
|
10
|
-
// TUI's screen. The host announces the fd number via `CODEM_PROJECT_CHANNEL_FD`.
|
|
11
|
-
//
|
|
12
|
-
// This shim sits BETWEEN the two processes, and `stdio: "inherit"` is shorthand for
|
|
13
|
-
// ["inherit","inherit","inherit"] — exactly three slots. It therefore dropped fd 3
|
|
14
|
-
// on the floor while still forwarding the env var that advertises it, so linco kept
|
|
15
|
-
// writing to a fd the exec'd process no longer held (ENXIO, not EBADF: libuv fills
|
|
16
|
-
// unlisted slots with an unusable placeholder rather than leaving them closed).
|
|
17
|
-
//
|
|
18
|
-
// All three layers then failed quietly — linco's writer is best-effort and only
|
|
19
|
-
// `tracing::warn!`s, and on the host side an idle pipe is indistinguishable from
|
|
20
|
-
// "an old linco that never writes fd 3" (codem-bridge says so in a comment). Net
|
|
21
|
-
// effect: `/space` reported success and the selection was silently lost.
|
|
22
|
-
//
|
|
23
|
-
// SCOPE — unix only. linco's writer is a `#[cfg(not(unix))]` no-op on Windows
|
|
24
|
-
// (crates/linco-cli/src/space_channel.rs), so there is nothing to forward there and
|
|
25
|
-
// no reason to perturb Windows process creation.
|
|
26
|
-
|
|
27
|
-
/// The env var through which codem-bridge advertises the control fd.
|
|
28
|
-
const CONTROL_FD_ENV = "CODEM_PROJECT_CHANNEL_FD";
|
|
29
|
-
|
|
30
|
-
/// stdio for a plain interactive run: the terminal and nothing else.
|
|
31
|
-
const TERMINAL_STDIO = ["inherit", "inherit", "inherit"];
|
|
32
|
-
|
|
33
|
-
/// Parse the advertised fd, or null when there is nothing to forward.
|
|
34
|
-
///
|
|
35
|
-
/// Only fd 3 is expressible here: `stdio` slots are positional, so the 4th entry
|
|
36
|
-
/// IS fd 3. A host asking for fd 4+ cannot be honoured by appending one entry, and
|
|
37
|
-
/// silently forwarding the wrong fd would be worse than not forwarding at all.
|
|
38
|
-
function controlFdFromEnv(env) {
|
|
39
|
-
const raw = env && env[CONTROL_FD_ENV];
|
|
40
|
-
if (typeof raw !== "string") {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
const trimmed = raw.trim();
|
|
44
|
-
// Number.parseInt would accept "3x"/"3.5"; the host writes a bare integer.
|
|
45
|
-
if (!/^-?\d+$/.test(trimmed)) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
return Number.parseInt(trimmed, 10) === 3 ? 3 : null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/// Build the stdio array to hand the platform binary. Returns a fresh array —
|
|
52
|
-
/// `launch` mutates it when degrading after an EBADF.
|
|
53
|
-
function buildStdio({ env = process.env, platform = process.platform } = {}) {
|
|
54
|
-
if (platform === "win32") {
|
|
55
|
-
return TERMINAL_STDIO.slice();
|
|
56
|
-
}
|
|
57
|
-
if (controlFdFromEnv(env) === null) {
|
|
58
|
-
return TERMINAL_STDIO.slice();
|
|
59
|
-
}
|
|
60
|
-
return [...TERMINAL_STDIO, "inherit"];
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
module.exports = { buildStdio, controlFdFromEnv, CONTROL_FD_ENV, TERMINAL_STDIO };
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// codem-bridge launches linco with FOUR stdio slots, not three:
|
|
4
|
+
//
|
|
5
|
+
// stdio: ["inherit", "inherit", "inherit", "pipe"] (codem-bridge linco-runner.js)
|
|
6
|
+
// \______ the terminal ______/ \_ the /space control channel
|
|
7
|
+
//
|
|
8
|
+
// fd 3 is an out-of-band channel: on a `/space` selection linco writes one NDJSON
|
|
9
|
+
// line to it so codem-bridge can persist the chosen project without polluting the
|
|
10
|
+
// TUI's screen. The host announces the fd number via `CODEM_PROJECT_CHANNEL_FD`.
|
|
11
|
+
//
|
|
12
|
+
// This shim sits BETWEEN the two processes, and `stdio: "inherit"` is shorthand for
|
|
13
|
+
// ["inherit","inherit","inherit"] — exactly three slots. It therefore dropped fd 3
|
|
14
|
+
// on the floor while still forwarding the env var that advertises it, so linco kept
|
|
15
|
+
// writing to a fd the exec'd process no longer held (ENXIO, not EBADF: libuv fills
|
|
16
|
+
// unlisted slots with an unusable placeholder rather than leaving them closed).
|
|
17
|
+
//
|
|
18
|
+
// All three layers then failed quietly — linco's writer is best-effort and only
|
|
19
|
+
// `tracing::warn!`s, and on the host side an idle pipe is indistinguishable from
|
|
20
|
+
// "an old linco that never writes fd 3" (codem-bridge says so in a comment). Net
|
|
21
|
+
// effect: `/space` reported success and the selection was silently lost.
|
|
22
|
+
//
|
|
23
|
+
// SCOPE — unix only. linco's writer is a `#[cfg(not(unix))]` no-op on Windows
|
|
24
|
+
// (crates/linco-cli/src/space_channel.rs), so there is nothing to forward there and
|
|
25
|
+
// no reason to perturb Windows process creation.
|
|
26
|
+
|
|
27
|
+
/// The env var through which codem-bridge advertises the control fd.
|
|
28
|
+
const CONTROL_FD_ENV = "CODEM_PROJECT_CHANNEL_FD";
|
|
29
|
+
|
|
30
|
+
/// stdio for a plain interactive run: the terminal and nothing else.
|
|
31
|
+
const TERMINAL_STDIO = ["inherit", "inherit", "inherit"];
|
|
32
|
+
|
|
33
|
+
/// Parse the advertised fd, or null when there is nothing to forward.
|
|
34
|
+
///
|
|
35
|
+
/// Only fd 3 is expressible here: `stdio` slots are positional, so the 4th entry
|
|
36
|
+
/// IS fd 3. A host asking for fd 4+ cannot be honoured by appending one entry, and
|
|
37
|
+
/// silently forwarding the wrong fd would be worse than not forwarding at all.
|
|
38
|
+
function controlFdFromEnv(env) {
|
|
39
|
+
const raw = env && env[CONTROL_FD_ENV];
|
|
40
|
+
if (typeof raw !== "string") {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const trimmed = raw.trim();
|
|
44
|
+
// Number.parseInt would accept "3x"/"3.5"; the host writes a bare integer.
|
|
45
|
+
if (!/^-?\d+$/.test(trimmed)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return Number.parseInt(trimmed, 10) === 3 ? 3 : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Build the stdio array to hand the platform binary. Returns a fresh array —
|
|
52
|
+
/// `launch` mutates it when degrading after an EBADF.
|
|
53
|
+
function buildStdio({ env = process.env, platform = process.platform } = {}) {
|
|
54
|
+
if (platform === "win32") {
|
|
55
|
+
return TERMINAL_STDIO.slice();
|
|
56
|
+
}
|
|
57
|
+
if (controlFdFromEnv(env) === null) {
|
|
58
|
+
return TERMINAL_STDIO.slice();
|
|
59
|
+
}
|
|
60
|
+
return [...TERMINAL_STDIO, "inherit"];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = { buildStdio, controlFdFromEnv, CONTROL_FD_ENV, TERMINAL_STDIO };
|
package/bin/lib/detect-target.js
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require("node:fs");
|
|
4
|
-
|
|
5
|
-
function normalizeArch(arch) {
|
|
6
|
-
switch (arch) {
|
|
7
|
-
case "x64":
|
|
8
|
-
case "arm64":
|
|
9
|
-
return arch;
|
|
10
|
-
case "ia32":
|
|
11
|
-
return "ia32";
|
|
12
|
-
default:
|
|
13
|
-
throw new Error(`Unsupported architecture: ${arch}`);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function detectLinuxLibc() {
|
|
18
|
-
if (process.report && typeof process.report.getReport === "function") {
|
|
19
|
-
try {
|
|
20
|
-
const report = process.report.getReport();
|
|
21
|
-
if (report && report.header && report.header.glibcVersionRuntime) {
|
|
22
|
-
return "gnu";
|
|
23
|
-
}
|
|
24
|
-
} catch {
|
|
25
|
-
// fall through to fs probes
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const muslProbes = [
|
|
30
|
-
"/lib/ld-musl-x86_64.so.1",
|
|
31
|
-
"/lib/ld-musl-aarch64.so.1",
|
|
32
|
-
"/lib/ld-musl-i386.so.1",
|
|
33
|
-
];
|
|
34
|
-
for (const probe of muslProbes) {
|
|
35
|
-
if (fs.existsSync(probe)) {
|
|
36
|
-
return "musl";
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return "gnu";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function detectTarget(host = process, env = process.env) {
|
|
44
|
-
if (env && typeof env.LINCO_NPM_TARGET === "string" && env.LINCO_NPM_TARGET.trim()) {
|
|
45
|
-
return env.LINCO_NPM_TARGET.trim();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const platform = host.platform;
|
|
49
|
-
const arch = normalizeArch(host.arch);
|
|
50
|
-
|
|
51
|
-
if (platform === "darwin") {
|
|
52
|
-
return `darwin-${arch}`;
|
|
53
|
-
}
|
|
54
|
-
if (platform === "linux") {
|
|
55
|
-
const libc = host.libc || detectLinuxLibc();
|
|
56
|
-
return `linux-${arch}-${libc}`;
|
|
57
|
-
}
|
|
58
|
-
if (platform === "win32") {
|
|
59
|
-
return `win32-${arch}-msvc`;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
throw new Error(`Unsupported platform: ${platform}`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
module.exports = { detectTarget, normalizeArch, detectLinuxLibc };
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
|
|
5
|
+
function normalizeArch(arch) {
|
|
6
|
+
switch (arch) {
|
|
7
|
+
case "x64":
|
|
8
|
+
case "arm64":
|
|
9
|
+
return arch;
|
|
10
|
+
case "ia32":
|
|
11
|
+
return "ia32";
|
|
12
|
+
default:
|
|
13
|
+
throw new Error(`Unsupported architecture: ${arch}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function detectLinuxLibc() {
|
|
18
|
+
if (process.report && typeof process.report.getReport === "function") {
|
|
19
|
+
try {
|
|
20
|
+
const report = process.report.getReport();
|
|
21
|
+
if (report && report.header && report.header.glibcVersionRuntime) {
|
|
22
|
+
return "gnu";
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// fall through to fs probes
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const muslProbes = [
|
|
30
|
+
"/lib/ld-musl-x86_64.so.1",
|
|
31
|
+
"/lib/ld-musl-aarch64.so.1",
|
|
32
|
+
"/lib/ld-musl-i386.so.1",
|
|
33
|
+
];
|
|
34
|
+
for (const probe of muslProbes) {
|
|
35
|
+
if (fs.existsSync(probe)) {
|
|
36
|
+
return "musl";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return "gnu";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function detectTarget(host = process, env = process.env) {
|
|
44
|
+
if (env && typeof env.LINCO_NPM_TARGET === "string" && env.LINCO_NPM_TARGET.trim()) {
|
|
45
|
+
return env.LINCO_NPM_TARGET.trim();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const platform = host.platform;
|
|
49
|
+
const arch = normalizeArch(host.arch);
|
|
50
|
+
|
|
51
|
+
if (platform === "darwin") {
|
|
52
|
+
return `darwin-${arch}`;
|
|
53
|
+
}
|
|
54
|
+
if (platform === "linux") {
|
|
55
|
+
const libc = host.libc || detectLinuxLibc();
|
|
56
|
+
return `linux-${arch}-${libc}`;
|
|
57
|
+
}
|
|
58
|
+
if (platform === "win32") {
|
|
59
|
+
return `win32-${arch}-msvc`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = { detectTarget, normalizeArch, detectLinuxLibc };
|
package/bin/lib/launch.js
CHANGED
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// Spawning the platform binary, with the two failure modes that actually reach
|
|
4
|
-
// users spelled out.
|
|
5
|
-
//
|
|
6
|
-
// WHY THIS IS NOT JUST `spawn(...)` + `child.on("error")`:
|
|
7
|
-
// `child_process.spawn` reports ENOENT asynchronously (via the "error" event) but
|
|
8
|
-
// throws SYNCHRONOUSLY for every other errno — see the `throw new ErrnoException(err,
|
|
9
|
-
// 'spawn')` in node:internal/child_process. An `on("error")` handler therefore cannot
|
|
10
|
-
// observe an EACCES/EBADF/UNKNOWN failure, and the user gets a raw Node stack trace
|
|
11
|
-
// naming an internal file instead of a sentence about their own machine. Both paths
|
|
12
|
-
// must funnel into the same reporter.
|
|
13
|
-
|
|
14
|
-
const { TERMINAL_STDIO } = require("./control-fd");
|
|
15
|
-
|
|
16
|
-
/// Human-readable follow-up for the errnos we can actually say something useful
|
|
17
|
-
/// about. Anything else falls through to the bare message.
|
|
18
|
-
function hintsFor(code, platform, binary) {
|
|
19
|
-
switch (code) {
|
|
20
|
-
case "UNKNOWN":
|
|
21
|
-
// libuv's catch-all for Win32 errors it has no mapping for. The common
|
|
22
|
-
// cause in the field is CreateProcessW being denied by security software
|
|
23
|
-
// (ERROR_VIRUS_INFECTED / ERROR_VIRUS_DELETED have no libuv mapping).
|
|
24
|
-
return platform === "win32"
|
|
25
|
-
? [
|
|
26
|
-
"On Windows this usually means antivirus or endpoint security software blocked",
|
|
27
|
-
"the executable, or the file on disk is damaged. Run it directly to see the",
|
|
28
|
-
"underlying OS error:",
|
|
29
|
-
` & "${binary}" --version`,
|
|
30
|
-
]
|
|
31
|
-
: null;
|
|
32
|
-
case "ENOENT":
|
|
33
|
-
return [
|
|
34
|
-
"The platform binary is missing. Reinstall it with:",
|
|
35
|
-
" npm install --include=optional @lark-codem/codem-core",
|
|
36
|
-
];
|
|
37
|
-
case "EACCES":
|
|
38
|
-
return [
|
|
39
|
-
"The platform binary is not executable. Check its file permissions:",
|
|
40
|
-
` ls -l "${binary}"`,
|
|
41
|
-
];
|
|
42
|
-
default:
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function reportFatal({ error, binary, platform, stderr, exit }) {
|
|
48
|
-
stderr(`linco: failed to launch ${binary}`);
|
|
49
|
-
stderr(` ${error && error.message ? error.message : String(error)}`);
|
|
50
|
-
const hints = hintsFor(error && error.code, platform, binary);
|
|
51
|
-
if (hints) {
|
|
52
|
-
for (const line of hints) {
|
|
53
|
-
stderr(` ${line}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
exit(1);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/// Spawn the platform binary. Returns the child process, or undefined when the
|
|
60
|
-
/// launch failed (the caller has already been told via `exit`).
|
|
61
|
-
///
|
|
62
|
-
/// `stdio` may carry a 4th slot for codem-bridge's /space control fd. If the host
|
|
63
|
-
/// advertised that fd but did not actually pass it, spawning fails with EBADF —
|
|
64
|
-
/// degrade to the terminal-only trio and start anyway rather than refusing to run
|
|
65
|
-
/// over a non-essential side channel.
|
|
66
|
-
function launch({
|
|
67
|
-
binary,
|
|
68
|
-
args,
|
|
69
|
-
stdio,
|
|
70
|
-
platform = process.platform,
|
|
71
|
-
spawnImpl = require("node:child_process").spawn,
|
|
72
|
-
stderr = (line) => console.error(line),
|
|
73
|
-
exit = (code) => process.exit(code),
|
|
74
|
-
}) {
|
|
75
|
-
const attempt = (stdioForAttempt) =>
|
|
76
|
-
spawnImpl(binary, args, { stdio: stdioForAttempt, windowsHide: true });
|
|
77
|
-
|
|
78
|
-
let child;
|
|
79
|
-
try {
|
|
80
|
-
child = attempt(stdio);
|
|
81
|
-
} catch (error) {
|
|
82
|
-
if (stdio.length > 3 && error && error.code === "EBADF") {
|
|
83
|
-
stderr(
|
|
84
|
-
"linco: the host advertised a /space control fd it did not pass; continuing without it"
|
|
85
|
-
);
|
|
86
|
-
try {
|
|
87
|
-
child = attempt(TERMINAL_STDIO.slice());
|
|
88
|
-
} catch (retryError) {
|
|
89
|
-
reportFatal({ error: retryError, binary, platform, stderr, exit });
|
|
90
|
-
return undefined;
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
reportFatal({ error, binary, platform, stderr, exit });
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// ENOENT lands here instead of in the catch above.
|
|
99
|
-
child.on("error", (error) => {
|
|
100
|
-
reportFatal({ error, binary, platform, stderr, exit });
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
return child;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
module.exports = { launch, hintsFor };
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Spawning the platform binary, with the two failure modes that actually reach
|
|
4
|
+
// users spelled out.
|
|
5
|
+
//
|
|
6
|
+
// WHY THIS IS NOT JUST `spawn(...)` + `child.on("error")`:
|
|
7
|
+
// `child_process.spawn` reports ENOENT asynchronously (via the "error" event) but
|
|
8
|
+
// throws SYNCHRONOUSLY for every other errno — see the `throw new ErrnoException(err,
|
|
9
|
+
// 'spawn')` in node:internal/child_process. An `on("error")` handler therefore cannot
|
|
10
|
+
// observe an EACCES/EBADF/UNKNOWN failure, and the user gets a raw Node stack trace
|
|
11
|
+
// naming an internal file instead of a sentence about their own machine. Both paths
|
|
12
|
+
// must funnel into the same reporter.
|
|
13
|
+
|
|
14
|
+
const { TERMINAL_STDIO } = require("./control-fd");
|
|
15
|
+
|
|
16
|
+
/// Human-readable follow-up for the errnos we can actually say something useful
|
|
17
|
+
/// about. Anything else falls through to the bare message.
|
|
18
|
+
function hintsFor(code, platform, binary) {
|
|
19
|
+
switch (code) {
|
|
20
|
+
case "UNKNOWN":
|
|
21
|
+
// libuv's catch-all for Win32 errors it has no mapping for. The common
|
|
22
|
+
// cause in the field is CreateProcessW being denied by security software
|
|
23
|
+
// (ERROR_VIRUS_INFECTED / ERROR_VIRUS_DELETED have no libuv mapping).
|
|
24
|
+
return platform === "win32"
|
|
25
|
+
? [
|
|
26
|
+
"On Windows this usually means antivirus or endpoint security software blocked",
|
|
27
|
+
"the executable, or the file on disk is damaged. Run it directly to see the",
|
|
28
|
+
"underlying OS error:",
|
|
29
|
+
` & "${binary}" --version`,
|
|
30
|
+
]
|
|
31
|
+
: null;
|
|
32
|
+
case "ENOENT":
|
|
33
|
+
return [
|
|
34
|
+
"The platform binary is missing. Reinstall it with:",
|
|
35
|
+
" npm install --include=optional @lark-codem/codem-core",
|
|
36
|
+
];
|
|
37
|
+
case "EACCES":
|
|
38
|
+
return [
|
|
39
|
+
"The platform binary is not executable. Check its file permissions:",
|
|
40
|
+
` ls -l "${binary}"`,
|
|
41
|
+
];
|
|
42
|
+
default:
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function reportFatal({ error, binary, platform, stderr, exit }) {
|
|
48
|
+
stderr(`linco: failed to launch ${binary}`);
|
|
49
|
+
stderr(` ${error && error.message ? error.message : String(error)}`);
|
|
50
|
+
const hints = hintsFor(error && error.code, platform, binary);
|
|
51
|
+
if (hints) {
|
|
52
|
+
for (const line of hints) {
|
|
53
|
+
stderr(` ${line}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// Spawn the platform binary. Returns the child process, or undefined when the
|
|
60
|
+
/// launch failed (the caller has already been told via `exit`).
|
|
61
|
+
///
|
|
62
|
+
/// `stdio` may carry a 4th slot for codem-bridge's /space control fd. If the host
|
|
63
|
+
/// advertised that fd but did not actually pass it, spawning fails with EBADF —
|
|
64
|
+
/// degrade to the terminal-only trio and start anyway rather than refusing to run
|
|
65
|
+
/// over a non-essential side channel.
|
|
66
|
+
function launch({
|
|
67
|
+
binary,
|
|
68
|
+
args,
|
|
69
|
+
stdio,
|
|
70
|
+
platform = process.platform,
|
|
71
|
+
spawnImpl = require("node:child_process").spawn,
|
|
72
|
+
stderr = (line) => console.error(line),
|
|
73
|
+
exit = (code) => process.exit(code),
|
|
74
|
+
}) {
|
|
75
|
+
const attempt = (stdioForAttempt) =>
|
|
76
|
+
spawnImpl(binary, args, { stdio: stdioForAttempt, windowsHide: true });
|
|
77
|
+
|
|
78
|
+
let child;
|
|
79
|
+
try {
|
|
80
|
+
child = attempt(stdio);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (stdio.length > 3 && error && error.code === "EBADF") {
|
|
83
|
+
stderr(
|
|
84
|
+
"linco: the host advertised a /space control fd it did not pass; continuing without it"
|
|
85
|
+
);
|
|
86
|
+
try {
|
|
87
|
+
child = attempt(TERMINAL_STDIO.slice());
|
|
88
|
+
} catch (retryError) {
|
|
89
|
+
reportFatal({ error: retryError, binary, platform, stderr, exit });
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
reportFatal({ error, binary, platform, stderr, exit });
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ENOENT lands here instead of in the catch above.
|
|
99
|
+
child.on("error", (error) => {
|
|
100
|
+
reportFatal({ error, binary, platform, stderr, exit });
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return child;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = { launch, hintsFor };
|
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// Windows release binaries are currently cross-compiled from macOS via
|
|
4
|
-
// cargo-zigbuild, which can only produce *-pc-windows-gnu artifacts. The
|
|
5
|
-
// canonical npm target on Windows stays win32-<arch>-msvc (preferred when a
|
|
6
|
-
// Windows-host MSVC build is published); when that platform package is
|
|
7
|
-
// absent we fall back to the sibling win32-<arch>-gnu package.
|
|
8
|
-
const TARGET_FALLBACKS = {
|
|
9
|
-
"win32-x64-msvc": ["win32-x64-gnu"],
|
|
10
|
-
"win32-arm64-msvc": ["win32-arm64-gnu"],
|
|
11
|
-
"win32-ia32-msvc": ["win32-ia32-gnu"],
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
function packageNameForTarget(target) {
|
|
15
|
-
return `@lark-codem/codem-core-${target}`;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function binaryNameForTarget(target) {
|
|
19
|
-
return target.startsWith("win32-") ? "codem-core.exe" : "codem-core";
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function resolvePlatformBinary(target, options = {}) {
|
|
23
|
-
const req = options.require || require;
|
|
24
|
-
const paths = options.paths;
|
|
25
|
-
const candidates = [target, ...(TARGET_FALLBACKS[target] || [])];
|
|
26
|
-
|
|
27
|
-
let lastNotFound;
|
|
28
|
-
for (const candidate of candidates) {
|
|
29
|
-
const request = `${packageNameForTarget(candidate)}/${binaryNameForTarget(candidate)}`;
|
|
30
|
-
try {
|
|
31
|
-
if (paths) {
|
|
32
|
-
return req.resolve(request, { paths });
|
|
33
|
-
}
|
|
34
|
-
return req.resolve(request);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
if (error && error.code === "MODULE_NOT_FOUND") {
|
|
37
|
-
lastNotFound = error;
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
throw error;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const packageName = packageNameForTarget(target);
|
|
45
|
-
const helpLines = [
|
|
46
|
-
`linco: platform package ${packageName} is not installed.`,
|
|
47
|
-
];
|
|
48
|
-
if (candidates.length > 1) {
|
|
49
|
-
const fallbacks = candidates.slice(1).map(packageNameForTarget).join(", ");
|
|
50
|
-
helpLines.push(`Also tried fallback package(s): ${fallbacks}.`);
|
|
51
|
-
}
|
|
52
|
-
helpLines.push(
|
|
53
|
-
"This usually means optionalDependencies were skipped during install.",
|
|
54
|
-
"Try one of:",
|
|
55
|
-
" - reinstall with optional deps: npm install --include=optional @lark-codem/codem-core",
|
|
56
|
-
` - override target: LINCO_NPM_TARGET=<other-target> (current: ${target})`
|
|
57
|
-
);
|
|
58
|
-
const wrapped = new Error(helpLines.join("\n"));
|
|
59
|
-
wrapped.code = "LINCO_PLATFORM_PACKAGE_MISSING";
|
|
60
|
-
wrapped.cause = lastNotFound;
|
|
61
|
-
throw wrapped;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
module.exports = { resolvePlatformBinary };
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Windows release binaries are currently cross-compiled from macOS via
|
|
4
|
+
// cargo-zigbuild, which can only produce *-pc-windows-gnu artifacts. The
|
|
5
|
+
// canonical npm target on Windows stays win32-<arch>-msvc (preferred when a
|
|
6
|
+
// Windows-host MSVC build is published); when that platform package is
|
|
7
|
+
// absent we fall back to the sibling win32-<arch>-gnu package.
|
|
8
|
+
const TARGET_FALLBACKS = {
|
|
9
|
+
"win32-x64-msvc": ["win32-x64-gnu"],
|
|
10
|
+
"win32-arm64-msvc": ["win32-arm64-gnu"],
|
|
11
|
+
"win32-ia32-msvc": ["win32-ia32-gnu"],
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function packageNameForTarget(target) {
|
|
15
|
+
return `@lark-codem/codem-core-${target}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function binaryNameForTarget(target) {
|
|
19
|
+
return target.startsWith("win32-") ? "codem-core.exe" : "codem-core";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function resolvePlatformBinary(target, options = {}) {
|
|
23
|
+
const req = options.require || require;
|
|
24
|
+
const paths = options.paths;
|
|
25
|
+
const candidates = [target, ...(TARGET_FALLBACKS[target] || [])];
|
|
26
|
+
|
|
27
|
+
let lastNotFound;
|
|
28
|
+
for (const candidate of candidates) {
|
|
29
|
+
const request = `${packageNameForTarget(candidate)}/${binaryNameForTarget(candidate)}`;
|
|
30
|
+
try {
|
|
31
|
+
if (paths) {
|
|
32
|
+
return req.resolve(request, { paths });
|
|
33
|
+
}
|
|
34
|
+
return req.resolve(request);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error && error.code === "MODULE_NOT_FOUND") {
|
|
37
|
+
lastNotFound = error;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const packageName = packageNameForTarget(target);
|
|
45
|
+
const helpLines = [
|
|
46
|
+
`linco: platform package ${packageName} is not installed.`,
|
|
47
|
+
];
|
|
48
|
+
if (candidates.length > 1) {
|
|
49
|
+
const fallbacks = candidates.slice(1).map(packageNameForTarget).join(", ");
|
|
50
|
+
helpLines.push(`Also tried fallback package(s): ${fallbacks}.`);
|
|
51
|
+
}
|
|
52
|
+
helpLines.push(
|
|
53
|
+
"This usually means optionalDependencies were skipped during install.",
|
|
54
|
+
"Try one of:",
|
|
55
|
+
" - reinstall with optional deps: npm install --include=optional @lark-codem/codem-core",
|
|
56
|
+
` - override target: LINCO_NPM_TARGET=<other-target> (current: ${target})`
|
|
57
|
+
);
|
|
58
|
+
const wrapped = new Error(helpLines.join("\n"));
|
|
59
|
+
wrapped.code = "LINCO_PLATFORM_PACKAGE_MISSING";
|
|
60
|
+
wrapped.cause = lastNotFound;
|
|
61
|
+
throw wrapped;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = { resolvePlatformBinary };
|
package/bin/linco
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const fs = require("node:fs");
|
|
5
|
-
const { detectTarget } = require("./lib/detect-target");
|
|
6
|
-
const { resolvePlatformBinary } = require("./lib/resolve-binary");
|
|
7
|
-
const { buildStdio } = require("./lib/control-fd");
|
|
8
|
-
const { launch } = require("./lib/launch");
|
|
9
|
-
|
|
10
|
-
function main() {
|
|
11
|
-
let target;
|
|
12
|
-
try {
|
|
13
|
-
target = detectTarget();
|
|
14
|
-
} catch (error) {
|
|
15
|
-
console.error(`linco: ${error.message}`);
|
|
16
|
-
console.error("Hint: set LINCO_NPM_TARGET to override platform detection.");
|
|
17
|
-
process.exit(1);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let binary;
|
|
21
|
-
try {
|
|
22
|
-
binary = resolvePlatformBinary(target);
|
|
23
|
-
} catch (error) {
|
|
24
|
-
console.error(error.message);
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
fs.chmodSync(binary, 0o755);
|
|
30
|
-
} catch {
|
|
31
|
-
// best-effort
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Not a bare `stdio: "inherit"`: that is three slots and would drop the /space
|
|
35
|
-
// control fd codem-bridge passes as fd 3. See lib/control-fd.js.
|
|
36
|
-
const child = launch({
|
|
37
|
-
binary,
|
|
38
|
-
args: process.argv.slice(2),
|
|
39
|
-
stdio: buildStdio(),
|
|
40
|
-
});
|
|
41
|
-
if (!child) {
|
|
42
|
-
// launch() already reported the failure and called exit(1).
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const forwardSignal = (signal) => {
|
|
47
|
-
try {
|
|
48
|
-
child.kill(signal);
|
|
49
|
-
} catch {
|
|
50
|
-
// child may already be exiting
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
const signalHandlers = {
|
|
54
|
-
SIGINT: () => forwardSignal("SIGINT"),
|
|
55
|
-
SIGTERM: () => forwardSignal("SIGTERM"),
|
|
56
|
-
SIGHUP: () => forwardSignal("SIGHUP"),
|
|
57
|
-
};
|
|
58
|
-
for (const [name, handler] of Object.entries(signalHandlers)) {
|
|
59
|
-
process.on(name, handler);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
child.on("exit", (code, signal) => {
|
|
63
|
-
if (signal) {
|
|
64
|
-
for (const [name, handler] of Object.entries(signalHandlers)) {
|
|
65
|
-
process.off(name, handler);
|
|
66
|
-
}
|
|
67
|
-
process.kill(process.pid, signal);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
process.exit(code ?? 0);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const { detectTarget } = require("./lib/detect-target");
|
|
6
|
+
const { resolvePlatformBinary } = require("./lib/resolve-binary");
|
|
7
|
+
const { buildStdio } = require("./lib/control-fd");
|
|
8
|
+
const { launch } = require("./lib/launch");
|
|
9
|
+
|
|
10
|
+
function main() {
|
|
11
|
+
let target;
|
|
12
|
+
try {
|
|
13
|
+
target = detectTarget();
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error(`linco: ${error.message}`);
|
|
16
|
+
console.error("Hint: set LINCO_NPM_TARGET to override platform detection.");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let binary;
|
|
21
|
+
try {
|
|
22
|
+
binary = resolvePlatformBinary(target);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(error.message);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
fs.chmodSync(binary, 0o755);
|
|
30
|
+
} catch {
|
|
31
|
+
// best-effort
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Not a bare `stdio: "inherit"`: that is three slots and would drop the /space
|
|
35
|
+
// control fd codem-bridge passes as fd 3. See lib/control-fd.js.
|
|
36
|
+
const child = launch({
|
|
37
|
+
binary,
|
|
38
|
+
args: process.argv.slice(2),
|
|
39
|
+
stdio: buildStdio(),
|
|
40
|
+
});
|
|
41
|
+
if (!child) {
|
|
42
|
+
// launch() already reported the failure and called exit(1).
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const forwardSignal = (signal) => {
|
|
47
|
+
try {
|
|
48
|
+
child.kill(signal);
|
|
49
|
+
} catch {
|
|
50
|
+
// child may already be exiting
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const signalHandlers = {
|
|
54
|
+
SIGINT: () => forwardSignal("SIGINT"),
|
|
55
|
+
SIGTERM: () => forwardSignal("SIGTERM"),
|
|
56
|
+
SIGHUP: () => forwardSignal("SIGHUP"),
|
|
57
|
+
};
|
|
58
|
+
for (const [name, handler] of Object.entries(signalHandlers)) {
|
|
59
|
+
process.on(name, handler);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
child.on("exit", (code, signal) => {
|
|
63
|
+
if (signal) {
|
|
64
|
+
for (const [name, handler] of Object.entries(signalHandlers)) {
|
|
65
|
+
process.off(name, handler);
|
|
66
|
+
}
|
|
67
|
+
process.kill(process.pid, signal);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
process.exit(code ?? 0);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-codem/codem-core",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.11",
|
|
4
4
|
"description": "CodeM CLI.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"bin": {
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
"LICENSE"
|
|
12
12
|
],
|
|
13
13
|
"optionalDependencies": {
|
|
14
|
-
"@lark-codem/codem-core-darwin-arm64": "0.8.
|
|
15
|
-
"@lark-codem/codem-core-darwin-x64": "0.8.
|
|
16
|
-
"@lark-codem/codem-core-linux-x64-gnu": "0.8.
|
|
17
|
-
"@lark-codem/codem-core-linux-arm64-gnu": "0.8.
|
|
18
|
-
"@lark-codem/codem-core-win32-arm64-gnu": "0.8.
|
|
19
|
-
"@lark-codem/codem-core-win32-x64-msvc": "0.8.
|
|
14
|
+
"@lark-codem/codem-core-darwin-arm64": "0.8.11",
|
|
15
|
+
"@lark-codem/codem-core-darwin-x64": "0.8.11",
|
|
16
|
+
"@lark-codem/codem-core-linux-x64-gnu": "0.8.11",
|
|
17
|
+
"@lark-codem/codem-core-linux-arm64-gnu": "0.8.11",
|
|
18
|
+
"@lark-codem/codem-core-win32-arm64-gnu": "0.8.11",
|
|
19
|
+
"@lark-codem/codem-core-win32-x64-msvc": "0.8.11"
|
|
20
20
|
},
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=18"
|