larkin 0.4.29 → 0.5.0
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/CHANGELOG.md +11 -0
- package/README.md +5 -9
- package/artifacts/release/THIRD_PARTY_NOTICES.txt +751 -4425
- package/dist/app/agent-cli.mjs +14 -2
- package/dist/app/agent-config.mjs +26 -86
- package/dist/app/binary-entry.mjs +0 -20
- package/dist/app/cli.mjs +6 -22
- package/dist/app/internal-command.cjs +0 -2
- package/dist/app/internal-command.mjs +0 -2
- package/dist/app/runtime-model-directory.mjs +2 -16
- package/dist/app/runtime-process.mjs +0 -1
- package/dist/app/setup.mjs +52 -57
- package/dist/dashboard/dashboard-config-controller.mjs +43 -109
- package/dist/dashboard/dashboard-view-model.mjs +4 -7
- package/dist/dashboard/web/assets/dashboard.css +1 -1
- package/dist/dashboard/web/assets/dashboard.js +15 -15
- package/dist/feishu/host-shell.cjs +1 -2
- package/dist/feishu/host-shell.mjs +1 -2
- package/dist/platform/config.cjs +168 -133
- package/dist/platform/config.mjs +168 -133
- package/dist/runtime/pi-artifact-provenance.mjs +3 -3
- package/dist/runtime/pi-bash-timeout-extension.mjs +4 -5
- package/dist/runtime/pi-compaction-recovery.cjs +9 -11
- package/dist/runtime/pi-compaction-recovery.mjs +8 -10
- package/dist/runtime/pi-extension-api.mjs +1 -0
- package/dist/runtime/pi-model-catalog.cjs +7 -8
- package/dist/runtime/pi-model-catalog.mjs +7 -8
- package/dist/runtime/pi-subagent-injection.mjs +17 -4
- package/dist/runtime/runtime-adapters.mjs +26 -43
- package/dist/runtime/runtime-host.cjs +21 -43
- package/dist/runtime/runtime-host.mjs +22 -44
- package/dist/runtime/runtime-model-catalog.cjs +1 -1
- package/dist/runtime/runtime-model-catalog.mjs +1 -1
- package/dist/runtime/runtime-readiness.cjs +42 -99
- package/dist/runtime/runtime-readiness.mjs +40 -98
- package/dist/runtime/user-runtime.cjs +7 -24
- package/dist/runtime/user-runtime.mjs +7 -23
- package/dist/setup/bot-register.mjs +15 -123
- package/dist/setup/setup-agent-choice.mjs +48 -74
- package/dist/setup/setup-bind.mjs +51 -151
- package/dist/setup/setup-binding.mjs +6 -9
- package/package.json +3 -2
- package/dist/app/pi-auth-cli.mjs +0 -277
- package/dist/platform/process-boundary-trace.cjs +0 -108
- package/dist/runtime/builtin-pi-assets.cjs +0 -117
- package/dist/runtime/builtin-pi-assets.mjs +0 -105
- package/dist/runtime/pi-artifact-provenance.cjs +0 -147
- package/dist/runtime/pi-distribution-label.cjs +0 -6
- package/dist/runtime/pi-distribution-label.mjs +0 -3
- package/dist/runtime/pi-inline-extensions.mjs +0 -78
- package/dist/runtime/pi-official-auth.cjs +0 -594
- package/dist/runtime/pi-official-auth.mjs +0 -544
- package/dist/runtime/pi-profile-migration.cjs +0 -764
- package/dist/runtime/pi-profile-migration.mjs +0 -719
- package/dist/runtime/pi-provider-config.cjs +0 -354
- package/dist/runtime/pi-provider-config.mjs +0 -334
- package/dist/runtime/pi-provider-login.mjs +0 -240
package/dist/app/agent-cli.mjs
CHANGED
|
@@ -12,6 +12,8 @@ import { createReminderRoutes } from "../agent/reminder-routes.mjs";
|
|
|
12
12
|
import { InteractionStateMachine } from "../agent/interaction-state-machine.mjs";
|
|
13
13
|
import { issueCallbackProbe, readCallbackCapability } from "../platform/callback-capability.mjs";
|
|
14
14
|
import { requestAgentUpsert } from "./local-control.mjs";
|
|
15
|
+
import { isUserRuntime } from "../runtime/user-runtime.mjs";
|
|
16
|
+
import { resolveRuntimeExecutable, runtimeInstallNextAction } from "../runtime/runtime-readiness.mjs";
|
|
15
17
|
export { AGENT_CLI_CAPABILITIES } from "../agent/agent-cli-capabilities.mjs";
|
|
16
18
|
import { AGENT_CLI_CAPABILITIES } from "../agent/agent-cli-capabilities.mjs";
|
|
17
19
|
import { CONFIG_CLI_USAGE, CONFIG_CLI_VALUES } from "../agent/config-cli-contract.mjs";
|
|
@@ -157,7 +159,7 @@ function agentConfigRequest(argv, env, config, agent, dependencies) {
|
|
|
157
159
|
const target = config.agents[targetId];
|
|
158
160
|
if (!target)
|
|
159
161
|
throw new Error(`Agent 不存在:${targetId};运行 larkin config show --json 查看可用 Agent`);
|
|
160
|
-
const currentDirectory = (runtime = larkinConfig.toUserRuntime(target.runtime
|
|
162
|
+
const currentDirectory = (runtime = larkinConfig.toUserRuntime(target.runtime)) => (dependencies.modelDirectory ?? ((input) => discoverRuntimeModelDirectorySync(input, env)))({
|
|
161
163
|
agentId: target.agentId, cwd: target.workspaceDir, runtime,
|
|
162
164
|
});
|
|
163
165
|
if (operation === "show") {
|
|
@@ -173,6 +175,16 @@ function agentConfigRequest(argv, env, config, agent, dependencies) {
|
|
|
173
175
|
const runtime = options.positionals[0];
|
|
174
176
|
if (!runtime || options.positionals.length > 1)
|
|
175
177
|
throw new Error("用法: larkin config runtime <runtime> [--model <model>] [--agent <App ID>]");
|
|
178
|
+
if (!isUserRuntime(runtime))
|
|
179
|
+
throw new Error(`未知 runtime:${runtime}`);
|
|
180
|
+
const command = runtime === "pi"
|
|
181
|
+
? env.LARKIN_PI_COMMAND || "pi"
|
|
182
|
+
: runtime === "codex"
|
|
183
|
+
? env.LARKIN_CODEX_COMMAND || "codex"
|
|
184
|
+
: env.LARKIN_CLAUDE_COMMAND || "claude";
|
|
185
|
+
if (!resolveRuntimeExecutable(command, env)) {
|
|
186
|
+
throw new Error(`${runtime} is not installed;${runtimeInstallNextAction(runtime)}`);
|
|
187
|
+
}
|
|
176
188
|
const model = options.values.get("--model");
|
|
177
189
|
if (model && !currentDirectory(runtime).some((item) => item.id === model))
|
|
178
190
|
throw new Error(`model 不在 ${runtime} 当前目录中:${model}`);
|
|
@@ -594,7 +606,7 @@ export function runAgentCli(argv, env = process.env, dependencies = {}) {
|
|
|
594
606
|
openId: identity?.open_id ?? identity?.openId ?? null,
|
|
595
607
|
avatarUrl: identity?.avatar_url ?? identity?.avatarUrl ?? null,
|
|
596
608
|
runtime: agent.runtime,
|
|
597
|
-
runtimeOption: larkinConfig.toUserRuntime(agent.runtime
|
|
609
|
+
runtimeOption: larkinConfig.toUserRuntime(agent.runtime),
|
|
598
610
|
model: agent.model, reasoningEffort: agent.effort ?? null,
|
|
599
611
|
createdAt: agent.createdAt ?? "1970-01-01T00:00:00.000Z",
|
|
600
612
|
});
|
|
@@ -11,9 +11,8 @@ import { isChannelReconnecting, projectAgentReadiness } from "./agent-readiness.
|
|
|
11
11
|
import { requestAgentUpsert } from "./local-control.mjs";
|
|
12
12
|
import * as larkinConfig from "../platform/config.mjs";
|
|
13
13
|
import { managedOfficialLarkCli } from "./agent-lark-cli-workspace.mjs";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { traceProcessBoundary } from "../platform/process-boundary-trace.mjs";
|
|
14
|
+
import { RUNTIME_OPTIONS, fromUserRuntime, isUserRuntime, toUserRuntime } from "../runtime/user-runtime.mjs";
|
|
15
|
+
import { resolveRuntimeExecutable, runtimeInstallNextAction } from "../runtime/runtime-readiness.mjs";
|
|
17
16
|
function safeDocumentCommentCapabilities(configDir, profile) {
|
|
18
17
|
try {
|
|
19
18
|
return {
|
|
@@ -52,15 +51,15 @@ const die = (message) => {
|
|
|
52
51
|
console.error(" 下一步: larkin config --help");
|
|
53
52
|
process.exit(1);
|
|
54
53
|
};
|
|
55
|
-
if (!kind || !["agents", "model", "runtime", "effort", "
|
|
56
|
-
die("用法: larkin agents | larkin config <show|mention|apply> | larkin model [<id>] | larkin runtime [<id>] [--model <id>] | larkin
|
|
54
|
+
if (!kind || !["agents", "model", "runtime", "effort", "chats", "config"].includes(kind)) {
|
|
55
|
+
die("用法: larkin agents | larkin config <show|mention|apply> | larkin model [<id>] | larkin runtime [<id>] [--model <id>] | larkin effort [<level>|clear] | larkin chats [free|strict <oc_id>]");
|
|
57
56
|
}
|
|
58
57
|
const allowedValueFlags = {
|
|
59
58
|
agents: new Set(), model: new Set(["--agent"]), runtime: new Set(["--agent", "--model"]),
|
|
60
|
-
effort: new Set(["--agent"]),
|
|
59
|
+
effort: new Set(["--agent"]), chats: new Set(["--agent"]), config: new Set(["--agent", "--chat"]),
|
|
61
60
|
};
|
|
62
61
|
const allowedBooleanFlags = {
|
|
63
|
-
agents: new Set(["--json"]), model: new Set(), runtime: new Set(), effort: new Set(),
|
|
62
|
+
agents: new Set(["--json"]), model: new Set(), runtime: new Set(), effort: new Set(), chats: new Set(), config: new Set(["--json"]),
|
|
64
63
|
};
|
|
65
64
|
const parsedValues = new Map();
|
|
66
65
|
const parsedBooleans = new Set();
|
|
@@ -94,7 +93,6 @@ const flagAgent = parsedValues.get("--agent");
|
|
|
94
93
|
const flagModel = parsedValues.get("--model");
|
|
95
94
|
const flagChat = parsedValues.get("--chat");
|
|
96
95
|
const flagJson = parsedBooleans.has("--json");
|
|
97
|
-
const importExternalProfile = parsedBooleans.has("--import-external-profile");
|
|
98
96
|
const value = positionals[0];
|
|
99
97
|
function assertOnlyFlags(valueFlags, booleanFlags = []) {
|
|
100
98
|
const allowedValues = new Set(valueFlags);
|
|
@@ -126,11 +124,6 @@ else if (kind === "effort") {
|
|
|
126
124
|
if (positionals.length > 1)
|
|
127
125
|
die("用法: larkin effort [<level>|clear|default] [--agent <App ID>]");
|
|
128
126
|
}
|
|
129
|
-
else if (kind === "pi-distribution") {
|
|
130
|
-
assertOnlyFlags(["--agent", "--snapshot"], ["--import-external-profile"]);
|
|
131
|
-
if (positionals.length > 1)
|
|
132
|
-
die("用法: larkin pi-distribution [show|builtin|external] [--agent <App ID>] [--snapshot <private-file>]");
|
|
133
|
-
}
|
|
134
127
|
else if (kind === "chats") {
|
|
135
128
|
assertOnlyFlags(["--agent"]);
|
|
136
129
|
if (![0, 2].includes(positionals.length))
|
|
@@ -178,21 +171,6 @@ const loaded = (() => {
|
|
|
178
171
|
const { configDir, file, config } = loaded;
|
|
179
172
|
if (!fs.existsSync(file))
|
|
180
173
|
die("未找到 Larkin 配置,请运行 larkin setup");
|
|
181
|
-
const snapshotFile = parsedValues.get("--snapshot");
|
|
182
|
-
if (kind === "pi-distribution" && value === "rollback") {
|
|
183
|
-
if (importExternalProfile)
|
|
184
|
-
die("--import-external-profile 只允许与 builtin 一起使用");
|
|
185
|
-
if (!snapshotFile || flagAgent || positionals.length !== 1)
|
|
186
|
-
die("用法: larkin pi-distribution rollback --snapshot <private-file>");
|
|
187
|
-
try {
|
|
188
|
-
const result = larkinConfig.rollbackConfig(process.env, snapshotFile);
|
|
189
|
-
say(JSON.stringify({ ok: true, rolledBackAgent: result.agentId, revision: result.revision }));
|
|
190
|
-
}
|
|
191
|
-
catch (error) {
|
|
192
|
-
die(`回滚失败:${error instanceof Error ? error.message : String(error)}`);
|
|
193
|
-
}
|
|
194
|
-
process.exit(0);
|
|
195
|
-
}
|
|
196
174
|
if (kind === "agents") {
|
|
197
175
|
const list = Object.values(config.agents || {});
|
|
198
176
|
const daemon = readProcessState(config.larkinHome).daemon;
|
|
@@ -224,7 +202,7 @@ if (kind === "agents") {
|
|
|
224
202
|
agent_id: agent.agentId,
|
|
225
203
|
name: agent.name,
|
|
226
204
|
runtime: agent.runtime,
|
|
227
|
-
runtimeOption: toUserRuntime(agent.runtime
|
|
205
|
+
runtimeOption: toUserRuntime(agent.runtime),
|
|
228
206
|
model: effectiveModel,
|
|
229
207
|
document_comment: {
|
|
230
208
|
event: "drive.notice.comment_add_v1",
|
|
@@ -295,7 +273,7 @@ if (kind === "agents") {
|
|
|
295
273
|
say(` ${agent.name}${agent.name === config.activeAgent ? " [active]" : ""}`);
|
|
296
274
|
const effectiveModel = status?.session?.runtime === agent.runtime && status.session.model ? status.session.model : agent.model;
|
|
297
275
|
const effectiveEffort = status?.session?.runtime === agent.runtime && status.session.reasoningEffort ? status.session.reasoningEffort : agent.effort;
|
|
298
|
-
say(` runtime=${toUserRuntime(agent.runtime
|
|
276
|
+
say(` runtime=${toUserRuntime(agent.runtime)} model=${effectiveModel}${effectiveModel !== agent.model ? ` stored=${agent.model}` : ""}${effectiveEffort ? ` effort=${effectiveEffort}` : ""}`);
|
|
299
277
|
if (status?.runtimeReadiness) {
|
|
300
278
|
const current = projectedReadiness.readiness.runtime_ready;
|
|
301
279
|
const state = status.runtimeReadiness.state === "ready" && !current ? "unavailable" : status.runtimeReadiness.state || "incompatible";
|
|
@@ -429,7 +407,7 @@ if (!key) {
|
|
|
429
407
|
say(`共 ${keys.length} 个 agent(用 --agent <appId> 查看/修改指定一个):`);
|
|
430
408
|
for (const candidate of keys) {
|
|
431
409
|
const item = config.agents[candidate];
|
|
432
|
-
say(` ${candidate}${candidate === config.activeAgent ? " [active]" : ""} runtime=${toUserRuntime(item.runtime
|
|
410
|
+
say(` ${candidate}${candidate === config.activeAgent ? " [active]" : ""} runtime=${toUserRuntime(item.runtime)} model=${item.model}`);
|
|
433
411
|
}
|
|
434
412
|
process.exit(0);
|
|
435
413
|
}
|
|
@@ -437,49 +415,21 @@ if (!key) {
|
|
|
437
415
|
}
|
|
438
416
|
const selectedKey = key;
|
|
439
417
|
const agent = config.agents[selectedKey];
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}));
|
|
452
|
-
process.exit(0);
|
|
453
|
-
}
|
|
454
|
-
if (requested !== "builtin" && requested !== "external")
|
|
455
|
-
die("Pi distribution 只允许 show/builtin/external");
|
|
456
|
-
if (agent.runtime !== "pi")
|
|
457
|
-
die(`Agent ${selectedKey} 不是 Pi runtime`);
|
|
458
|
-
if (!snapshotFile)
|
|
459
|
-
die("修改 Pi distribution 必须提供 --snapshot <private-file>");
|
|
460
|
-
if (requested === "builtin" && !importExternalProfile) {
|
|
461
|
-
try {
|
|
462
|
-
assertBuiltinPiAgentDirectory(piAgentDirectory(configDir, selectedKey));
|
|
463
|
-
}
|
|
464
|
-
catch {
|
|
465
|
-
die(agent.piDistribution === "builtin"
|
|
466
|
-
? "内置 Pi provider 尚未为该 Agent 配置。请使用 Dashboard Provider Credentials 或 larkin pi-auth login。"
|
|
467
|
-
: "内置 Pi provider 尚未为该 Agent 配置。");
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
try {
|
|
471
|
-
const result = larkinConfig.mutateConfig(process.env, { kind: "set-agent-pi-distribution", agentId: selectedKey, distribution: requested }, { kind: "user" }, {
|
|
472
|
-
snapshotFile: snapshotFile, ...(importExternalProfile ? { importExternalProfile: true } : {}),
|
|
473
|
-
});
|
|
474
|
-
traceProcessBoundary(process.env, "agent-config:pi-distribution-persisted", { configDir, agentId: selectedKey, targetDir: piAgentDirectory(configDir, selectedKey), requested, applyState: result.applyState });
|
|
475
|
-
say(JSON.stringify({ ok: true, agentId: selectedKey, piDistribution: requested, revision: result.revision, applyState: result.applyState }));
|
|
418
|
+
function runtimeExecutableName(runtime) {
|
|
419
|
+
if (runtime === "pi")
|
|
420
|
+
return process.env.LARKIN_PI_COMMAND || "pi";
|
|
421
|
+
if (runtime === "codex")
|
|
422
|
+
return process.env.LARKIN_CODEX_COMMAND || "codex";
|
|
423
|
+
return process.env.LARKIN_CLAUDE_COMMAND || "claude";
|
|
424
|
+
}
|
|
425
|
+
if (kind === "runtime" && value) {
|
|
426
|
+
if (!isUserRuntime(value)) {
|
|
427
|
+
console.error(`✗ 未知 runtime:${value}。合法值: ${RUNTIME_OPTIONS.join(", ")}`);
|
|
428
|
+
process.exit(1);
|
|
476
429
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
die("Pi external profile import failed; no secret or private path was disclosed");
|
|
480
|
-
die(`Pi distribution 修改失败:${error instanceof Error ? error.message : String(error)}`);
|
|
430
|
+
if (!resolveRuntimeExecutable(runtimeExecutableName(value), process.env)) {
|
|
431
|
+
die(`${value} is not installed;${runtimeInstallNextAction(value)}`);
|
|
481
432
|
}
|
|
482
|
-
process.exit(0);
|
|
483
433
|
}
|
|
484
434
|
const catalog = larkinConfig.loadRuntimeModels();
|
|
485
435
|
if ((["model", "effort"].includes(kind || "") && agent.runtime === "codex") || (kind === "runtime" && value === "codex")) {
|
|
@@ -501,28 +451,22 @@ if ((["model", "effort"].includes(kind || "") && agent.runtime === "claude") ||
|
|
|
501
451
|
}
|
|
502
452
|
}
|
|
503
453
|
let piCatalog = null;
|
|
504
|
-
const userRuntime = toUserRuntime(agent.runtime
|
|
505
|
-
const catalogUserRuntime = kind === "runtime" && (value === "pi" || value === "builtin-pi")
|
|
506
|
-
? value
|
|
507
|
-
: userRuntime;
|
|
454
|
+
const userRuntime = toUserRuntime(agent.runtime);
|
|
508
455
|
if ((kind === "model" || kind === "effort" || kind === "runtime") && !value) {
|
|
509
456
|
say(`agent=${selectedKey} runtime=${userRuntime} model=${agent.model}`);
|
|
510
457
|
}
|
|
511
458
|
const needsLivePiCatalog = (["model", "effort"].includes(kind) && agent.runtime === "pi")
|
|
512
|
-
|| (kind === "runtime" &&
|
|
459
|
+
|| (kind === "runtime" && value === "pi" && Boolean(flagModel));
|
|
513
460
|
if (needsLivePiCatalog) {
|
|
514
461
|
try {
|
|
515
|
-
const catalogCommand = piCatalogCommandSpec(piCatalogDistributionForUserRuntime(catalogUserRuntime), process.env);
|
|
516
462
|
piCatalog = await discoverPiModelCatalog({
|
|
517
463
|
cwd: String(agent.workspaceDir),
|
|
518
|
-
|
|
519
|
-
command: catalogCommand.command,
|
|
520
|
-
commandArgs: catalogCommand.commandArgs,
|
|
464
|
+
command: process.env.LARKIN_PI_COMMAND || "pi",
|
|
521
465
|
env: process.env,
|
|
522
466
|
});
|
|
523
467
|
}
|
|
524
468
|
catch (error) {
|
|
525
|
-
die(`Pi 模型目录加载失败:${error instanceof Error ? error.message : String(error)}
|
|
469
|
+
die(`Pi 模型目录加载失败:${error instanceof Error ? error.message : String(error)}。请先运行官方 pi 登录流程。`);
|
|
526
470
|
}
|
|
527
471
|
if (!piCatalog)
|
|
528
472
|
die("Pi 模型目录加载失败");
|
|
@@ -695,10 +639,6 @@ else {
|
|
|
695
639
|
say("\n切换: larkin runtime <id> [--model <id>]");
|
|
696
640
|
process.exit(0);
|
|
697
641
|
}
|
|
698
|
-
if (!isUserRuntime(value)) {
|
|
699
|
-
console.error(`✗ "${value}" 不是合法 runtime。合法值: ${RUNTIME_OPTIONS.join(", ")}`);
|
|
700
|
-
process.exit(1);
|
|
701
|
-
}
|
|
702
642
|
let model = flagModel;
|
|
703
643
|
if (model) {
|
|
704
644
|
const items = catalog[catalogRuntimeKey(value)];
|
|
@@ -49,26 +49,6 @@ async function dispatchInternal(mode, rest) {
|
|
|
49
49
|
case "lark-channel-secret":
|
|
50
50
|
await (await import("./lark-channel-secret.mjs")).main(process.env);
|
|
51
51
|
return;
|
|
52
|
-
case "pi-rpc": {
|
|
53
|
-
const { prepareBuiltinPiPackageAssets } = await import("../runtime/builtin-pi-assets.mjs");
|
|
54
|
-
prepareBuiltinPiPackageAssets();
|
|
55
|
-
process.title = "pi-rpc";
|
|
56
|
-
process.env.PI_CODING_AGENT = "true";
|
|
57
|
-
process.emitWarning = (() => { });
|
|
58
|
-
// The Pi package's normal Bun CLI registers OAuth loaders before starting.
|
|
59
|
-
// Larkin invokes its RPC entrypoint directly, so do the same here; otherwise
|
|
60
|
-
// an OAuth-backed compact falls back to a dynamic module import unavailable in
|
|
61
|
-
// a compiled standalone binary.
|
|
62
|
-
const { registerBunOAuthFlows } = await import("@earendil-works/pi-ai/bun-oauth");
|
|
63
|
-
registerBunOAuthFlows();
|
|
64
|
-
const { main: piMain } = await import("@earendil-works/pi-coding-agent");
|
|
65
|
-
const { invokeBuiltinPiRpc } = await import("../runtime/pi-inline-extensions.mjs");
|
|
66
|
-
await invokeBuiltinPiRpc(piMain, rest);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
case "pi-auth":
|
|
70
|
-
await (await import("./pi-auth-cli.mjs")).main(rest, process.env);
|
|
71
|
-
return;
|
|
72
52
|
case "telemetry":
|
|
73
53
|
await (await import("./telemetry.mjs")).main(rest, process.env);
|
|
74
54
|
return;
|
package/dist/app/cli.mjs
CHANGED
|
@@ -29,11 +29,9 @@ const routes = {
|
|
|
29
29
|
model: ["agent-config", "model"],
|
|
30
30
|
runtime: ["agent-config", "runtime"],
|
|
31
31
|
effort: ["agent-config", "effort"],
|
|
32
|
-
"pi-distribution": ["agent-config", "pi-distribution"],
|
|
33
32
|
chats: ["agent-config", "chats"],
|
|
34
33
|
config: ["agent-config", "config"],
|
|
35
34
|
session: ["session-cli"],
|
|
36
|
-
"pi-auth": ["pi-auth"],
|
|
37
35
|
telemetry: ["telemetry"],
|
|
38
36
|
};
|
|
39
37
|
const runtimeAgentAuthority = typeof process.env.LARKIN_AGENT_ID === "string"
|
|
@@ -49,8 +47,8 @@ if (runtimeAgentAuthority && command === "comment")
|
|
|
49
47
|
const commandHelp = {
|
|
50
48
|
start: `Usage: larkin start [--agent <App ID> | --agents <App ID,...>]
|
|
51
49
|
Start one foreground supervisor for the daemon and local dashboard, or reuse it.`,
|
|
52
|
-
setup: `Usage: larkin setup [--tenant feishu|lark] [--runtime <
|
|
53
|
-
Run setup to create or connect a bot, configure its Agent, and attach it. Choose Feishu or Lark before the authorization QR (--tenant; default feishu). Interactive terminals
|
|
50
|
+
setup: `Usage: larkin setup [--tenant feishu|lark] [--runtime <pi|codex|claude>]
|
|
51
|
+
Run setup to create or connect a bot, configure its Agent, and attach it. Choose Feishu or Lark before the authorization QR (--tenant; default feishu). Interactive terminals list pi, Codex, and Claude Code with installed / not installed status. Non-TTY runs require --runtime and fail if that executable is missing.`,
|
|
54
52
|
status: `Usage: larkin status [--json]
|
|
55
53
|
Show Agent configuration, bot identity, credentials, and connection status. Use --json for readiness automation.`,
|
|
56
54
|
agents: `Usage: larkin agents [--json]
|
|
@@ -58,10 +56,7 @@ List every configured Agent and its current local status. Use --json for daemon/
|
|
|
58
56
|
model: `Usage: larkin model [<model>] [--agent <App ID>]
|
|
59
57
|
Show or change an Agent model.`,
|
|
60
58
|
runtime: `Usage: larkin runtime [<runtime>] [--agent <App ID>] [--model <model>]
|
|
61
|
-
Show or change an Agent runtime. User-facing ids: pi |
|
|
62
|
-
"pi-distribution": `Usage: larkin pi-distribution [show|builtin|external] [--agent <App ID>] [--snapshot <private-file>] [--import-external-profile]
|
|
63
|
-
larkin pi-distribution rollback --snapshot <private-file>
|
|
64
|
-
Show or change one Pi Agent distribution. builtin requires configured provider state, or explicitly imports the external Pi 0.84.2 profile with --import-external-profile; all changes support config-lock CAS rollback.`,
|
|
59
|
+
Show or change an Agent runtime. User-facing ids: pi | codex | claude. A missing executable is rejected and does not write config.`,
|
|
65
60
|
effort: `Usage: larkin effort [<level>|clear|default] [--agent <App ID>]
|
|
66
61
|
Show or change an Agent reasoning effort; clear/default restores the Runtime default.`,
|
|
67
62
|
chats: `Usage: larkin chats [--agent <App ID>]
|
|
@@ -83,11 +78,6 @@ Credentials, internal paths, serverId, activeAgent, and raw config are never exp
|
|
|
83
78
|
session: `Usage: larkin session reset --agent <App ID> --json [--wait-ready <seconds>]
|
|
84
79
|
larkin session recover --agent <App ID> --reason context-overflow --json [--wait-ready <seconds>]
|
|
85
80
|
Reset replaces one idle, zero-backlog Runtime session. Recover is an explicit operator-only context-window recovery that preserves and replays canonical Inbox deliveries.`,
|
|
86
|
-
"pi-auth": `Usage: larkin pi-auth status [--agent <App ID>] [--json]
|
|
87
|
-
larkin pi-auth providers [--json]
|
|
88
|
-
larkin pi-auth login <preset|custom> --agent <App ID> [--model <model>] [--base-url <url>] [--api-key-stdin] [--json]
|
|
89
|
-
larkin pi-auth logout <provider> [--agent <App ID>]
|
|
90
|
-
Show a redacted builtin Pi provider catalog, configure one existing Agent with a known preset or custom OpenAI-compatible endpoint, or remove one target provider credential. API keys are accepted only from a TTY prompt or --api-key-stdin.`,
|
|
91
81
|
comment: `Usage: larkin comment reply --message-id <doc_comment_message_id> --text '<reply>' --json
|
|
92
82
|
Reply as the Runtime-bound Bot to the exact cloud-document comment locator supplied by canonical Inbox. Retrying the same body is idempotent; a different body appends a follow-up reply to the same comment.`,
|
|
93
83
|
telemetry: `Usage: larkin telemetry <status|export|import|flush>
|
|
@@ -112,9 +102,10 @@ if (!runtimeAgentCommand && command === "config" && ["runtime", "model", "effort
|
|
|
112
102
|
// At a user terminal, unregistered non-flag commands keep the legacy lark-cli passthrough.
|
|
113
103
|
// Inside an Agent Runtime, unknown Feishu command groups enter the same Larkin-owned
|
|
114
104
|
// identity and freshness AOP before delegation to the verified global official CLI.
|
|
105
|
+
const retiredPublicCommands = new Set(["pi-auth", "pi-distribution"]);
|
|
115
106
|
const wantsHelp = command === "help" || command === "--help" || command === "-h"
|
|
116
107
|
|| (!runtimeAgentAuthority && command.startsWith("-"));
|
|
117
|
-
if (!routes[command] && !wantsHelp) {
|
|
108
|
+
if (!routes[command] && !wantsHelp && !retiredPublicCommands.has(command)) {
|
|
118
109
|
routes[command] = runtimeAgentAuthority ? ["lark-cli", command] : ["lark", command];
|
|
119
110
|
}
|
|
120
111
|
if (!routes[command]) {
|
|
@@ -132,7 +123,6 @@ Usage: larkin <command>
|
|
|
132
123
|
config Inspect effective config/source, edit mention inheritance, or explicitly apply runtime changes
|
|
133
124
|
session reset Replace one idle, zero-backlog Agent Runtime session for a fresh scenario
|
|
134
125
|
session recover Explicitly recover a context-overflowed session and replay retained Inbox deliveries
|
|
135
|
-
pi-auth Show, configure, or logout one built-in Pi Agent provider credential
|
|
136
126
|
comment reply Reply to the exact cloud-document comment bound by a polled Inbox message
|
|
137
127
|
telemetry Inspect, export, import, or flush the durable OpenTelemetry trace queue
|
|
138
128
|
<lark-cli 命令组> im/docs/wiki/drive 等 lark-cli 命令原样转发,机器人身份已锁定(如 larkin im +chat-list)
|
|
@@ -146,15 +136,9 @@ Process persistence is managed externally. larkin start stays in the foreground
|
|
|
146
136
|
}
|
|
147
137
|
const [mode, ...presetArguments] = routes[command];
|
|
148
138
|
const childSpec = internalCommandSpec(mode, [...presetArguments, ...rest]);
|
|
149
|
-
// 管道 stdin 必须显式转发:Linux 上 inherit 会在父进程排空后再交给子进程,导致 --api-key-stdin 读到空。
|
|
150
|
-
const pipeStdin = command === "pi-auth" && rest.includes("--api-key-stdin") && !process.stdin.isTTY;
|
|
151
|
-
if (pipeStdin && typeof process.stdin.pause === "function")
|
|
152
|
-
process.stdin.pause();
|
|
153
139
|
const child = spawn(childSpec.command, childSpec.args, {
|
|
154
|
-
stdio: [
|
|
140
|
+
stdio: ["inherit", "inherit", "inherit"],
|
|
155
141
|
});
|
|
156
|
-
if (pipeStdin && child.stdin)
|
|
157
|
-
process.stdin.pipe(child.stdin);
|
|
158
142
|
// Package-manager bin shims add a wrapper process. Forward terminal signals so the actual command
|
|
159
143
|
// does not become an orphan that keeps the machine lock or Feishu connection.
|
|
160
144
|
const signalForwarders = new Map();
|
|
@@ -2,12 +2,9 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { loadConfig, resolveConfigDir } from "../platform/config.mjs";
|
|
6
5
|
import { discoverClaudeModelCatalog } from "../runtime/claude-model-catalog.mjs";
|
|
7
6
|
import { discoverPiModelCatalog } from "../runtime/pi-model-catalog.mjs";
|
|
8
7
|
import { discoverCodexModelCatalog } from "../runtime/codex-model-catalog.mjs";
|
|
9
|
-
import { ownedPiCatalogAgentDir, piCatalogCommandSpec } from "../runtime/pi-provider-config.mjs";
|
|
10
|
-
import { piCatalogDistributionForUserRuntime } from "../runtime/user-runtime.mjs";
|
|
11
8
|
function readTestModelDirectory(env) {
|
|
12
9
|
const fixture = env?.LARKIN_TEST_RUNTIME_MODEL_DIRECTORY_FILE;
|
|
13
10
|
if (!fixture)
|
|
@@ -37,22 +34,11 @@ export async function discoverRuntimeModelDirectory(input) {
|
|
|
37
34
|
...catalog.models.map(({ id, label, supportedReasoningEfforts }) => ({ id, label, supportedReasoningEfforts })),
|
|
38
35
|
];
|
|
39
36
|
}
|
|
40
|
-
if (input.runtime === "pi"
|
|
37
|
+
if (input.runtime === "pi") {
|
|
41
38
|
const env = input.env ?? process.env;
|
|
42
|
-
const agentId = input.agentId || "";
|
|
43
|
-
if (!agentId)
|
|
44
|
-
throw new Error("runtime model directory requires agentId for Pi");
|
|
45
|
-
const configDir = resolveConfigDir(env);
|
|
46
|
-
const { config } = loadConfig(env);
|
|
47
|
-
const agent = config.agents[agentId];
|
|
48
|
-
if (!agent)
|
|
49
|
-
throw new Error(`unknown agent: ${agentId}`);
|
|
50
|
-
const catalogCommand = piCatalogCommandSpec(piCatalogDistributionForUserRuntime(input.runtime), env);
|
|
51
39
|
const catalog = await discoverPiModelCatalog({
|
|
52
40
|
cwd: input.cwd,
|
|
53
|
-
|
|
54
|
-
command: catalogCommand.command,
|
|
55
|
-
commandArgs: catalogCommand.commandArgs,
|
|
41
|
+
command: env.LARKIN_PI_COMMAND ?? process.env.LARKIN_PI_COMMAND ?? "pi",
|
|
56
42
|
env,
|
|
57
43
|
});
|
|
58
44
|
if (!catalog.effectiveModel)
|
|
@@ -65,7 +65,6 @@ export async function markConfigAppliedAfterRuntimeReady(env, runningAgents, run
|
|
|
65
65
|
for (const running of runningAgents) {
|
|
66
66
|
const current = loaded.config.agents[running.agentId];
|
|
67
67
|
if (!current || current.runtime !== running.runtime || current.model !== running.model
|
|
68
|
-
|| (current.piDistribution || "external") !== (running.piDistribution || "external")
|
|
69
68
|
|| (current.effort || null) !== (running.effort || null))
|
|
70
69
|
continue;
|
|
71
70
|
markConfigApplied(env, running.agentId, runtimeConfigSignature(loaded.config, running.agentId));
|
package/dist/app/setup.mjs
CHANGED
|
@@ -7,13 +7,11 @@ import path from "node:path";
|
|
|
7
7
|
import readline from "node:readline/promises";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import * as larkinConfig from "../platform/config.mjs";
|
|
10
|
-
import {
|
|
11
|
-
import { fromUserRuntime, isUserRuntime } from "../runtime/user-runtime.mjs";
|
|
12
|
-
const PI_PROVIDER_IDS = PI_PROVIDER_PRESETS.map((p) => p.id).join(" | ") + " | custom";
|
|
10
|
+
import { isUserRuntime } from "../runtime/user-runtime.mjs";
|
|
13
11
|
import { acquireProcessLock, readProcessState } from "../platform/process-state.mjs";
|
|
14
12
|
import { requestAgentUpsert } from "./local-control.mjs";
|
|
15
13
|
import { openOwnedDashboardWhenReady } from "./setup-dashboard.mjs";
|
|
16
|
-
import { probeNativeRuntimeReadiness } from "../runtime/runtime-readiness.mjs";
|
|
14
|
+
import { probeNativeRuntimeReadiness, resolveRuntimeExecutable, runtimeInstallNextAction } from "../runtime/runtime-readiness.mjs";
|
|
17
15
|
import { internalCommandSpec, processCommandToken } from "./internal-command.mjs";
|
|
18
16
|
import { ensureOfficialLarkCliForSetup, formatOfficialLarkCliConsent, } from "./official-lark-cli.mjs";
|
|
19
17
|
const CFG_DIR = process.env.LARKIN_CONFIG_DIR || path.join(os.homedir(), ".larkin");
|
|
@@ -33,60 +31,54 @@ if (has("--start") && has("--no-start"))
|
|
|
33
31
|
if (has("--comment-subscription")) {
|
|
34
32
|
die("--comment-subscription 已移除:评论订阅默认 application(平台验证的应用级订阅),不需要该 flag");
|
|
35
33
|
}
|
|
34
|
+
const REMOVED_SETUP_FLAGS = ["--provider", "--api-key", "--base-url", "--pi-distribution", "--api-key-stdin", "--model-from-preset"];
|
|
35
|
+
if (REMOVED_SETUP_FLAGS.some((name) => has(name) || argv.some((arg) => arg.startsWith(`${name}=`)))) {
|
|
36
|
+
die("setup 不再接受 --provider/--api-key/--base-url;请安装并登录外部 pi、codex 或 claude");
|
|
37
|
+
}
|
|
36
38
|
// 默认应用级评论订阅:平台验证的应用订阅让该应用可见文档的全部评论进入 Agent Inbox。
|
|
37
39
|
const commentSubscription = "application";
|
|
38
|
-
const OPT = { runtime: flag("--runtime") || null, commentSubscription, start:
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
const OPT = { runtime: flag("--runtime") || null, commentSubscription, start: !has("--no-start"), help: has("--help") || has("-h"), nonInteractive: !Boolean(process.stdin.isTTY) };
|
|
41
|
+
const RUNTIME_ENUM = ["pi", "codex", "claude"];
|
|
42
|
+
function runtimeCommand(runtime, env = process.env) {
|
|
43
|
+
if (runtime === "pi")
|
|
44
|
+
return env.LARKIN_PI_COMMAND || "pi";
|
|
45
|
+
if (runtime === "codex")
|
|
46
|
+
return env.LARKIN_CODEX_COMMAND || "codex";
|
|
47
|
+
return env.LARKIN_CLAUDE_COMMAND || "claude";
|
|
48
|
+
}
|
|
41
49
|
function normalizeRuntime(value) {
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
if (!value)
|
|
51
|
+
return null;
|
|
52
|
+
if (value === "external-pi") {
|
|
44
53
|
console.error("external-pi 已改名为 pi");
|
|
45
|
-
return
|
|
54
|
+
return "pi";
|
|
46
55
|
}
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
return fromUserRuntime(raw);
|
|
51
|
-
}
|
|
52
|
-
const normalizedRuntime = normalizeRuntime(OPT.runtime);
|
|
53
|
-
// 参数化路径:显式 --runtime、无 TTY(Agent 驱动)、或任何 provider 参数。
|
|
54
|
-
// 交互终端且未指定任何参数时,不传 --runtime 给 bot-register,由其交互选择流程接管。
|
|
55
|
-
const parameterized = Boolean(OPT.runtime) || OPT.nonInteractive
|
|
56
|
-
|| ["--provider", "--api-key", "--base-url"].some((name) => has(name));
|
|
57
|
-
// builtin-pi 前置校验:授权前就 fast-fail(避免用户白授权后才发现缺 key);--help 永远跳过
|
|
58
|
-
if (!OPT.help && parameterized && normalizedRuntime.distribution === "builtin") {
|
|
59
|
-
const p = flag("--provider");
|
|
60
|
-
const b = flag("--base-url");
|
|
61
|
-
const k = flag("--api-key");
|
|
62
|
-
if (!p && !b)
|
|
63
|
-
die(`builtin-pi 需要 --provider <id>(${PI_PROVIDER_IDS})或 --base-url;或 --runtime pi 用已有 pi`);
|
|
64
|
-
if (!k)
|
|
65
|
-
die("builtin-pi 需要 --api-key;或 --runtime pi 用已有登录");
|
|
56
|
+
if (isUserRuntime(value))
|
|
57
|
+
return value;
|
|
58
|
+
return die(`未知 runtime \`${value}\`;可选:${RUNTIME_ENUM.join(" | ")}`);
|
|
66
59
|
}
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
function assertRuntimeInstalled(runtime) {
|
|
61
|
+
if (resolveRuntimeExecutable(runtimeCommand(runtime), process.env))
|
|
62
|
+
return;
|
|
63
|
+
die(`${runtime} is not installed;${runtimeInstallNextAction(runtime)}`);
|
|
69
64
|
}
|
|
70
65
|
if (OPT.help) {
|
|
71
66
|
say(`larkin setup — Create or connect a Feishu (Lark) bot, then configure and attach its Agent
|
|
72
67
|
|
|
73
68
|
Usage:
|
|
74
|
-
larkin setup Select a bot in the browser and
|
|
75
|
-
larkin setup --
|
|
69
|
+
larkin setup Select a bot in the browser and choose an installed runtime
|
|
70
|
+
larkin setup --runtime pi Non-interactive setup for an externally installed runtime
|
|
76
71
|
|
|
77
72
|
Options:
|
|
78
|
-
--runtime <runtime> Agent runtime
|
|
79
|
-
--
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
--
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
--tenant feishu|lark 授权二维码之前选择品牌。默认 feishu(扫码 /page/launcher)。
|
|
88
|
-
Lark 用 --tenant lark:扫码 /page/cli,完成后凭证回传,
|
|
89
|
-
不要打开 /page/launcher。交互式未指定时会先询问。
|
|
73
|
+
--runtime <runtime> Required without a TTY. Agent runtime: pi | codex | claude
|
|
74
|
+
--model <id> Optional model ID; otherwise the runtime default is used.
|
|
75
|
+
Use \`larkin model\` after setup to inspect or switch.
|
|
76
|
+
--from-cli-profile <name> Optional: reuse an existing official lark-cli profile
|
|
77
|
+
(requires LARKIN_SETUP_APP_SECRET)
|
|
78
|
+
--tenant feishu|lark Choose the brand before the authorization QR. Default feishu
|
|
79
|
+
(scan /page/launcher). Lark uses --tenant lark: scan /page/cli,
|
|
80
|
+
then credentials return; do not open /page/launcher.
|
|
81
|
+
Interactive runs ask when omitted.
|
|
90
82
|
|
|
91
83
|
setup handles browser authorization, permission grants, credential storage, Agent configuration,
|
|
92
84
|
and target-only hot attach. Each Agent is identified by its bot App ID: selecting the same bot reuses
|
|
@@ -95,11 +87,18 @@ its existing Agent, memory, and state; creating a new bot creates a new Agent.
|
|
|
95
87
|
If larkin is running, only the selected Agent is added or updated. Otherwise setup starts the same
|
|
96
88
|
daemon + dashboard supervisor used by larkin start.
|
|
97
89
|
|
|
98
|
-
Interactive terminals
|
|
99
|
-
Non-interactive (Agent-driven) runs
|
|
100
|
-
|
|
90
|
+
Interactive terminals list pi, Codex, and Claude Code with installed / not installed status and
|
|
91
|
+
refuse a runtime that is not installed. Non-interactive (Agent-driven) runs require --runtime
|
|
92
|
+
and exit non-zero with the readiness message when that executable is missing.`);
|
|
101
93
|
process.exit(0);
|
|
102
94
|
}
|
|
95
|
+
if (OPT.nonInteractive && !OPT.runtime) {
|
|
96
|
+
die("非交互 setup 必须指定 --runtime pi|codex|claude");
|
|
97
|
+
}
|
|
98
|
+
const selectedRuntime = normalizeRuntime(OPT.runtime);
|
|
99
|
+
if (selectedRuntime)
|
|
100
|
+
assertRuntimeInstalled(selectedRuntime);
|
|
101
|
+
const parameterized = Boolean(OPT.runtime);
|
|
103
102
|
function runForeground(mode, args) {
|
|
104
103
|
return new Promise((resolve, reject) => {
|
|
105
104
|
const childSpec = internalCommandSpec(mode, args);
|
|
@@ -151,11 +150,8 @@ export async function main() {
|
|
|
151
150
|
fs.mkdirSync(CFG_DIR, { recursive: true, mode: 0o700 });
|
|
152
151
|
const resultFile = path.join(CFG_DIR, `.setup-result-${process.pid}.json`);
|
|
153
152
|
const registerArgs = ["--auto", "--result-file", resultFile];
|
|
154
|
-
if (parameterized)
|
|
155
|
-
registerArgs.push("--runtime",
|
|
156
|
-
if (normalizedRuntime.distribution)
|
|
157
|
-
registerArgs.push("--pi-distribution", normalizedRuntime.distribution);
|
|
158
|
-
}
|
|
153
|
+
if (parameterized && selectedRuntime)
|
|
154
|
+
registerArgs.push("--runtime", selectedRuntime);
|
|
159
155
|
registerArgs.push("--comment-subscription", OPT.commentSubscription);
|
|
160
156
|
if (has("--tenant")) {
|
|
161
157
|
const tenant = flag("--tenant");
|
|
@@ -163,7 +159,7 @@ export async function main() {
|
|
|
163
159
|
die("--tenant 只支持 feishu 或 lark");
|
|
164
160
|
registerArgs.push("--tenant", tenant === "lark" ? "lark" : "feishu");
|
|
165
161
|
}
|
|
166
|
-
for (const name of ["--
|
|
162
|
+
for (const name of ["--model", "--from-cli-profile"]) {
|
|
167
163
|
const value = flag(name);
|
|
168
164
|
if (value)
|
|
169
165
|
registerArgs.push(name, value);
|
|
@@ -193,12 +189,11 @@ export async function main() {
|
|
|
193
189
|
die(`Runtime ${configuredAgent.runtime} 不受支持`);
|
|
194
190
|
// 非交互(Agent 驱动)时仍跳过 probeNativeRuntimeReadiness:该探测保持 interactive-only。
|
|
195
191
|
// external-pi 绑定已在 setup-bind 内做非交互 Pi RPC catalog discovery(RPC over pipes 不需要 TTY);
|
|
196
|
-
// daemon attach 探测仍是权威校验。其余真实阻塞(缺 runtime 可执行文件 /
|
|
192
|
+
// daemon attach 探测仍是权威校验。其余真实阻塞(缺 runtime 可执行文件 / 缺登录 / 缺 lark-cli)一律 fast-fail。
|
|
197
193
|
if (!OPT.nonInteractive) {
|
|
198
194
|
const runtimeReadiness = await probeNativeRuntimeReadiness({ runtime: configuredAgent.runtime,
|
|
199
195
|
agentId: selectedAgentId, cwd: configuredAgent.workspaceDir,
|
|
200
|
-
env: { ...process.env, LARKIN_CONFIG_DIR: CFG_DIR
|
|
201
|
-
...(configuredAgent.piDistribution ? { LARKIN_PI_DISTRIBUTION: configuredAgent.piDistribution } : {}) } });
|
|
196
|
+
env: { ...process.env, LARKIN_CONFIG_DIR: CFG_DIR } });
|
|
202
197
|
if (runtimeReadiness.state !== "ready") {
|
|
203
198
|
die(`Runtime ${configuredAgent.runtime} ${runtimeReadiness.state}:${runtimeReadiness.reason || "prerequisite unavailable"};${runtimeReadiness.nextAction || "修复后重试"}`);
|
|
204
199
|
}
|