@lark-codem/codem-cli 0.1.141
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 -0
- package/bin/codem-service.js +113 -0
- package/bin/codem.js +112 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +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.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
+
|
|
8
|
+
// Inlined from src/runtime-install-form.js#isRuntimeManagedInstall(保持逐字节
|
|
9
|
+
// 同语义,二者需同步改):主包不再发布 dist/,本入口必须自包含。
|
|
10
|
+
function canonicalPath(filePath) {
|
|
11
|
+
const resolved = path.resolve(filePath);
|
|
12
|
+
const realpath = realpathSync.native ?? realpathSync;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
return realpath(resolved);
|
|
16
|
+
} catch {
|
|
17
|
+
return resolved;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isRuntimeManagedInstall({ cliRoot, codemHome }) {
|
|
22
|
+
const versionsRoot = canonicalPath(path.join(codemHome, "runtime", "versions"));
|
|
23
|
+
const actualCliRoot = canonicalPath(cliRoot);
|
|
24
|
+
const relative = path.relative(versionsRoot, actualCliRoot);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
relative !== "" &&
|
|
28
|
+
relative !== ".." &&
|
|
29
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
30
|
+
!path.isAbsolute(relative)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const CODEM_HOME = process.env.CODEM_HOME || path.join(os.homedir(), ".codem");
|
|
35
|
+
const RUNTIME_ROOT = path.join(CODEM_HOME, "runtime");
|
|
36
|
+
const CURRENT_POINTER_PATH = path.join(RUNTIME_ROOT, "current.json");
|
|
37
|
+
// Must match the store layout written by RuntimeManager (runtime-manager.js
|
|
38
|
+
// RUNTIME_CLI_PACKAGE_NAME) and resolved by the stable service shim in
|
|
39
|
+
// service-manager.js. 存量兼容:scope 改名(@byted-meego → @lark-codem)前
|
|
40
|
+
// 落盘的版本目录还在旧 scope 下,探测新名优先、旧名兜底。
|
|
41
|
+
const PACKAGE_NAME = "@lark-codem/codem-cli";
|
|
42
|
+
const LEGACY_PACKAGE_NAME = "@byted-meego/codem-cli";
|
|
43
|
+
|
|
44
|
+
async function pathExists(filePath) {
|
|
45
|
+
try {
|
|
46
|
+
await fs.access(filePath);
|
|
47
|
+
return true;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Same install-form predicate as service-manager.js#isRuntimeManaged: this entry
|
|
54
|
+
// belongs to the managed architecture only when the file itself lives inside
|
|
55
|
+
// ~/.codem/runtime/versions/. A legacy (global npm) install must always run its
|
|
56
|
+
// own copy and never follow current.json — otherwise a runtime store left behind
|
|
57
|
+
// on a legacy host would silently capture the daemon while the on-PATH CLI keeps
|
|
58
|
+
// updating in place (the two copies diverge).
|
|
59
|
+
function isRuntimeManagedHere() {
|
|
60
|
+
const cliRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
61
|
+
return isRuntimeManagedInstall({ cliRoot, codemHome: CODEM_HOME });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function readCurrentRuntimeVersion() {
|
|
65
|
+
try {
|
|
66
|
+
const pointer = JSON.parse(await fs.readFile(CURRENT_POINTER_PATH, "utf8"));
|
|
67
|
+
return typeof pointer.version === "string" && pointer.version.trim()
|
|
68
|
+
? pointer.version.trim()
|
|
69
|
+
: null;
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function resolveRuntimeEntry(version) {
|
|
76
|
+
if (!version) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const versionDir = path.join(RUNTIME_ROOT, "versions", version);
|
|
81
|
+
const directEntry = path.join(versionDir, "bin", "codem.js");
|
|
82
|
+
|
|
83
|
+
if (await pathExists(directEntry)) {
|
|
84
|
+
return directEntry;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const packageName of [PACKAGE_NAME, LEGACY_PACKAGE_NAME]) {
|
|
88
|
+
const scopedEntry = path.join(versionDir, "node_modules", ...packageName.split("/"), "bin", "codem.js");
|
|
89
|
+
|
|
90
|
+
if (await pathExists(scopedEntry)) {
|
|
91
|
+
return scopedEntry;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 开发态逃生门:跳过 runtime 指针,直接加载本仓源码。否则自动更新会把
|
|
99
|
+
// 指针自愈回发布版 runtime,本地源码改动永远不会被 daemon 加载。
|
|
100
|
+
const forceSource = process.env.CODEM_DAEMON_DEV_SOURCE === "1";
|
|
101
|
+
const runtimeEntry =
|
|
102
|
+
forceSource || !isRuntimeManagedHere()
|
|
103
|
+
? null
|
|
104
|
+
: await resolveRuntimeEntry(await readCurrentRuntimeVersion());
|
|
105
|
+
|
|
106
|
+
if (runtimeEntry) {
|
|
107
|
+
await import(pathToFileURL(runtimeEntry));
|
|
108
|
+
} else {
|
|
109
|
+
if (forceSource) {
|
|
110
|
+
console.log("[codem-service] CODEM_DAEMON_DEV_SOURCE=1,加载本地 CLI 入口 ./codem.js");
|
|
111
|
+
}
|
|
112
|
+
await import("./codem.js");
|
|
113
|
+
}
|
package/bin/codem.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Thin launcher for the compiled CodeM CLI binary.
|
|
3
|
+
//
|
|
4
|
+
// 主包 @lark-codem/codem-cli 不再携带可运行的 JS 源码:真正的 CLI 是
|
|
5
|
+
// bun --compile 产物,按平台拆包发布在 optionalDependencies
|
|
6
|
+
// `@lark-codem/codem-cli-<os>-<arch>` 里(npm 依据平台包 package.json 的
|
|
7
|
+
// os/cpu 字段自动只装匹配的那个)。本文件是保留给所有历史消费方的稳定入口
|
|
8
|
+
// (npm bin 链接、daemon-service.cjs、codem-app 的 CodemCLIResolver、
|
|
9
|
+
// codem-shim 等都指向 bin/codem.js):解析平台包里的二进制,spawn 并透传
|
|
10
|
+
// stdio / 信号 / 退出码。
|
|
11
|
+
//
|
|
12
|
+
// 开发态兜底:repo checkout 里没有平台包,若 ../dist/src/index.js 存在
|
|
13
|
+
// (npm run build 产物),直接 import 执行——`node bin/codem.js` 在仓内可用。
|
|
14
|
+
|
|
15
|
+
import { spawn } from "node:child_process";
|
|
16
|
+
import fs from "node:fs";
|
|
17
|
+
import { createRequire } from "node:module";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
20
|
+
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
const LAUNCHER_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const PACKAGE_ROOT = path.resolve(LAUNCHER_DIR, "..");
|
|
24
|
+
// scope 从自己的安装落点推导(…/node_modules/<scope>/codem-cli/bin/codem.js),
|
|
25
|
+
// 不能写死单一 scope:历史 @byted-meego(bnpm)与公网 @lark-codem 两套包树
|
|
26
|
+
// 都用这同一份 launcher,平台包 scope 跟随主包。非 npm 布局(scope 目录名
|
|
27
|
+
// 不带 @)回退发布 scope。
|
|
28
|
+
const SCOPE_DIR_NAME = path.basename(path.dirname(PACKAGE_ROOT));
|
|
29
|
+
const PACKAGE_SCOPE = SCOPE_DIR_NAME.startsWith("@") ? SCOPE_DIR_NAME : "@lark-codem";
|
|
30
|
+
const PLATFORM_PACKAGE_NAME = `${PACKAGE_SCOPE}/codem-cli-${process.platform}-${process.arch}`;
|
|
31
|
+
const BINARY_NAME = process.platform === "win32" ? "codem.exe" : "codem";
|
|
32
|
+
|
|
33
|
+
function resolvePlatformBinary() {
|
|
34
|
+
// 常规路径:npm 已把平台包装进依赖树,走标准解析。
|
|
35
|
+
try {
|
|
36
|
+
const manifestPath = require.resolve(`${PLATFORM_PACKAGE_NAME}/package.json`);
|
|
37
|
+
return path.join(path.dirname(manifestPath), "bin", BINARY_NAME);
|
|
38
|
+
} catch {
|
|
39
|
+
// fall through to explicit probes
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 兜底探测:主包自己的 node_modules 与同 scope 兄弟目录(npm 提升安装)。
|
|
43
|
+
const [scope, name] = PLATFORM_PACKAGE_NAME.split("/");
|
|
44
|
+
const candidates = [
|
|
45
|
+
path.join(PACKAGE_ROOT, "node_modules", scope, name, "bin", BINARY_NAME),
|
|
46
|
+
path.join(path.dirname(PACKAGE_ROOT), name, "bin", BINARY_NAME)
|
|
47
|
+
];
|
|
48
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) ?? null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const binaryPath = resolvePlatformBinary();
|
|
52
|
+
|
|
53
|
+
if (binaryPath) {
|
|
54
|
+
const child = spawn(binaryPath, process.argv.slice(2), { stdio: "inherit" });
|
|
55
|
+
|
|
56
|
+
// 终端会把 SIGINT/SIGHUP 广播给整个前台进程组,但 execFile timeout、
|
|
57
|
+
// child.kill()、launchd/systemd 等调用方也会只命中 launcher PID。三类终止
|
|
58
|
+
// 信号都显式转发,保证后一种场景不会留下 Bun 子进程;child.kill 只命中
|
|
59
|
+
// 直接子进程,不会把同一信号再次广播给它下面的 linco 进程组。
|
|
60
|
+
const signalHandlers = new Map();
|
|
61
|
+
for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"]) {
|
|
62
|
+
const handler = () => {
|
|
63
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
child.kill(signal);
|
|
68
|
+
} catch {
|
|
69
|
+
// child already gone
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
signalHandlers.set(signal, handler);
|
|
73
|
+
process.on(signal, handler);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
child.on("error", (error) => {
|
|
77
|
+
console.error(`codem: 无法启动 CLI 二进制(${binaryPath}):${error.message}`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
child.on("exit", (code, signal) => {
|
|
82
|
+
for (const [registeredSignal, handler] of signalHandlers) {
|
|
83
|
+
process.removeListener(registeredSignal, handler);
|
|
84
|
+
}
|
|
85
|
+
if (signal) {
|
|
86
|
+
// 让退出信号对上游可见:自己的 handler 已摘掉,可以安全补发同名信号。
|
|
87
|
+
process.kill(process.pid, signal);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
process.exit(code ?? 0);
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
// 两个候选覆盖两种落点:仓根 bin/(→ dist/src)与构建产物 dist/bin/(→ ../src)。
|
|
94
|
+
const devEntry = [
|
|
95
|
+
path.join(PACKAGE_ROOT, "dist", "src", "index.js"),
|
|
96
|
+
path.join(PACKAGE_ROOT, "src", "index.js")
|
|
97
|
+
].find((candidate) => fs.existsSync(candidate));
|
|
98
|
+
if (devEntry) {
|
|
99
|
+
const { main } = await import(pathToFileURL(devEntry));
|
|
100
|
+
main().catch((error) => {
|
|
101
|
+
console.error(error.message);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
console.error(
|
|
106
|
+
`codem: 未找到平台 CLI 包 ${PLATFORM_PACKAGE_NAME}。` +
|
|
107
|
+
"通常是安装时 optional 依赖被跳过(npm install --no-optional)或当前平台暂不支持;" +
|
|
108
|
+
"请重新安装 CodeM CLI。"
|
|
109
|
+
);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lark-codem/codem-cli",
|
|
3
|
+
"version": "0.1.141",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "CodeM local CLI for Feishu-driven coding sessions",
|
|
6
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
7
|
+
"files": [
|
|
8
|
+
"bin",
|
|
9
|
+
"LICENSE"
|
|
10
|
+
],
|
|
11
|
+
"os": [
|
|
12
|
+
"darwin",
|
|
13
|
+
"linux",
|
|
14
|
+
"win32"
|
|
15
|
+
],
|
|
16
|
+
"cpu": [
|
|
17
|
+
"arm64",
|
|
18
|
+
"x64"
|
|
19
|
+
],
|
|
20
|
+
"bin": {
|
|
21
|
+
"codem": "bin/codem.js",
|
|
22
|
+
"codem-cli": "bin/codem.js"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"registry": "https://registry.npmjs.org",
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@lark-codem/codem-core": "latest"
|
|
30
|
+
},
|
|
31
|
+
"optionalDependencies": {
|
|
32
|
+
"@lark-codem/codem-cli-darwin-arm64": "0.1.141",
|
|
33
|
+
"@lark-codem/codem-cli-darwin-x64": "0.1.141",
|
|
34
|
+
"@lark-codem/codem-cli-linux-arm64": "0.1.141",
|
|
35
|
+
"@lark-codem/codem-cli-linux-x64": "0.1.141",
|
|
36
|
+
"@lark-codem/codem-cli-win32-arm64": "0.1.141",
|
|
37
|
+
"@lark-codem/codem-cli-win32-x64": "0.1.141"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
}
|
|
42
|
+
}
|