larkin 0.2.70
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/CONTRIBUTING.md +48 -0
- package/LICENSE +201 -0
- package/README.md +122 -0
- package/SECURITY.md +14 -0
- package/artifacts/release/THIRD_PARTY_NOTICES.txt +5488 -0
- package/dist/agent/agent-cli-capabilities.cjs +43 -0
- package/dist/agent/agent-cli-capabilities.mjs +35 -0
- package/dist/agent/agent-state-store.cjs +961 -0
- package/dist/agent/agent-state-store.mjs +923 -0
- package/dist/agent/agent-transport.cjs +201 -0
- package/dist/agent/agent-transport.mjs +194 -0
- package/dist/agent/config-cli-contract.cjs +19 -0
- package/dist/agent/config-cli-contract.mjs +16 -0
- package/dist/agent/context-prompt.cjs +159 -0
- package/dist/agent/context-prompt.mjs +155 -0
- package/dist/agent/freshness-gate.mjs +24 -0
- package/dist/agent/history-projection.cjs +72 -0
- package/dist/agent/history-projection.mjs +65 -0
- package/dist/agent/host-reminder-orchestrator.cjs +289 -0
- package/dist/agent/host-reminder-orchestrator.mjs +252 -0
- package/dist/agent/inbox-projection.cjs +92 -0
- package/dist/agent/inbox-projection.mjs +85 -0
- package/dist/agent/interaction-state-machine.cjs +710 -0
- package/dist/agent/interaction-state-machine.mjs +705 -0
- package/dist/agent/reminder-routes.cjs +192 -0
- package/dist/agent/reminder-routes.mjs +156 -0
- package/dist/agent/reminder-store.cjs +220 -0
- package/dist/agent/reminder-store.mjs +205 -0
- package/dist/agent/transport-business-context.cjs +486 -0
- package/dist/agent/transport-business-context.mjs +446 -0
- package/dist/agent/transport-shell.cjs +45 -0
- package/dist/agent/transport-shell.mjs +39 -0
- package/dist/app/agent-cli.mjs +596 -0
- package/dist/app/agent-config.mjs +633 -0
- package/dist/app/agent-lark-cli-workspace.cjs +95 -0
- package/dist/app/agent-lark-cli-workspace.mjs +85 -0
- package/dist/app/agent-readiness.cjs +42 -0
- package/dist/app/agent-readiness.mjs +38 -0
- package/dist/app/binary-entry.mjs +86 -0
- package/dist/app/cli.mjs +176 -0
- package/dist/app/dashboard.mjs +205 -0
- package/dist/app/internal-command.cjs +57 -0
- package/dist/app/internal-command.mjs +48 -0
- package/dist/app/lark-channel-secret.mjs +22 -0
- package/dist/app/lark-cli.mjs +887 -0
- package/dist/app/lark.mjs +45 -0
- package/dist/app/local-control.mjs +728 -0
- package/dist/app/official-lark-cli.cjs +138 -0
- package/dist/app/official-lark-cli.mjs +128 -0
- package/dist/app/pi-auth-cli.mjs +67 -0
- package/dist/app/run.mjs +364 -0
- package/dist/app/runtime-agent-config.mjs +416 -0
- package/dist/app/runtime-model-directory.mjs +78 -0
- package/dist/app/runtime-process.mjs +113 -0
- package/dist/app/session-cli.mjs +83 -0
- package/dist/app/setup-dashboard.mjs +26 -0
- package/dist/app/setup.mjs +166 -0
- package/dist/app/telemetry.mjs +83 -0
- package/dist/dashboard/dashboard-assets.mjs +21 -0
- package/dist/dashboard/dashboard-avatar.mjs +128 -0
- package/dist/dashboard/dashboard-config-controller.mjs +525 -0
- package/dist/dashboard/dashboard-lifecycle.cjs +97 -0
- package/dist/dashboard/dashboard-lifecycle.mjs +88 -0
- package/dist/dashboard/dashboard-template.mjs +29 -0
- package/dist/dashboard/dashboard-view-model.mjs +556 -0
- package/dist/dashboard/dashboard-workspace.mjs +104 -0
- package/dist/dashboard/web/assets/dashboard.css +3 -0
- package/dist/dashboard/web/assets/dashboard.js +49 -0
- package/dist/feishu/channel-transport.cjs +151 -0
- package/dist/feishu/channel-transport.mjs +148 -0
- package/dist/feishu/document-comment.cjs +120 -0
- package/dist/feishu/document-comment.mjs +109 -0
- package/dist/feishu/host-business-state.cjs +477 -0
- package/dist/feishu/host-business-state.mjs +462 -0
- package/dist/feishu/host-channel-business.cjs +161 -0
- package/dist/feishu/host-channel-business.mjs +157 -0
- package/dist/feishu/host-processing-eye.cjs +185 -0
- package/dist/feishu/host-processing-eye.mjs +181 -0
- package/dist/feishu/host-shell.cjs +1425 -0
- package/dist/feishu/host-shell.mjs +1385 -0
- package/dist/feishu/im-freshness-adapter.mjs +89 -0
- package/dist/feishu/interaction-orchestrator.cjs +279 -0
- package/dist/feishu/interaction-orchestrator.mjs +242 -0
- package/dist/feishu/lark-passthrough.mjs +70 -0
- package/dist/feishu/message-policy.cjs +111 -0
- package/dist/feishu/message-policy.mjs +98 -0
- package/dist/feishu/outbound-transport.cjs +221 -0
- package/dist/feishu/outbound-transport.mjs +215 -0
- package/dist/platform/build-info.cjs +25 -0
- package/dist/platform/build-info.mjs +18 -0
- package/dist/platform/callback-capability.cjs +301 -0
- package/dist/platform/callback-capability.mjs +252 -0
- package/dist/platform/check-bun-version.cjs +12 -0
- package/dist/platform/config.cjs +751 -0
- package/dist/platform/config.mjs +694 -0
- package/dist/platform/process-inspect.cjs +89 -0
- package/dist/platform/process-state.cjs +237 -0
- package/dist/platform/process-state.mjs +223 -0
- package/dist/platform/release-artifacts.mjs +61 -0
- package/dist/platform/root-layout.cjs +111 -0
- package/dist/platform/root-layout.mjs +72 -0
- package/dist/platform/telemetry-config.cjs +64 -0
- package/dist/platform/telemetry-config.mjs +56 -0
- package/dist/platform/telemetry-spool.cjs +661 -0
- package/dist/platform/telemetry-spool.mjs +653 -0
- package/dist/platform/telemetry-tracing.cjs +602 -0
- package/dist/platform/telemetry-tracing.mjs +596 -0
- package/dist/platform/telemetry-uploader.cjs +150 -0
- package/dist/platform/telemetry-uploader.mjs +146 -0
- package/dist/platform/workspace-service.cjs +469 -0
- package/dist/platform/workspace-service.mjs +430 -0
- package/dist/runtime/builtin-pi-assets.mjs +33 -0
- package/dist/runtime/claude-model-catalog.cjs +102 -0
- package/dist/runtime/claude-model-catalog.mjs +99 -0
- package/dist/runtime/codex-model-catalog.cjs +108 -0
- package/dist/runtime/codex-model-catalog.mjs +105 -0
- package/dist/runtime/pi-bash-timeout-extension.mjs +60 -0
- package/dist/runtime/pi-bash-timeout-injection.mjs +60 -0
- package/dist/runtime/pi-bash-timeout.bundle.js +43 -0
- package/dist/runtime/pi-model-catalog.cjs +116 -0
- package/dist/runtime/pi-model-catalog.mjs +75 -0
- package/dist/runtime/pi-official-auth.mjs +480 -0
- package/dist/runtime/pi-provider-config.cjs +252 -0
- package/dist/runtime/pi-provider-config.mjs +239 -0
- package/dist/runtime/pi-rpc-client.cjs +255 -0
- package/dist/runtime/pi-rpc-client.mjs +251 -0
- package/dist/runtime/pi-subagent-injection.mjs +129 -0
- package/dist/runtime/pi-subagents.bundle.js +8990 -0
- package/dist/runtime/runtime-adapters.mjs +1019 -0
- package/dist/runtime/runtime-contracts.cjs +2 -0
- package/dist/runtime/runtime-contracts.mjs +1 -0
- package/dist/runtime/runtime-host.cjs +930 -0
- package/dist/runtime/runtime-host.mjs +922 -0
- package/dist/runtime/runtime-model-catalog.cjs +43 -0
- package/dist/runtime/runtime-model-catalog.mjs +40 -0
- package/dist/runtime/runtime-readiness.cjs +177 -0
- package/dist/runtime/runtime-readiness.mjs +133 -0
- package/dist/setup/bot-register.mjs +655 -0
- package/dist/setup/grant-scopes.mjs +133 -0
- package/dist/setup/run-credential-preflight.mjs +89 -0
- package/dist/setup/setup-agent-choice.mjs +172 -0
- package/dist/setup/setup-bind.mjs +337 -0
- package/dist/setup/setup-binding.mjs +52 -0
- package/package.json +112 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.larkChannelSourceConfigPath = larkChannelSourceConfigPath;
|
|
7
|
+
exports.larkChannelWorkspaceConfigPath = larkChannelWorkspaceConfigPath;
|
|
8
|
+
exports.managedLarkCliEnv = managedLarkCliEnv;
|
|
9
|
+
exports.managedOfficialLarkCli = managedOfficialLarkCli;
|
|
10
|
+
exports.assertAgentWorkspaceBound = assertAgentWorkspaceBound;
|
|
11
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
13
|
+
const official_lark_cli_js_1 = require("./official-lark-cli.cjs");
|
|
14
|
+
function larkChannelSourceConfigPath(agent) {
|
|
15
|
+
if (!agent.stateDir)
|
|
16
|
+
throw new Error(`Agent ${agent.agentId} 缺少 stateDir,无法建立 lark-channel binding`);
|
|
17
|
+
return node_path_1.default.join(node_path_1.default.resolve(agent.stateDir), "lark-channel-source", "config.json");
|
|
18
|
+
}
|
|
19
|
+
function larkChannelWorkspaceConfigPath(agent) {
|
|
20
|
+
if (!agent.larkConfigDir)
|
|
21
|
+
throw new Error(`Agent ${agent.agentId} 缺少 larkConfigDir,无法建立 lark-channel binding`);
|
|
22
|
+
return node_path_1.default.join(node_path_1.default.resolve(agent.larkConfigDir), "lark-channel", "config.json");
|
|
23
|
+
}
|
|
24
|
+
function managedLarkCliEnv(agent, env) {
|
|
25
|
+
if (!agent.larkConfigDir)
|
|
26
|
+
throw new Error(`Agent ${agent.agentId} 缺少 larkConfigDir,拒绝回退 local workspace`);
|
|
27
|
+
const isolated = { ...env };
|
|
28
|
+
for (const key of ["OPENCLAW_CLI", "OPENCLAW_HOME", "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_SERVICE_MARKER",
|
|
29
|
+
"OPENCLAW_SERVICE_VERSION", "OPENCLAW_GATEWAY_PORT", "OPENCLAW_SHELL", "HERMES_HOME", "HERMES_QUIET", "HERMES_EXEC_ASK",
|
|
30
|
+
"HERMES_GATEWAY_TOKEN", "HERMES_SESSION_KEY"])
|
|
31
|
+
delete isolated[key];
|
|
32
|
+
return {
|
|
33
|
+
...isolated,
|
|
34
|
+
LARK_CHANNEL: "1",
|
|
35
|
+
LARK_CHANNEL_CONFIG: larkChannelSourceConfigPath(agent),
|
|
36
|
+
LARKSUITE_CLI_CONFIG_DIR: node_path_1.default.resolve(agent.larkConfigDir),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function managedOfficialLarkCli(agent, env, resolve = official_lark_cli_js_1.resolveOfficialLarkCli) {
|
|
40
|
+
assertAgentWorkspaceBound(agent);
|
|
41
|
+
const managedEnv = managedLarkCliEnv(agent, env);
|
|
42
|
+
return { command: resolve({ env: managedEnv }), env: managedEnv };
|
|
43
|
+
}
|
|
44
|
+
function readPrivateJson(file, label) {
|
|
45
|
+
const directory = node_fs_1.default.lstatSync(node_path_1.default.dirname(file));
|
|
46
|
+
if (!directory.isDirectory() || directory.isSymbolicLink() || (directory.mode & 0o777) !== 0o700
|
|
47
|
+
|| (typeof process.getuid === "function" && directory.uid !== process.getuid()))
|
|
48
|
+
throw new Error(`${label} 目录不安全`);
|
|
49
|
+
const fd = node_fs_1.default.openSync(file, node_fs_1.default.constants.O_RDONLY | (node_fs_1.default.constants.O_NOFOLLOW || 0));
|
|
50
|
+
try {
|
|
51
|
+
const stat = node_fs_1.default.fstatSync(fd);
|
|
52
|
+
if (!stat.isFile() || (stat.mode & 0o777) !== 0o600
|
|
53
|
+
|| (typeof process.getuid === "function" && stat.uid !== process.getuid()))
|
|
54
|
+
throw new Error(`${label} 文件不安全`);
|
|
55
|
+
return JSON.parse(node_fs_1.default.readFileSync(fd, "utf8"));
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
node_fs_1.default.closeSync(fd);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function assertAgentWorkspaceBound(agent) {
|
|
62
|
+
if (!agent.feishuAppId)
|
|
63
|
+
throw new Error(`Agent ${agent.agentId} 缺少 feishuAppId,拒绝回退 local workspace`);
|
|
64
|
+
let source;
|
|
65
|
+
let workspace;
|
|
66
|
+
try {
|
|
67
|
+
source = readPrivateJson(larkChannelSourceConfigPath(agent), "lark-channel source projection");
|
|
68
|
+
workspace = readPrivateJson(larkChannelWorkspaceConfigPath(agent), "lark-channel workspace");
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error.code === "ENOENT")
|
|
72
|
+
throw new Error(`Agent ${agent.agentId} lark-channel workspace 未绑定;运行 larkin setup`);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
const sourceApp = source.accounts?.app;
|
|
76
|
+
const provider = source.secrets?.providers?.["larkin-bot-credential"];
|
|
77
|
+
if (sourceApp?.id !== agent.feishuAppId || sourceApp?.secret?.source !== "exec"
|
|
78
|
+
|| sourceApp?.secret?.provider !== "larkin-bot-credential" || sourceApp?.secret?.id !== agent.feishuAppId
|
|
79
|
+
|| provider?.source !== "exec" || typeof provider.command !== "string" || !node_path_1.default.isAbsolute(provider.command)
|
|
80
|
+
|| !Array.isArray(provider.args) || provider.env?.LARKIN_AGENT_ID !== agent.agentId
|
|
81
|
+
|| provider.env?.LARKIN_SECRET_PROVIDER_CONTEXT !== "bind") {
|
|
82
|
+
throw new Error(`Agent ${agent.agentId} lark-channel source projection 错配`);
|
|
83
|
+
}
|
|
84
|
+
if (agent.credentialRevision && source.credentialRevision !== agent.credentialRevision) {
|
|
85
|
+
throw new Error(`Agent ${agent.agentId} lark-channel credential revision 已过期;运行 larkin setup`);
|
|
86
|
+
}
|
|
87
|
+
const apps = workspace.apps;
|
|
88
|
+
const app = Array.isArray(apps) && apps.length === 1 ? apps[0] : null;
|
|
89
|
+
if (!app || app.appId !== agent.feishuAppId || app.defaultAs !== "bot" || app.strictMode !== "bot"
|
|
90
|
+
|| app.appSecret?.source !== "keychain" || app.appSecret?.id !== `appsecret:${agent.feishuAppId}`
|
|
91
|
+
|| (Array.isArray(app.users) ? app.users.length !== 0
|
|
92
|
+
: app.users && typeof app.users === "object" ? Object.keys(app.users).length !== 0 : app.users !== undefined && app.users !== null)) {
|
|
93
|
+
throw new Error(`Agent ${agent.agentId} lark-channel workspace identity/bot-only policy 错配;运行 larkin setup`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { resolveOfficialLarkCli } from "./official-lark-cli.mjs";
|
|
4
|
+
export function larkChannelSourceConfigPath(agent) {
|
|
5
|
+
if (!agent.stateDir)
|
|
6
|
+
throw new Error(`Agent ${agent.agentId} 缺少 stateDir,无法建立 lark-channel binding`);
|
|
7
|
+
return path.join(path.resolve(agent.stateDir), "lark-channel-source", "config.json");
|
|
8
|
+
}
|
|
9
|
+
export function larkChannelWorkspaceConfigPath(agent) {
|
|
10
|
+
if (!agent.larkConfigDir)
|
|
11
|
+
throw new Error(`Agent ${agent.agentId} 缺少 larkConfigDir,无法建立 lark-channel binding`);
|
|
12
|
+
return path.join(path.resolve(agent.larkConfigDir), "lark-channel", "config.json");
|
|
13
|
+
}
|
|
14
|
+
export function managedLarkCliEnv(agent, env) {
|
|
15
|
+
if (!agent.larkConfigDir)
|
|
16
|
+
throw new Error(`Agent ${agent.agentId} 缺少 larkConfigDir,拒绝回退 local workspace`);
|
|
17
|
+
const isolated = { ...env };
|
|
18
|
+
for (const key of ["OPENCLAW_CLI", "OPENCLAW_HOME", "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_SERVICE_MARKER",
|
|
19
|
+
"OPENCLAW_SERVICE_VERSION", "OPENCLAW_GATEWAY_PORT", "OPENCLAW_SHELL", "HERMES_HOME", "HERMES_QUIET", "HERMES_EXEC_ASK",
|
|
20
|
+
"HERMES_GATEWAY_TOKEN", "HERMES_SESSION_KEY"])
|
|
21
|
+
delete isolated[key];
|
|
22
|
+
return {
|
|
23
|
+
...isolated,
|
|
24
|
+
LARK_CHANNEL: "1",
|
|
25
|
+
LARK_CHANNEL_CONFIG: larkChannelSourceConfigPath(agent),
|
|
26
|
+
LARKSUITE_CLI_CONFIG_DIR: path.resolve(agent.larkConfigDir),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function managedOfficialLarkCli(agent, env, resolve = resolveOfficialLarkCli) {
|
|
30
|
+
assertAgentWorkspaceBound(agent);
|
|
31
|
+
const managedEnv = managedLarkCliEnv(agent, env);
|
|
32
|
+
return { command: resolve({ env: managedEnv }), env: managedEnv };
|
|
33
|
+
}
|
|
34
|
+
function readPrivateJson(file, label) {
|
|
35
|
+
const directory = fs.lstatSync(path.dirname(file));
|
|
36
|
+
if (!directory.isDirectory() || directory.isSymbolicLink() || (directory.mode & 0o777) !== 0o700
|
|
37
|
+
|| (typeof process.getuid === "function" && directory.uid !== process.getuid()))
|
|
38
|
+
throw new Error(`${label} 目录不安全`);
|
|
39
|
+
const fd = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
|
40
|
+
try {
|
|
41
|
+
const stat = fs.fstatSync(fd);
|
|
42
|
+
if (!stat.isFile() || (stat.mode & 0o777) !== 0o600
|
|
43
|
+
|| (typeof process.getuid === "function" && stat.uid !== process.getuid()))
|
|
44
|
+
throw new Error(`${label} 文件不安全`);
|
|
45
|
+
return JSON.parse(fs.readFileSync(fd, "utf8"));
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
fs.closeSync(fd);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function assertAgentWorkspaceBound(agent) {
|
|
52
|
+
if (!agent.feishuAppId)
|
|
53
|
+
throw new Error(`Agent ${agent.agentId} 缺少 feishuAppId,拒绝回退 local workspace`);
|
|
54
|
+
let source;
|
|
55
|
+
let workspace;
|
|
56
|
+
try {
|
|
57
|
+
source = readPrivateJson(larkChannelSourceConfigPath(agent), "lark-channel source projection");
|
|
58
|
+
workspace = readPrivateJson(larkChannelWorkspaceConfigPath(agent), "lark-channel workspace");
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (error.code === "ENOENT")
|
|
62
|
+
throw new Error(`Agent ${agent.agentId} lark-channel workspace 未绑定;运行 larkin setup`);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
const sourceApp = source.accounts?.app;
|
|
66
|
+
const provider = source.secrets?.providers?.["larkin-bot-credential"];
|
|
67
|
+
if (sourceApp?.id !== agent.feishuAppId || sourceApp?.secret?.source !== "exec"
|
|
68
|
+
|| sourceApp?.secret?.provider !== "larkin-bot-credential" || sourceApp?.secret?.id !== agent.feishuAppId
|
|
69
|
+
|| provider?.source !== "exec" || typeof provider.command !== "string" || !path.isAbsolute(provider.command)
|
|
70
|
+
|| !Array.isArray(provider.args) || provider.env?.LARKIN_AGENT_ID !== agent.agentId
|
|
71
|
+
|| provider.env?.LARKIN_SECRET_PROVIDER_CONTEXT !== "bind") {
|
|
72
|
+
throw new Error(`Agent ${agent.agentId} lark-channel source projection 错配`);
|
|
73
|
+
}
|
|
74
|
+
if (agent.credentialRevision && source.credentialRevision !== agent.credentialRevision) {
|
|
75
|
+
throw new Error(`Agent ${agent.agentId} lark-channel credential revision 已过期;运行 larkin setup`);
|
|
76
|
+
}
|
|
77
|
+
const apps = workspace.apps;
|
|
78
|
+
const app = Array.isArray(apps) && apps.length === 1 ? apps[0] : null;
|
|
79
|
+
if (!app || app.appId !== agent.feishuAppId || app.defaultAs !== "bot" || app.strictMode !== "bot"
|
|
80
|
+
|| app.appSecret?.source !== "keychain" || app.appSecret?.id !== `appsecret:${agent.feishuAppId}`
|
|
81
|
+
|| (Array.isArray(app.users) ? app.users.length !== 0
|
|
82
|
+
: app.users && typeof app.users === "object" ? Object.keys(app.users).length !== 0 : app.users !== undefined && app.users !== null)) {
|
|
83
|
+
throw new Error(`Agent ${agent.agentId} lark-channel workspace identity/bot-only policy 错配;运行 larkin setup`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isChannelReconnecting = isChannelReconnecting;
|
|
4
|
+
exports.projectAgentReadiness = projectAgentReadiness;
|
|
5
|
+
const process_state_js_1 = require("../platform/process-state.cjs");
|
|
6
|
+
function timestamp(value) {
|
|
7
|
+
const parsed = Date.parse(String(value || ""));
|
|
8
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
9
|
+
}
|
|
10
|
+
function isChannelReconnecting(status) {
|
|
11
|
+
const reconnectingAt = timestamp(status?.reconnectingAt);
|
|
12
|
+
const connectedAt = timestamp(status?.connectedAt);
|
|
13
|
+
const reconnectedAt = timestamp(status?.reconnectedAt);
|
|
14
|
+
const latestConnectedAt = Math.max(connectedAt ?? Number.NEGATIVE_INFINITY, reconnectedAt ?? Number.NEGATIVE_INFINITY);
|
|
15
|
+
return reconnectingAt !== null && reconnectingAt > latestConnectedAt;
|
|
16
|
+
}
|
|
17
|
+
function projectAgentReadiness(input) {
|
|
18
|
+
const status = input.status ?? null;
|
|
19
|
+
const daemonOwned = (0, process_state_js_1.daemonHasAgent)(input.daemon, input.agentId);
|
|
20
|
+
const daemonStartedAt = timestamp(input.daemon.startedAt);
|
|
21
|
+
const connectedAt = timestamp(status?.connectedAt);
|
|
22
|
+
const channelConnected = daemonOwned
|
|
23
|
+
&& status?.connectedVia === "channel"
|
|
24
|
+
&& daemonStartedAt !== null
|
|
25
|
+
&& connectedAt !== null
|
|
26
|
+
&& connectedAt >= daemonStartedAt;
|
|
27
|
+
const readiness = {
|
|
28
|
+
daemon_owned: daemonOwned,
|
|
29
|
+
runtime_ready: status?.runtimeReadiness?.state === "ready",
|
|
30
|
+
channel_connected: channelConnected,
|
|
31
|
+
channel_not_reconnecting: !isChannelReconnecting(status),
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
ready: Object.values(readiness).every(Boolean),
|
|
35
|
+
readiness,
|
|
36
|
+
channel: {
|
|
37
|
+
connected_at: connectedAt === null ? null : status?.connectedAt ?? null,
|
|
38
|
+
connected_via: status?.connectedVia || null,
|
|
39
|
+
inbound_verified_at: timestamp(status?.inboundVerifiedAt) === null ? null : status?.inboundVerifiedAt ?? null,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { daemonHasAgent } from "../platform/process-state.mjs";
|
|
2
|
+
function timestamp(value) {
|
|
3
|
+
const parsed = Date.parse(String(value || ""));
|
|
4
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
5
|
+
}
|
|
6
|
+
export function isChannelReconnecting(status) {
|
|
7
|
+
const reconnectingAt = timestamp(status?.reconnectingAt);
|
|
8
|
+
const connectedAt = timestamp(status?.connectedAt);
|
|
9
|
+
const reconnectedAt = timestamp(status?.reconnectedAt);
|
|
10
|
+
const latestConnectedAt = Math.max(connectedAt ?? Number.NEGATIVE_INFINITY, reconnectedAt ?? Number.NEGATIVE_INFINITY);
|
|
11
|
+
return reconnectingAt !== null && reconnectingAt > latestConnectedAt;
|
|
12
|
+
}
|
|
13
|
+
export function projectAgentReadiness(input) {
|
|
14
|
+
const status = input.status ?? null;
|
|
15
|
+
const daemonOwned = daemonHasAgent(input.daemon, input.agentId);
|
|
16
|
+
const daemonStartedAt = timestamp(input.daemon.startedAt);
|
|
17
|
+
const connectedAt = timestamp(status?.connectedAt);
|
|
18
|
+
const channelConnected = daemonOwned
|
|
19
|
+
&& status?.connectedVia === "channel"
|
|
20
|
+
&& daemonStartedAt !== null
|
|
21
|
+
&& connectedAt !== null
|
|
22
|
+
&& connectedAt >= daemonStartedAt;
|
|
23
|
+
const readiness = {
|
|
24
|
+
daemon_owned: daemonOwned,
|
|
25
|
+
runtime_ready: status?.runtimeReadiness?.state === "ready",
|
|
26
|
+
channel_connected: channelConnected,
|
|
27
|
+
channel_not_reconnecting: !isChannelReconnecting(status),
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
ready: Object.values(readiness).every(Boolean),
|
|
31
|
+
readiness,
|
|
32
|
+
channel: {
|
|
33
|
+
connected_at: connectedAt === null ? null : status?.connectedAt ?? null,
|
|
34
|
+
connected_via: status?.connectedVia || null,
|
|
35
|
+
inbound_verified_at: timestamp(status?.inboundVerifiedAt) === null ? null : status?.inboundVerifiedAt ?? null,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { INTERNAL_COMMAND_MARKER, INTERNAL_MODES } from "./internal-command.mjs";
|
|
3
|
+
function normalizeArgv(mode, rest) {
|
|
4
|
+
process.env.LARKIN_INTERNAL_DISPATCH = "1";
|
|
5
|
+
process.argv = [process.execPath, `${INTERNAL_COMMAND_MARKER} ${mode}`, ...rest];
|
|
6
|
+
}
|
|
7
|
+
async function dispatchInternal(mode, rest) {
|
|
8
|
+
normalizeArgv(mode, rest);
|
|
9
|
+
switch (mode) {
|
|
10
|
+
case "run":
|
|
11
|
+
await (await import("./run.mjs")).main();
|
|
12
|
+
return;
|
|
13
|
+
case "setup":
|
|
14
|
+
await (await import("./setup.mjs")).main();
|
|
15
|
+
return;
|
|
16
|
+
case "runtime-process":
|
|
17
|
+
await (await import("./runtime-process.mjs")).main();
|
|
18
|
+
return;
|
|
19
|
+
case "agent-cli":
|
|
20
|
+
await (await import("./agent-cli.mjs")).main(rest, process.env);
|
|
21
|
+
return;
|
|
22
|
+
case "lark-cli":
|
|
23
|
+
process.exitCode = await (await import("./lark-cli.mjs")).runLarkCliProcess(rest, process.env);
|
|
24
|
+
return;
|
|
25
|
+
case "runtime-model-directory":
|
|
26
|
+
await (await import("./runtime-model-directory.mjs")).main();
|
|
27
|
+
return;
|
|
28
|
+
case "agent-config":
|
|
29
|
+
await import("./agent-config.mjs");
|
|
30
|
+
return;
|
|
31
|
+
case "session-cli":
|
|
32
|
+
await (await import("./session-cli.mjs")).main(rest, process.env);
|
|
33
|
+
return;
|
|
34
|
+
case "lark":
|
|
35
|
+
await import("./lark.mjs");
|
|
36
|
+
return;
|
|
37
|
+
case "dashboard":
|
|
38
|
+
await import("./dashboard.mjs");
|
|
39
|
+
return;
|
|
40
|
+
case "bot-register":
|
|
41
|
+
await (await import("../setup/bot-register.mjs")).main();
|
|
42
|
+
return;
|
|
43
|
+
case "setup-bind":
|
|
44
|
+
await (await import("../setup/setup-bind.mjs")).main();
|
|
45
|
+
return;
|
|
46
|
+
case "grant-scopes":
|
|
47
|
+
await (await import("../setup/grant-scopes.mjs")).main();
|
|
48
|
+
return;
|
|
49
|
+
case "lark-channel-secret":
|
|
50
|
+
await (await import("./lark-channel-secret.mjs")).main(process.env);
|
|
51
|
+
return;
|
|
52
|
+
case "pi-rpc": {
|
|
53
|
+
const { prepareBuiltinPiPackageAssets } = await import("../runtime/builtin-pi-assets.mjs");
|
|
54
|
+
prepareBuiltinPiPackageAssets();
|
|
55
|
+
await import("@earendil-works/pi-coding-agent/rpc-entry");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
case "pi-auth":
|
|
59
|
+
await (await import("./pi-auth-cli.mjs")).main(rest, process.env);
|
|
60
|
+
return;
|
|
61
|
+
case "telemetry":
|
|
62
|
+
await (await import("./telemetry.mjs")).main(rest, process.env);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function main() {
|
|
67
|
+
const standalone = process.env.LARKIN_STANDALONE === "1";
|
|
68
|
+
if (!standalone && process.argv[1])
|
|
69
|
+
process.env.LARKIN_BINARY_ENTRY_PATH = process.argv[1];
|
|
70
|
+
const argv = process.argv.slice(standalone ? 1 : 2);
|
|
71
|
+
if (argv[0] === INTERNAL_COMMAND_MARKER) {
|
|
72
|
+
const mode = argv[1];
|
|
73
|
+
if (!mode || !INTERNAL_MODES.includes(mode))
|
|
74
|
+
throw new Error("invalid internal command");
|
|
75
|
+
await dispatchInternal(mode, argv.slice(2));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
process.argv = [process.execPath, "larkin", ...argv];
|
|
79
|
+
await import("./cli.mjs");
|
|
80
|
+
}
|
|
81
|
+
if (typeof LARKIN_WRAPPED_ENTRY === "undefined" || !LARKIN_WRAPPED_ENTRY) {
|
|
82
|
+
main().catch((error) => {
|
|
83
|
+
process.stderr.write(`larkin: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
});
|
|
86
|
+
}
|
package/dist/app/cli.mjs
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Public larkin CLI router. This file deliberately owns only the outer shell:
|
|
3
|
+
// command selection, read-only help, child spawning, and signal/exit forwarding.
|
|
4
|
+
import "../platform/check-bun-version.cjs";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { CONFIG_CLI_USAGE } from "../agent/config-cli-contract.mjs";
|
|
9
|
+
import { internalCommandSpec } from "./internal-command.mjs";
|
|
10
|
+
import { packageVersion } from "../platform/build-info.mjs";
|
|
11
|
+
const command = process.argv[2] || "help";
|
|
12
|
+
let rest = process.argv.slice(3);
|
|
13
|
+
const PACKAGE_VERSION = packageVersion(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."));
|
|
14
|
+
const removedCommandMessages = {
|
|
15
|
+
init: "首次配置或重新授权请使用 larkin setup。",
|
|
16
|
+
"bot:connect": "首次配置或重新授权请使用 larkin setup。",
|
|
17
|
+
build: "源码构建命令已移除;仓库维护者请使用 bun run build。",
|
|
18
|
+
dashboard: "dashboard 已由 larkin start 自动启动和监控。",
|
|
19
|
+
};
|
|
20
|
+
if (removedCommandMessages[command]) {
|
|
21
|
+
console.error(`larkin: ${command} 已移除;${removedCommandMessages[command]}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const routes = {
|
|
25
|
+
start: ["run"],
|
|
26
|
+
setup: ["setup"],
|
|
27
|
+
status: ["agent-config", "agents"],
|
|
28
|
+
agents: ["agent-config", "agents"],
|
|
29
|
+
model: ["agent-config", "model"],
|
|
30
|
+
runtime: ["agent-config", "runtime"],
|
|
31
|
+
effort: ["agent-config", "effort"],
|
|
32
|
+
chats: ["agent-config", "chats"],
|
|
33
|
+
config: ["agent-config", "config"],
|
|
34
|
+
session: ["session-cli"],
|
|
35
|
+
"pi-auth": ["pi-auth"],
|
|
36
|
+
telemetry: ["telemetry"],
|
|
37
|
+
};
|
|
38
|
+
const runtimeAgentAuthority = typeof process.env.LARKIN_AGENT_ID === "string"
|
|
39
|
+
&& process.env.LARKIN_AGENT_ID.trim().length > 0;
|
|
40
|
+
const runtimeAgentCommand = runtimeAgentAuthority
|
|
41
|
+
&& ["inbox", "reminder", "interaction", "profile", "config"].includes(command);
|
|
42
|
+
if (runtimeAgentCommand)
|
|
43
|
+
routes[command] = ["agent-cli", command];
|
|
44
|
+
if (runtimeAgentAuthority && command === "comment")
|
|
45
|
+
routes.comment = ["lark-cli", "comment"];
|
|
46
|
+
// Help stays in the outer CLI so it never loads configuration, inspects a process,
|
|
47
|
+
// or starts a foreground service merely to print usage.
|
|
48
|
+
const commandHelp = {
|
|
49
|
+
start: `Usage: larkin start [--agent <App ID> | --agents <App ID,...>]
|
|
50
|
+
Start one foreground supervisor for the daemon and local dashboard, or reuse it.`,
|
|
51
|
+
setup: `Usage: larkin setup [--runtime <runtime>] [--comment-subscription <none|application>] [--no-start]
|
|
52
|
+
Run the interactive setup to create or connect a bot and configure its Agent.`,
|
|
53
|
+
status: `Usage: larkin status [--json]
|
|
54
|
+
Show Agent configuration, bot identity, credentials, and connection status. Use --json for readiness automation.`,
|
|
55
|
+
agents: `Usage: larkin agents [--json]
|
|
56
|
+
List every configured Agent and its current local status. Use --json for daemon/Runtime/channel readiness.`,
|
|
57
|
+
model: `Usage: larkin model [<model>] [--agent <App ID>]
|
|
58
|
+
Show or change an Agent model.`,
|
|
59
|
+
runtime: `Usage: larkin runtime [<runtime>] [--agent <App ID>] [--model <model>]
|
|
60
|
+
Show or change an Agent runtime.`,
|
|
61
|
+
effort: `Usage: larkin effort [<level>|clear|default] [--agent <App ID>]
|
|
62
|
+
Show or change an Agent reasoning effort; clear/default restores the Runtime default.`,
|
|
63
|
+
chats: `Usage: larkin chats [--agent <App ID>]
|
|
64
|
+
larkin chats (free|strict) <oc_id> [--agent <App ID>]
|
|
65
|
+
List known chats or configure mention requirements.`,
|
|
66
|
+
config: `Usage: ${CONFIG_CLI_USAGE.join("\n ")}
|
|
67
|
+
Inspect and safely update user-facing Larkin configuration.
|
|
68
|
+
|
|
69
|
+
Values:
|
|
70
|
+
default / clear Do not persist a model override / clear effort to Runtime default
|
|
71
|
+
inherit Clear an Agent or chat mention override
|
|
72
|
+
|
|
73
|
+
Examples:
|
|
74
|
+
larkin config show --agent cli_x --chat oc_x --json
|
|
75
|
+
larkin config runtime pi --agent cli_x --model default
|
|
76
|
+
larkin config mention chat oc_x free --agent cli_x
|
|
77
|
+
|
|
78
|
+
Credentials, internal paths, serverId, activeAgent, and raw config are never exposed here.`,
|
|
79
|
+
session: `Usage: larkin session reset --agent <App ID> --json [--wait-ready <seconds>]
|
|
80
|
+
Atomically replace one idle, zero-backlog Agent Runtime session through authenticated local control.`,
|
|
81
|
+
"pi-auth": `Usage: larkin pi-auth status [--agent <App ID>] [--json]
|
|
82
|
+
larkin pi-auth logout <provider> [--agent <App ID>]
|
|
83
|
+
Show non-sensitive official Pi credential metadata or remove one target provider credential.`,
|
|
84
|
+
comment: `Usage: larkin comment reply --message-id <doc_comment_message_id> --text '<reply>' --json
|
|
85
|
+
Reply once, as the Runtime-bound Bot, to the exact cloud-document comment locator supplied by canonical Inbox.`,
|
|
86
|
+
telemetry: `Usage: larkin telemetry <status|export|import|flush>
|
|
87
|
+
Inspect the durable local trace queue, move an offline bundle, or upload queued OTLP traces.`,
|
|
88
|
+
};
|
|
89
|
+
if (command === "--version" || command === "-V") {
|
|
90
|
+
console.log(`larkin ${PACKAGE_VERSION}`);
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
if (command === "help" && rest[0] && commandHelp[rest[0]]) {
|
|
94
|
+
console.log(commandHelp[rest[0]]);
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
if (!runtimeAgentCommand && routes[command] && rest.some((argument) => argument === "--help" || argument === "-h")) {
|
|
98
|
+
console.log(commandHelp[command]);
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
if (!runtimeAgentCommand && command === "config" && ["runtime", "model", "effort"].includes(rest[0] || "")) {
|
|
102
|
+
routes.config = ["agent-config", rest[0]];
|
|
103
|
+
rest = rest.slice(1);
|
|
104
|
+
}
|
|
105
|
+
// At a user terminal, unregistered non-flag commands keep the legacy lark-cli passthrough.
|
|
106
|
+
// Inside an Agent Runtime, unknown Feishu command groups enter the same Larkin-owned
|
|
107
|
+
// identity and freshness AOP before delegation to the verified global official CLI.
|
|
108
|
+
const wantsHelp = command === "help" || command === "--help" || command === "-h"
|
|
109
|
+
|| (!runtimeAgentAuthority && command.startsWith("-"));
|
|
110
|
+
if (!routes[command] && !wantsHelp) {
|
|
111
|
+
routes[command] = runtimeAgentAuthority ? ["lark-cli", command] : ["lark", command];
|
|
112
|
+
}
|
|
113
|
+
if (!routes[command]) {
|
|
114
|
+
console.log(`larkin — Run persistent-personality agents on Feishu
|
|
115
|
+
|
|
116
|
+
Usage: larkin <command>
|
|
117
|
+
setup Create or connect a bot, configure its Agent, start it, and open the dashboard
|
|
118
|
+
start Start and supervise configured Agents plus the local dashboard
|
|
119
|
+
status Show Agent configuration, bot identity, credentials, and connection status
|
|
120
|
+
agents List all Agents, including runtime, model, bot identity, and credential status
|
|
121
|
+
model [<id>] Show or change an Agent model, with workspace validation
|
|
122
|
+
runtime [<id>] Show or change an Agent runtime; changing it resets the model to its default
|
|
123
|
+
effort [<level>] Show or set the reasoning effort; use clear to restore the default
|
|
124
|
+
chats List known chats; use free/strict <oc_id> to configure mention requirements
|
|
125
|
+
config Inspect effective config/source, edit mention inheritance, or explicitly apply runtime changes
|
|
126
|
+
session reset Replace one idle, zero-backlog Agent Runtime session for a fresh scenario
|
|
127
|
+
pi-auth Show non-sensitive built-in Pi auth status or logout one provider
|
|
128
|
+
comment reply Reply to the exact cloud-document comment bound by a polled Inbox message
|
|
129
|
+
telemetry Inspect, export, import, or flush the durable OpenTelemetry trace queue
|
|
130
|
+
<lark-cli 命令组> im/docs/wiki/drive 等 lark-cli 命令原样转发,机器人身份已锁定(如 larkin im +chat-list)
|
|
131
|
+
Getting started:
|
|
132
|
+
First-time setup: larkin setup
|
|
133
|
+
Daily startup: larkin start
|
|
134
|
+
Health check: larkin status
|
|
135
|
+
|
|
136
|
+
Process persistence is managed externally. larkin start stays in the foreground and supervises the daemon and dashboard.`);
|
|
137
|
+
process.exit(command === "help" || command === "--help" || command === "-h" ? 0 : 1);
|
|
138
|
+
}
|
|
139
|
+
const [mode, ...presetArguments] = routes[command];
|
|
140
|
+
const childSpec = internalCommandSpec(mode, [...presetArguments, ...rest]);
|
|
141
|
+
const child = spawn(childSpec.command, childSpec.args, { stdio: "inherit" });
|
|
142
|
+
// Package-manager bin shims add a wrapper process. Forward terminal signals so the actual command
|
|
143
|
+
// does not become an orphan that keeps the machine lock or Feishu connection.
|
|
144
|
+
const signalForwarders = new Map();
|
|
145
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
146
|
+
const forward = () => {
|
|
147
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
148
|
+
child.kill(signal);
|
|
149
|
+
};
|
|
150
|
+
signalForwarders.set(signal, forward);
|
|
151
|
+
process.on(signal, forward);
|
|
152
|
+
}
|
|
153
|
+
const stopForwardingSignals = () => {
|
|
154
|
+
for (const [signal, forward] of signalForwarders)
|
|
155
|
+
process.off(signal, forward);
|
|
156
|
+
};
|
|
157
|
+
child.on("error", (error) => {
|
|
158
|
+
stopForwardingSignals();
|
|
159
|
+
console.error(`larkin ${command} 启动失败: ${error.message}`);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
});
|
|
162
|
+
child.on("exit", (code, signal) => {
|
|
163
|
+
stopForwardingSignals();
|
|
164
|
+
if (code !== null)
|
|
165
|
+
process.exit(code);
|
|
166
|
+
if (!signal)
|
|
167
|
+
process.exit(1);
|
|
168
|
+
const fallback = signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : signal === "SIGKILL" ? 137 : 1;
|
|
169
|
+
try {
|
|
170
|
+
process.kill(process.pid, signal);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
process.exit(fallback);
|
|
174
|
+
}
|
|
175
|
+
setImmediate(() => process.exit(fallback));
|
|
176
|
+
});
|