larkin 0.4.27 → 0.4.28
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/dist/app/agent-cli.mjs +4 -2
- package/dist/app/agent-config.mjs +42 -28
- package/dist/app/cli.mjs +3 -3
- package/dist/app/runtime-model-directory.mjs +3 -2
- package/dist/app/setup.mjs +15 -14
- package/dist/dashboard/dashboard-config-controller.mjs +15 -8
- package/dist/dashboard/dashboard-view-model.mjs +3 -2
- package/dist/dashboard/web/assets/dashboard.js +1 -1
- package/dist/platform/config.cjs +30 -7
- package/dist/platform/config.mjs +22 -7
- package/dist/runtime/runtime-model-catalog.cjs +4 -3
- package/dist/runtime/runtime-model-catalog.mjs +4 -3
- package/dist/runtime/user-runtime.cjs +51 -0
- package/dist/runtime/user-runtime.mjs +41 -0
- package/dist/setup/bot-register.mjs +3 -3
- package/dist/setup/setup-agent-choice.mjs +10 -9
- package/dist/setup/setup-bind.mjs +2 -2
- package/package.json +1 -1
package/dist/app/agent-cli.mjs
CHANGED
|
@@ -157,7 +157,7 @@ function agentConfigRequest(argv, env, config, agent, dependencies) {
|
|
|
157
157
|
const target = config.agents[targetId];
|
|
158
158
|
if (!target)
|
|
159
159
|
throw new Error(`Agent 不存在:${targetId};运行 larkin config show --json 查看可用 Agent`);
|
|
160
|
-
const currentDirectory = (runtime = target.runtime) => (dependencies.modelDirectory ?? ((input) => discoverRuntimeModelDirectorySync(input, env)))({
|
|
160
|
+
const currentDirectory = (runtime = larkinConfig.toUserRuntime(target.runtime, target.piDistribution)) => (dependencies.modelDirectory ?? ((input) => discoverRuntimeModelDirectorySync(input, env)))({
|
|
161
161
|
agentId: target.agentId, cwd: target.workspaceDir, runtime,
|
|
162
162
|
});
|
|
163
163
|
if (operation === "show") {
|
|
@@ -593,7 +593,9 @@ export function runAgentCli(argv, env = process.env, dependencies = {}) {
|
|
|
593
593
|
kind: "agent", id: agent.agentId, isSelf: true, name, displayName: name,
|
|
594
594
|
openId: identity?.open_id ?? identity?.openId ?? null,
|
|
595
595
|
avatarUrl: identity?.avatar_url ?? identity?.avatarUrl ?? null,
|
|
596
|
-
runtime: agent.runtime,
|
|
596
|
+
runtime: agent.runtime,
|
|
597
|
+
runtimeOption: larkinConfig.toUserRuntime(agent.runtime, agent.piDistribution),
|
|
598
|
+
model: agent.model, reasoningEffort: agent.effort ?? null,
|
|
597
599
|
createdAt: agent.createdAt ?? "1970-01-01T00:00:00.000Z",
|
|
598
600
|
});
|
|
599
601
|
return 0;
|
|
@@ -11,7 +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 { assertBuiltinPiAgentDirectory, ownedPiCatalogAgentDir, piAgentDirectory, piCatalogCommandSpec
|
|
14
|
+
import { assertBuiltinPiAgentDirectory, ownedPiCatalogAgentDir, piAgentDirectory, piCatalogCommandSpec } from "../runtime/pi-provider-config.mjs";
|
|
15
|
+
import { RUNTIME_OPTIONS, fromUserRuntime, isUserRuntime, piCatalogDistributionForUserRuntime, toUserRuntime } from "../runtime/user-runtime.mjs";
|
|
15
16
|
import { traceProcessBoundary } from "../platform/process-boundary-trace.mjs";
|
|
16
17
|
function safeDocumentCommentCapabilities(configDir, profile) {
|
|
17
18
|
try {
|
|
@@ -223,6 +224,7 @@ if (kind === "agents") {
|
|
|
223
224
|
agent_id: agent.agentId,
|
|
224
225
|
name: agent.name,
|
|
225
226
|
runtime: agent.runtime,
|
|
227
|
+
runtimeOption: toUserRuntime(agent.runtime, agent.piDistribution),
|
|
226
228
|
model: effectiveModel,
|
|
227
229
|
document_comment: {
|
|
228
230
|
event: "drive.notice.comment_add_v1",
|
|
@@ -293,7 +295,7 @@ if (kind === "agents") {
|
|
|
293
295
|
say(` ${agent.name}${agent.name === config.activeAgent ? " [active]" : ""}`);
|
|
294
296
|
const effectiveModel = status?.session?.runtime === agent.runtime && status.session.model ? status.session.model : agent.model;
|
|
295
297
|
const effectiveEffort = status?.session?.runtime === agent.runtime && status.session.reasoningEffort ? status.session.reasoningEffort : agent.effort;
|
|
296
|
-
say(` runtime=${agent.runtime} model=${effectiveModel}${effectiveModel !== agent.model ? ` stored=${agent.model}` : ""}${effectiveEffort ? ` effort=${effectiveEffort}` : ""}`);
|
|
298
|
+
say(` runtime=${toUserRuntime(agent.runtime, agent.piDistribution)} model=${effectiveModel}${effectiveModel !== agent.model ? ` stored=${agent.model}` : ""}${effectiveEffort ? ` effort=${effectiveEffort}` : ""}`);
|
|
297
299
|
if (status?.runtimeReadiness) {
|
|
298
300
|
const current = projectedReadiness.readiness.runtime_ready;
|
|
299
301
|
const state = status.runtimeReadiness.state === "ready" && !current ? "unavailable" : status.runtimeReadiness.state || "incompatible";
|
|
@@ -427,7 +429,7 @@ if (!key) {
|
|
|
427
429
|
say(`共 ${keys.length} 个 agent(用 --agent <appId> 查看/修改指定一个):`);
|
|
428
430
|
for (const candidate of keys) {
|
|
429
431
|
const item = config.agents[candidate];
|
|
430
|
-
say(` ${candidate}${candidate === config.activeAgent ? " [active]" : ""} runtime=${item.runtime} model=${item.model}`);
|
|
432
|
+
say(` ${candidate}${candidate === config.activeAgent ? " [active]" : ""} runtime=${toUserRuntime(item.runtime, item.piDistribution)} model=${item.model}`);
|
|
431
433
|
}
|
|
432
434
|
process.exit(0);
|
|
433
435
|
}
|
|
@@ -441,7 +443,12 @@ if (kind === "pi-distribution") {
|
|
|
441
443
|
if (importExternalProfile && requested !== "builtin")
|
|
442
444
|
die("--import-external-profile 只允许与 builtin 一起使用");
|
|
443
445
|
if (requested === "show") {
|
|
444
|
-
say(JSON.stringify({
|
|
446
|
+
say(JSON.stringify({
|
|
447
|
+
agentId: selectedKey,
|
|
448
|
+
runtime: agent.runtime,
|
|
449
|
+
runtimeOption: toUserRuntime(agent.runtime, agent.piDistribution),
|
|
450
|
+
piDistribution: agent.piDistribution ?? "external",
|
|
451
|
+
}));
|
|
445
452
|
process.exit(0);
|
|
446
453
|
}
|
|
447
454
|
if (requested !== "builtin" && requested !== "external")
|
|
@@ -492,11 +499,18 @@ if ((["model", "effort"].includes(kind || "") && agent.runtime === "claude") ||
|
|
|
492
499
|
}
|
|
493
500
|
}
|
|
494
501
|
let piCatalog = null;
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
502
|
+
const userRuntime = toUserRuntime(agent.runtime, agent.piDistribution);
|
|
503
|
+
const catalogUserRuntime = kind === "runtime" && (value === "pi" || value === "builtin-pi")
|
|
504
|
+
? value
|
|
505
|
+
: userRuntime;
|
|
506
|
+
if ((kind === "model" || kind === "effort" || kind === "runtime") && !value) {
|
|
507
|
+
say(`agent=${selectedKey} runtime=${userRuntime} model=${agent.model}`);
|
|
508
|
+
}
|
|
509
|
+
const needsLivePiCatalog = (["model", "effort"].includes(kind) && agent.runtime === "pi")
|
|
510
|
+
|| (kind === "runtime" && (value === "pi" || value === "builtin-pi") && Boolean(flagModel));
|
|
511
|
+
if (needsLivePiCatalog) {
|
|
498
512
|
try {
|
|
499
|
-
const catalogCommand = piCatalogCommandSpec(
|
|
513
|
+
const catalogCommand = piCatalogCommandSpec(piCatalogDistributionForUserRuntime(catalogUserRuntime), process.env);
|
|
500
514
|
piCatalog = await discoverPiModelCatalog({
|
|
501
515
|
cwd: String(agent.workspaceDir),
|
|
502
516
|
agentDir: ownedPiCatalogAgentDir(configDir, selectedKey),
|
|
@@ -513,10 +527,14 @@ if (agent.runtime === "pi" || (kind === "runtime" && value === "pi")) {
|
|
|
513
527
|
const loadedPiCatalog = piCatalog;
|
|
514
528
|
for (const diagnostic of loadedPiCatalog.diagnostics)
|
|
515
529
|
console.error(`Pi diagnostic: ${diagnostic}`);
|
|
516
|
-
|
|
530
|
+
const piModels = [{ id: "default", label: `default: ${loadedPiCatalog.effectiveModel}` }, ...loadedPiCatalog.models];
|
|
531
|
+
catalog.pi = piModels;
|
|
532
|
+
}
|
|
533
|
+
function catalogRuntimeKey(runtime) {
|
|
534
|
+
return isUserRuntime(runtime) ? fromUserRuntime(runtime).runtime : runtime;
|
|
517
535
|
}
|
|
518
536
|
function listModels(runtime, current) {
|
|
519
|
-
const items = catalog[runtime];
|
|
537
|
+
const items = catalog[catalogRuntimeKey(runtime)];
|
|
520
538
|
if (!items) {
|
|
521
539
|
say(` (无 ${runtime} 的目录数据)`);
|
|
522
540
|
return;
|
|
@@ -536,7 +554,7 @@ function writeAndHint(mutation) {
|
|
|
536
554
|
say(" 运行 larkin config apply --agent <App ID> 后应用到新 Runtime session");
|
|
537
555
|
}
|
|
538
556
|
function effortChoicesFor(runtime, model) {
|
|
539
|
-
const entry = catalog[runtime]?.find((candidate) => candidate.id === model);
|
|
557
|
+
const entry = catalog[catalogRuntimeKey(runtime)]?.find((candidate) => candidate.id === model);
|
|
540
558
|
if (!entry?.supportedReasoningEfforts?.length)
|
|
541
559
|
return null;
|
|
542
560
|
return { list: entry.supportedReasoningEfforts, note: "模型声明", def: entry.defaultReasoningEffort || null };
|
|
@@ -554,15 +572,14 @@ function dropEffortIfInvalid(next) {
|
|
|
554
572
|
return withoutEffort;
|
|
555
573
|
}
|
|
556
574
|
if (kind === "model") {
|
|
557
|
-
const runtime =
|
|
575
|
+
const runtime = userRuntime;
|
|
558
576
|
if (!value) {
|
|
559
|
-
say(`agent=${selectedKey} runtime=${runtime} model=${agent.model}`);
|
|
560
577
|
say(`\n${runtime} 可选模型:`);
|
|
561
578
|
listModels(runtime, agent.model);
|
|
562
579
|
say("\n切换: larkin model <id>");
|
|
563
580
|
process.exit(0);
|
|
564
581
|
}
|
|
565
|
-
const items = catalog[runtime];
|
|
582
|
+
const items = catalog[catalogRuntimeKey(runtime)];
|
|
566
583
|
if (items && !items.some((model) => model.id === value)) {
|
|
567
584
|
console.error(`✗ "${value}" 不是 ${runtime} 的合法模型。合法值:`);
|
|
568
585
|
for (const model of items)
|
|
@@ -588,13 +605,11 @@ else if (kind === "effort") {
|
|
|
588
605
|
}
|
|
589
606
|
const choices = effortChoicesFor(agent.runtime, agent.model);
|
|
590
607
|
if (!choices) {
|
|
591
|
-
say(
|
|
592
|
-
say(`模型 ${agent.runtime}/${agent.model} 未显式声明 supportedReasoningEfforts,不能设置 effort。`);
|
|
608
|
+
say(`模型 ${userRuntime}/${agent.model} 未显式声明 supportedReasoningEfforts,不能设置 effort。`);
|
|
593
609
|
process.exit(value ? 1 : 0);
|
|
594
610
|
}
|
|
595
611
|
const { list, note, def } = choices;
|
|
596
612
|
if (!value) {
|
|
597
|
-
say(`agent=${selectedKey} runtime=${agent.runtime} model=${agent.model}`);
|
|
598
613
|
say(`effort=${agent.effort || `(未设置,runtime 默认${def ? `,该模型默认 ${def}` : ""})`}`);
|
|
599
614
|
say(`\n${agent.model} 可选档位(${note}):`);
|
|
600
615
|
for (const level of list)
|
|
@@ -603,7 +618,7 @@ else if (kind === "effort") {
|
|
|
603
618
|
process.exit(0);
|
|
604
619
|
}
|
|
605
620
|
if (!list.includes(value)) {
|
|
606
|
-
console.error(`✗ "${value}" 不是 ${
|
|
621
|
+
console.error(`✗ "${value}" 不是 ${userRuntime}/${agent.model} 的合法档位。合法值: ${list.join(", ")}`);
|
|
607
622
|
process.exit(1);
|
|
608
623
|
}
|
|
609
624
|
if (value === agent.effort) {
|
|
@@ -671,34 +686,33 @@ else if (kind === "chats") {
|
|
|
671
686
|
}
|
|
672
687
|
else {
|
|
673
688
|
if (!value) {
|
|
674
|
-
say(`agent=${selectedKey} runtime=${agent.runtime} model=${agent.model}`);
|
|
675
689
|
say("\n可选 runtime:");
|
|
676
|
-
for (const runtime of
|
|
677
|
-
say(` ${runtime.padEnd(12)}${runtime ===
|
|
690
|
+
for (const runtime of RUNTIME_OPTIONS) {
|
|
691
|
+
say(` ${runtime.padEnd(12)}${runtime === userRuntime ? " [← 当前]" : ""} 默认模型=${larkinConfig.defaultModelFor(runtime)}`);
|
|
678
692
|
}
|
|
679
693
|
say("\n切换: larkin runtime <id> [--model <id>]");
|
|
680
694
|
process.exit(0);
|
|
681
695
|
}
|
|
682
|
-
if (!
|
|
683
|
-
console.error(`✗ "${value}" 不是合法 runtime。合法值: ${
|
|
696
|
+
if (!isUserRuntime(value)) {
|
|
697
|
+
console.error(`✗ "${value}" 不是合法 runtime。合法值: ${RUNTIME_OPTIONS.join(", ")}`);
|
|
684
698
|
process.exit(1);
|
|
685
699
|
}
|
|
686
700
|
let model = flagModel;
|
|
687
701
|
if (model) {
|
|
688
|
-
const items = catalog[value];
|
|
689
|
-
if (!items.some((candidate) => candidate.id === model)) {
|
|
702
|
+
const items = catalog[catalogRuntimeKey(value)];
|
|
703
|
+
if (!items || !items.some((candidate) => candidate.id === model)) {
|
|
690
704
|
console.error(`✗ "${model}" 不是 ${value} 的合法模型。合法值:`);
|
|
691
|
-
for (const item of items)
|
|
705
|
+
for (const item of items || [])
|
|
692
706
|
console.error(` ${item.id}`);
|
|
693
707
|
process.exit(1);
|
|
694
708
|
}
|
|
695
709
|
}
|
|
696
710
|
else {
|
|
697
711
|
model = larkinConfig.defaultModelFor(value);
|
|
698
|
-
if (value !==
|
|
712
|
+
if (value !== userRuntime)
|
|
699
713
|
say(`model 随 runtime 重置为默认: ${agent.model} → ${model}(可用 --model 显式指定)`);
|
|
700
714
|
}
|
|
701
|
-
if (value ===
|
|
715
|
+
if (value === userRuntime && model === agent.model) {
|
|
702
716
|
say(`runtime 已经是 ${value},无需修改`);
|
|
703
717
|
process.exit(0);
|
|
704
718
|
}
|
package/dist/app/cli.mjs
CHANGED
|
@@ -49,8 +49,8 @@ if (runtimeAgentAuthority && command === "comment")
|
|
|
49
49
|
const commandHelp = {
|
|
50
50
|
start: `Usage: larkin start [--agent <App ID> | --agents <App ID,...>]
|
|
51
51
|
Start one foreground supervisor for the daemon and local dashboard, or reuse it.`,
|
|
52
|
-
setup: `Usage: larkin setup [--tenant feishu|lark] [--runtime <builtin-pi|
|
|
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 keep the guided flow; Agent-driven (non-TTY) runs default to builtin-pi and need --provider/--api-key (or --runtime
|
|
52
|
+
setup: `Usage: larkin setup [--tenant feishu|lark] [--runtime <builtin-pi|pi|codex|claude>] [--provider <id> --api-key <key>]
|
|
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 keep the guided flow; Agent-driven (non-TTY) runs default to builtin-pi and need --provider/--api-key (or --runtime pi).`,
|
|
54
54
|
status: `Usage: larkin status [--json]
|
|
55
55
|
Show Agent configuration, bot identity, credentials, and connection status. Use --json for readiness automation.`,
|
|
56
56
|
agents: `Usage: larkin agents [--json]
|
|
@@ -58,7 +58,7 @@ List every configured Agent and its current local status. Use --json for daemon/
|
|
|
58
58
|
model: `Usage: larkin model [<model>] [--agent <App ID>]
|
|
59
59
|
Show or change an Agent model.`,
|
|
60
60
|
runtime: `Usage: larkin runtime [<runtime>] [--agent <App ID>] [--model <model>]
|
|
61
|
-
Show or change an Agent runtime.`,
|
|
61
|
+
Show or change an Agent runtime. User-facing ids: pi | builtin-pi | codex | claude.`,
|
|
62
62
|
"pi-distribution": `Usage: larkin pi-distribution [show|builtin|external] [--agent <App ID>] [--snapshot <private-file>] [--import-external-profile]
|
|
63
63
|
larkin pi-distribution rollback --snapshot <private-file>
|
|
64
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.`,
|
|
@@ -7,6 +7,7 @@ import { discoverClaudeModelCatalog } from "../runtime/claude-model-catalog.mjs"
|
|
|
7
7
|
import { discoverPiModelCatalog } from "../runtime/pi-model-catalog.mjs";
|
|
8
8
|
import { discoverCodexModelCatalog } from "../runtime/codex-model-catalog.mjs";
|
|
9
9
|
import { ownedPiCatalogAgentDir, piCatalogCommandSpec } from "../runtime/pi-provider-config.mjs";
|
|
10
|
+
import { piCatalogDistributionForUserRuntime } from "../runtime/user-runtime.mjs";
|
|
10
11
|
function readTestModelDirectory(env) {
|
|
11
12
|
const fixture = env?.LARKIN_TEST_RUNTIME_MODEL_DIRECTORY_FILE;
|
|
12
13
|
if (!fixture)
|
|
@@ -36,7 +37,7 @@ export async function discoverRuntimeModelDirectory(input) {
|
|
|
36
37
|
...catalog.models.map(({ id, label, supportedReasoningEfforts }) => ({ id, label, supportedReasoningEfforts })),
|
|
37
38
|
];
|
|
38
39
|
}
|
|
39
|
-
if (input.runtime === "pi") {
|
|
40
|
+
if (input.runtime === "pi" || input.runtime === "builtin-pi") {
|
|
40
41
|
const env = input.env ?? process.env;
|
|
41
42
|
const agentId = input.agentId || "";
|
|
42
43
|
if (!agentId)
|
|
@@ -46,7 +47,7 @@ export async function discoverRuntimeModelDirectory(input) {
|
|
|
46
47
|
const agent = config.agents[agentId];
|
|
47
48
|
if (!agent)
|
|
48
49
|
throw new Error(`unknown agent: ${agentId}`);
|
|
49
|
-
const catalogCommand = piCatalogCommandSpec(
|
|
50
|
+
const catalogCommand = piCatalogCommandSpec(piCatalogDistributionForUserRuntime(input.runtime), env);
|
|
50
51
|
const catalog = await discoverPiModelCatalog({
|
|
51
52
|
cwd: input.cwd,
|
|
52
53
|
agentDir: ownedPiCatalogAgentDir(configDir, agentId),
|
package/dist/app/setup.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import readline from "node:readline/promises";
|
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import * as larkinConfig from "../platform/config.mjs";
|
|
10
10
|
import { PI_PROVIDER_PRESETS } from "../runtime/pi-provider-config.mjs";
|
|
11
|
+
import { fromUserRuntime, isUserRuntime } from "../runtime/user-runtime.mjs";
|
|
11
12
|
const PI_PROVIDER_IDS = PI_PROVIDER_PRESETS.map((p) => p.id).join(" | ") + " | custom";
|
|
12
13
|
import { acquireProcessLock, readProcessState } from "../platform/process-state.mjs";
|
|
13
14
|
import { requestAgentUpsert } from "./local-control.mjs";
|
|
@@ -35,18 +36,18 @@ if (has("--comment-subscription")) {
|
|
|
35
36
|
// 默认应用级评论订阅:平台验证的应用订阅让该应用可见文档的全部评论进入 Agent Inbox。
|
|
36
37
|
const commentSubscription = "application";
|
|
37
38
|
const OPT = { runtime: flag("--runtime") || null, commentSubscription, start: true, help: has("--help") || has("-h"), nonInteractive: !Boolean(process.stdin.isTTY) };
|
|
38
|
-
// runtime 枚举归一:builtin-pi(默认)/
|
|
39
|
-
const RUNTIME_ENUM = ["builtin-pi", "
|
|
39
|
+
// runtime 枚举归一:builtin-pi(默认)/ pi / codex / claude → 内部 (runtime, distribution)
|
|
40
|
+
const RUNTIME_ENUM = ["builtin-pi", "pi", "codex", "claude"];
|
|
40
41
|
function normalizeRuntime(value) {
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
42
|
+
const raw = value || "builtin-pi";
|
|
43
|
+
if (raw === "external-pi") {
|
|
44
|
+
console.error("external-pi 已改名为 pi");
|
|
45
|
+
return fromUserRuntime("pi");
|
|
46
|
+
}
|
|
47
|
+
if (!isUserRuntime(raw) || !RUNTIME_ENUM.includes(raw)) {
|
|
43
48
|
die(`未知 runtime \`${value}\`;可选:${RUNTIME_ENUM.join(" | ")}`);
|
|
44
49
|
}
|
|
45
|
-
|
|
46
|
-
return { runtime: "pi", distribution: "builtin" };
|
|
47
|
-
if (chosen === "external-pi")
|
|
48
|
-
return { runtime: "pi", distribution: "external" };
|
|
49
|
-
return { runtime: chosen };
|
|
50
|
+
return fromUserRuntime(raw);
|
|
50
51
|
}
|
|
51
52
|
const normalizedRuntime = normalizeRuntime(OPT.runtime);
|
|
52
53
|
// 参数化路径:显式 --runtime、无 TTY(Agent 驱动)、或任何 provider 参数。
|
|
@@ -59,12 +60,12 @@ if (!OPT.help && parameterized && normalizedRuntime.distribution === "builtin")
|
|
|
59
60
|
const b = flag("--base-url");
|
|
60
61
|
const k = flag("--api-key");
|
|
61
62
|
if (!p && !b)
|
|
62
|
-
die(`builtin-pi 需要 --provider <id>(${PI_PROVIDER_IDS})或 --base-url;或 --runtime
|
|
63
|
+
die(`builtin-pi 需要 --provider <id>(${PI_PROVIDER_IDS})或 --base-url;或 --runtime pi 用已有 pi`);
|
|
63
64
|
if (!k)
|
|
64
|
-
die("builtin-pi 需要 --api-key;或 --runtime
|
|
65
|
+
die("builtin-pi 需要 --api-key;或 --runtime pi 用已有登录");
|
|
65
66
|
}
|
|
66
67
|
if (!OPT.help && normalizedRuntime.distribution === "external" && (flag("--provider") || flag("--api-key") || flag("--base-url"))) {
|
|
67
|
-
die("
|
|
68
|
+
die("pi 使用已有 pi 环境,不接受 --provider/--api-key/--base-url");
|
|
68
69
|
}
|
|
69
70
|
if (OPT.help) {
|
|
70
71
|
say(`larkin setup — Create or connect a Feishu (Lark) bot, then configure and attach its Agent
|
|
@@ -74,7 +75,7 @@ Usage:
|
|
|
74
75
|
larkin setup --provider <id> --api-key <key> 指定内置 Pi provider(无需交互)
|
|
75
76
|
|
|
76
77
|
Options:
|
|
77
|
-
--runtime <runtime> Agent runtime 枚举:builtin-pi(默认,内置 Pi)|
|
|
78
|
+
--runtime <runtime> Agent runtime 枚举:builtin-pi(默认,内置 Pi)| pi | codex | claude
|
|
78
79
|
--provider <id> 内置 Pi provider:deepseek | kimi | minimax | zhipu | openai |
|
|
79
80
|
anthropic | gemini | groq | cerebras | xai | fireworks |
|
|
80
81
|
together | mistral | openrouter | kimi-coding | qwen-cn | custom
|
|
@@ -96,7 +97,7 @@ daemon + dashboard supervisor used by larkin start.
|
|
|
96
97
|
|
|
97
98
|
Interactive terminals without flags keep the guided flow (browser bot selection + runtime choice).
|
|
98
99
|
Non-interactive (Agent-driven) runs default to builtin-pi and require --provider/--api-key (or
|
|
99
|
-
--runtime
|
|
100
|
+
--runtime pi to reuse an existing pi login).`);
|
|
100
101
|
process.exit(0);
|
|
101
102
|
}
|
|
102
103
|
function runForeground(mode, args) {
|
|
@@ -8,6 +8,7 @@ import { discoverCodexModelCatalog } from "../runtime/codex-model-catalog.mjs";
|
|
|
8
8
|
import { discoverPiModelCatalog } from "../runtime/pi-model-catalog.mjs";
|
|
9
9
|
import { managedOfficialLarkCli } from "../app/agent-lark-cli-workspace.mjs";
|
|
10
10
|
import { ownedPiCatalogAgentDir, piCatalogCommandSpec } from "../runtime/pi-provider-config.mjs";
|
|
11
|
+
import { RUNTIME_OPTIONS, piCatalogDistributionForUserRuntime, toUserRuntime } from "../runtime/user-runtime.mjs";
|
|
11
12
|
function pruneCache(cache, expired, maxEntries) {
|
|
12
13
|
for (const [key, value] of cache)
|
|
13
14
|
if (expired(value))
|
|
@@ -383,12 +384,12 @@ function dashboardMutation(value) {
|
|
|
383
384
|
return { kind: "set-agent-effort", agentId, effort: value.effort === null || value.effort === "default" ? null : String(value.effort || "") };
|
|
384
385
|
}
|
|
385
386
|
export function createDashboardConfigController({ csrfCapability, env = process.env, requestUpsert = requestAgentUpsert, chatDirectoryResolver = createChatDirectoryResolver(), claudeModelDirectoryResolver = createClaudeModelDirectoryResolver(), codexModelDirectoryResolver = createCodexModelDirectoryResolver(), piModelDirectoryResolver = createPiModelDirectoryResolver(), }) {
|
|
386
|
-
const resolvePiModelDirectory = async (agentId) => {
|
|
387
|
+
const resolvePiModelDirectory = async (agentId, userRuntime) => {
|
|
387
388
|
const { config, configDir } = loadConfig(env);
|
|
388
389
|
const agent = config.agents[agentId];
|
|
389
390
|
if (!agent)
|
|
390
391
|
throw new Error("unknown agent");
|
|
391
|
-
const catalogCommand = piCatalogCommandSpec(
|
|
392
|
+
const catalogCommand = piCatalogCommandSpec(piCatalogDistributionForUserRuntime(userRuntime), env);
|
|
392
393
|
return await piModelDirectoryResolver.resolve({
|
|
393
394
|
agentId,
|
|
394
395
|
cwd: agent.workspaceDir,
|
|
@@ -402,8 +403,8 @@ export function createDashboardConfigController({ csrfCapability, env = process.
|
|
|
402
403
|
const agent = config.agents[agentId];
|
|
403
404
|
if (!agent)
|
|
404
405
|
throw new Error("unknown agent");
|
|
405
|
-
if (runtime === "pi")
|
|
406
|
-
return await resolvePiModelDirectory(agentId);
|
|
406
|
+
if (runtime === "pi" || runtime === "builtin-pi")
|
|
407
|
+
return await resolvePiModelDirectory(agentId, runtime);
|
|
407
408
|
if (runtime === "codex")
|
|
408
409
|
return await codexModelDirectoryResolver.resolve({ agentId, cwd: agent.workspaceDir, env });
|
|
409
410
|
if (runtime === "claude")
|
|
@@ -420,7 +421,9 @@ export function createDashboardConfigController({ csrfCapability, env = process.
|
|
|
420
421
|
const agent = config.agents[mutation.agentId];
|
|
421
422
|
if (!agent)
|
|
422
423
|
throw new Error("unknown agent");
|
|
423
|
-
const runtime = mutation.kind === "set-agent-runtime"
|
|
424
|
+
const runtime = mutation.kind === "set-agent-runtime"
|
|
425
|
+
? mutation.runtime
|
|
426
|
+
: toUserRuntime(agent.runtime, agent.piDistribution);
|
|
424
427
|
const model = mutation.kind === "set-agent-runtime" ? mutation.model || "default"
|
|
425
428
|
: mutation.kind === "set-agent-model" ? mutation.model : agent.model;
|
|
426
429
|
if (mutation.kind === "set-agent-effort" && mutation.effort === null)
|
|
@@ -440,18 +443,22 @@ export function createDashboardConfigController({ csrfCapability, env = process.
|
|
|
440
443
|
if (requestUrl.pathname === "/api/config" && req.method === "GET") {
|
|
441
444
|
try {
|
|
442
445
|
assertPrivateReadRequest(req, csrfCapability);
|
|
443
|
-
json(res, 200, {
|
|
446
|
+
json(res, 200, {
|
|
447
|
+
...await sanitizedView(env, chatDirectoryResolver, requestUrl.searchParams.get("agent") || undefined, requestUrl.searchParams.get("chat") || undefined),
|
|
448
|
+
runtimeModels: loadRuntimeModels(),
|
|
449
|
+
runtimeOptions: [...RUNTIME_OPTIONS],
|
|
450
|
+
});
|
|
444
451
|
}
|
|
445
452
|
catch (error) {
|
|
446
453
|
json(res, error instanceof Error && /host|capability/.test(error.message) ? 403 : 500, { error: "configuration unavailable" });
|
|
447
454
|
}
|
|
448
455
|
return true;
|
|
449
456
|
}
|
|
450
|
-
if (requestUrl.pathname === "/api/models/pi" && req.method === "GET") {
|
|
457
|
+
if ((requestUrl.pathname === "/api/models/pi" || requestUrl.pathname === "/api/models/builtin-pi") && req.method === "GET") {
|
|
451
458
|
try {
|
|
452
459
|
assertPrivateReadRequest(req, csrfCapability);
|
|
453
460
|
const agentId = requestUrl.searchParams.get("agent") || "";
|
|
454
|
-
const models = await resolvePiModelDirectory(agentId);
|
|
461
|
+
const models = await resolvePiModelDirectory(agentId, requestUrl.pathname === "/api/models/builtin-pi" ? "builtin-pi" : "pi");
|
|
455
462
|
json(res, 200, { models });
|
|
456
463
|
}
|
|
457
464
|
catch (error) {
|
|
@@ -13,6 +13,7 @@ import { collectWorkspaceEntry as collectTypedWorkspaceEntry } from "./dashboard
|
|
|
13
13
|
import { buildFingerprint, packageVersion } from "../platform/build-info.mjs";
|
|
14
14
|
import * as larkinConfig from "../platform/config.mjs";
|
|
15
15
|
import { ownedPiCatalogAgentDir, piCatalogCommandSpec } from "../runtime/pi-provider-config.mjs";
|
|
16
|
+
import { toUserRuntime } from "../runtime/user-runtime.mjs";
|
|
16
17
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
17
18
|
const ROOT = path.resolve(HERE, "../..");
|
|
18
19
|
export const PACKAGE_VERSION = packageVersion(ROOT);
|
|
@@ -502,7 +503,7 @@ async function collectAgentStatus(a, configDir, daemonStartedAt, piModelResolver
|
|
|
502
503
|
agentId: a.agentId,
|
|
503
504
|
name: a.name,
|
|
504
505
|
displayName: (botIdentity && botIdentity.name) || a.name,
|
|
505
|
-
runtime: a.runtime,
|
|
506
|
+
runtime: toUserRuntime(a.runtime, a.piDistribution),
|
|
506
507
|
model: status.session?.runtime === a.runtime && status.session?.model ? String(status.session.model) : a.model,
|
|
507
508
|
effort: status.session?.runtime === a.runtime && status.session?.reasoningEffort ? String(status.session.reasoningEffort) : a.effort || null,
|
|
508
509
|
runtimeReadiness,
|
|
@@ -526,7 +527,7 @@ async function collectAgentStatus(a, configDir, daemonStartedAt, piModelResolver
|
|
|
526
527
|
sessions: agentState.sessions || {},
|
|
527
528
|
session: sessionId ? {
|
|
528
529
|
id: sessionId,
|
|
529
|
-
runtime: a.runtime,
|
|
530
|
+
runtime: toUserRuntime(a.runtime, a.piDistribution),
|
|
530
531
|
startedAt: usage.startedAt || (status.session?.id === sessionId ? status.session.startedAt : null) || null,
|
|
531
532
|
ageSec: ageSec(usage.startedAt || (status.session?.id === sessionId ? status.session.startedAt : null)),
|
|
532
533
|
lastTurnAt: status.session?.lastTurnAt || null,
|
|
@@ -46,4 +46,4 @@ Error generating stack: `+e.message+`
|
|
|
46
46
|
`},or=function(){var e=parseInt(document.body.getAttribute(`data-scroll-locked`)||`0`,10);return isFinite(e)?e:0},sr=function(){C.useEffect(function(){return document.body.setAttribute(ir,(or()+1).toString()),function(){var e=or()-1;e<=0?document.body.removeAttribute(ir):document.body.setAttribute(ir,e.toString())}},[])},cr=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,i=r===void 0?`margin`:r;sr();var a=C.useMemo(function(){return nr(i)},[i]);return C.createElement(rr,{styles:ar(a,!t,i,n?``:`!important`)})},lr=!1;if(typeof window<`u`)try{var ur=Object.defineProperty({},"passive",{get:function(){return lr=!0,!0}});window.addEventListener(`test`,ur,ur),window.removeEventListener(`test`,ur,ur)}catch{lr=!1}var dr=lr?{passive:!1}:!1,fr=function(e){return e.tagName===`TEXTAREA`},pr=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return n[t]!==`hidden`&&!(n.overflowY===n.overflowX&&!fr(e)&&n[t]===`visible`)},mr=function(e){return pr(e,`overflowY`)},hr=function(e){return pr(e,`overflowX`)},gr=function(e,t){var n=t.ownerDocument,r=t;do{if(typeof ShadowRoot<`u`&&r instanceof ShadowRoot&&(r=r.host),yr(e,r)){var i=br(e,r);if(i[1]>i[2])return!0}r=r.parentNode}while(r&&r!==n.body);return!1},_r=function(e){return[e.scrollTop,e.scrollHeight,e.clientHeight]},vr=function(e){return[e.scrollLeft,e.scrollWidth,e.clientWidth]},yr=function(e,t){return e===`v`?mr(t):hr(t)},br=function(e,t){return e===`v`?_r(t):vr(t)},xr=function(e,t){return e===`h`&&t===`rtl`?-1:1},Sr=function(e,t,n,r,i){var a=xr(e,window.getComputedStyle(t).direction),o=a*r,s=n.target,c=t.contains(s),l=!1,u=o>0,d=0,f=0;do{if(!s)break;var p=br(e,s),m=p[0],h=p[1]-p[2]-a*m;(m||h)&&yr(e,s)&&(d+=h,f+=m);var g=s.parentNode;s=g&&g.nodeType===Node.DOCUMENT_FRAGMENT_NODE?g.host:g}while(!c&&s!==document.body||c&&(t.contains(s)||t===s));return(u&&(i&&Math.abs(d)<1||!i&&o>d)||!u&&(i&&Math.abs(f)<1||!i&&-o>f))&&(l=!0),l},Cr=function(e){return`changedTouches`in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},wr=function(e){return[e.deltaX,e.deltaY]},Tr=function(e){return e&&`current`in e?e.current:e},Er=function(e,t){return e[0]===t[0]&&e[1]===t[1]},Dr=function(e){return`
|
|
47
47
|
.block-interactivity-${e} {pointer-events: none;}
|
|
48
48
|
.allow-interactivity-${e} {pointer-events: all;}
|
|
49
|
-
`},Or=0,kr=[];function Ar(e){var t=C.useRef([]),n=C.useRef([0,0]),r=C.useRef(),i=C.useState(Or++)[0],a=C.useState(Qn)[0],o=C.useRef(e);C.useEffect(function(){o.current=e},[e]),C.useEffect(function(){if(e.inert){document.body.classList.add(`block-interactivity-${i}`);var t=Dn([e.lockRef.current],(e.shards||[]).map(Tr),!0).filter(Boolean);return t.forEach(function(e){return e.classList.add(`allow-interactivity-${i}`)}),function(){document.body.classList.remove(`block-interactivity-${i}`),t.forEach(function(e){return e.classList.remove(`allow-interactivity-${i}`)})}}},[e.inert,e.lockRef.current,e.shards]);var s=C.useCallback(function(e,t){if(`touches`in e&&e.touches.length===2||e.type===`wheel`&&e.ctrlKey)return!o.current.allowPinchZoom;var i=Cr(e),a=n.current,s=`deltaX`in e?e.deltaX:a[0]-i[0],c=`deltaY`in e?e.deltaY:a[1]-i[1],l,u=e.target,d=Math.abs(s)>Math.abs(c)?`h`:`v`;if(`touches`in e&&d===`h`&&u.type===`range`)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=gr(d,u);if(!m)return!0;if(m?l=d:(l=d===`v`?`h`:`v`,m=gr(d,u)),!m)return!1;if(!r.current&&`changedTouches`in e&&(s||c)&&(r.current=l),!l)return!0;var h=r.current||l;return Sr(h,t,e,h===`h`?s:c,!0)},[]),c=C.useCallback(function(e){var n=e;if(!(!kr.length||kr[kr.length-1]!==a)){var r=`deltaY`in n?wr(n):Cr(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&Er(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map(Tr).filter(Boolean).filter(function(e){return e.contains(n.target)});(c.length>0?s(n,c[0]):!o.current.noIsolation)&&n.cancelable&&n.preventDefault()}}},[]),l=C.useCallback(function(e,n,r,i){var a={name:e,delta:n,target:r,should:i,shadowParent:jr(r)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),u=C.useCallback(function(e){n.current=Cr(e),r.current=void 0},[]),d=C.useCallback(function(t){l(t.type,wr(t),t.target,s(t,e.lockRef.current))},[]),f=C.useCallback(function(t){l(t.type,Cr(t),t.target,s(t,e.lockRef.current))},[]);C.useEffect(function(){return kr.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(`wheel`,c,dr),document.addEventListener(`touchmove`,c,dr),document.addEventListener(`touchstart`,u,dr),function(){kr=kr.filter(function(e){return e!==a}),document.removeEventListener(`wheel`,c,dr),document.removeEventListener(`touchmove`,c,dr),document.removeEventListener(`touchstart`,u,dr)}},[]);var p=e.removeScrollBar,m=e.inert;return C.createElement(C.Fragment,null,m?C.createElement(a,{styles:Dr(i)}):null,p?C.createElement(cr,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function jr(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var Mr=Vn(Hn,Ar),Nr=C.forwardRef(function(e,t){return C.createElement(Wn,Tn({},e,{ref:t,sideCar:Mr}))});Nr.classNames=Wn.classNames;var Pr=function(e){return typeof document>`u`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},Fr=new WeakMap,Ir=new WeakMap,Lr={},Rr=0,zr=function(e){return e&&(e.host||zr(e.parentNode))},Br=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=zr(t);return n&&e.contains(n)?n:(console.error(`aria-hidden`,t,`in not contained inside`,e,`. Doing nothing`),null)}).filter(function(e){return!!e})},Vr=function(e,t,n,r){var i=Br(t,Array.isArray(e)?e:[e]);Lr[n]||(Lr[n]=new WeakMap);var a=Lr[n],o=[],s=new Set,c=new Set(i),l=function(e){!e||s.has(e)||(s.add(e),l(e.parentNode))};i.forEach(l);var u=function(e){!e||c.has(e)||Array.prototype.forEach.call(e.children,function(e){if(s.has(e))u(e);else try{var t=e.getAttribute(r),i=t!==null&&t!==`false`,c=(Fr.get(e)||0)+1,l=(a.get(e)||0)+1;Fr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&Ir.set(e,!0),l===1&&e.setAttribute(n,`true`),i||e.setAttribute(r,`true`)}catch(t){console.error(`aria-hidden: cannot operate on `,e,t)}})};return u(t),s.clear(),Rr++,function(){o.forEach(function(e){var t=Fr.get(e)-1,i=a.get(e)-1;Fr.set(e,t),a.set(e,i),t||(Ir.has(e)||e.removeAttribute(r),Ir.delete(e)),i||e.removeAttribute(n)}),Rr--,Rr||(Fr=new WeakMap,Fr=new WeakMap,Ir=new WeakMap,Lr={})}},Hr=function(e,t,n){n===void 0&&(n=`data-aria-hidden`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||Pr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(`[aria-live], script`))),Vr(r,i,n,`aria-hidden`)):function(){return null}},Ur=Object.defineProperty,Wr=(e,t)=>Ur(e,`name`,{value:t,configurable:!0}),Gr=`Dialog`,[Kr,qr]=Be(Gr),[Jr,Yr]=Kr(Gr),Xr=Wr(e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:a,modal:o=!0}=e,s=C.useRef(null),c=C.useRef(null),[l,u]=nt({prop:r,defaultProp:i??!1,onChange:a,caller:Gr}),[d,f]=C.useState(0),[p,m]=C.useState(0);return(0,A.jsx)(Jr,{scope:t,triggerRef:s,contentRef:c,contentId:qe(),titleId:qe(),descriptionId:qe(),titlePresent:d>0,descriptionPresent:p>0,setTitleCount:f,setDescriptionCount:m,open:l,onOpenChange:u,onOpenToggle:C.useCallback(()=>u(e=>!e),[u]),modal:o,children:n})},`Dialog`),Zr=`DialogPortal`,[Qr,$r]=Kr(Zr,{forceMount:void 0}),ei=Wr(e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=Yr(Zr,t);return(0,A.jsx)(Qr,{scope:t,forceMount:n,children:C.Children.map(r,e=>(0,A.jsx)(fn,{present:n||a.open,children:(0,A.jsx)(cn,{asChild:!0,container:i,children:e})}))})},`DialogPortal`),ti=`DialogOverlay`,ni=C.forwardRef(Wr(function(e,t){let n=$r(ti,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Yr(ti,e.__scopeDialog);return a.modal?(0,A.jsx)(fn,{present:r||a.open,children:(0,A.jsx)(ii,{...i,ref:t})}):null},`DialogOverlay`)),ri=ut(`DialogOverlay.RemoveScroll`),ii=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(ti,n),a=Fe(t,Lt());return(0,A.jsx)(Nr,{as:ri,allowPinchZoom:!0,shards:[i.contentRef],children:(0,A.jsx)(Tt.div,{"data-state":gi(i.open),...r,ref:a,style:{pointerEvents:`auto`,...r.style}})})},`DialogOverlayImpl`)),ai=`DialogContent`,oi=C.forwardRef(Wr(function(e,t){let n=$r(ai,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Yr(ai,e.__scopeDialog);return(0,A.jsx)(fn,{present:r||a.open,children:a.modal?(0,A.jsx)(si,{...i,ref:t}):(0,A.jsx)(ci,{...i,ref:t})})},`DialogContent`)),si=C.forwardRef(Wr(function(e,t){let n=Yr(ai,e.__scopeDialog),r=C.useRef(null),i=Fe(t,n.contentRef,r);return C.useEffect(()=>{let e=r.current;if(e)return Hr(e)},[]),(0,A.jsx)(li,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:Ee(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:Ee(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:Ee(e.onFocusOutside,e=>e.preventDefault())})},`DialogContentModal`)),ci=C.forwardRef(Wr(function(e,t){let n=Yr(ai,e.__scopeDialog),r=C.useRef(!1),i=C.useRef(!1);return(0,A.jsx)(li,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})},`DialogContentNonModal`)),li=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=Yr(ai,n);return Cn(),(0,A.jsx)(A.Fragment,{children:(0,A.jsx)(Jt,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,A.jsx)(It,{role:`dialog`,id:s.contentId,"aria-describedby":s.descriptionPresent?s.descriptionId:void 0,"aria-labelledby":s.titlePresent?s.titleId:void 0,"data-state":gi(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})},`DialogContentImpl`)),ui=`DialogTitle`,di=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(ui,n),{setTitleCount:a}=i;return He(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(Tt.h2,{id:i.titleId,...r,ref:t})},`DialogTitle`)),fi=`DialogDescription`,pi=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(fi,n),{setDescriptionCount:a}=i;return He(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(Tt.p,{id:i.descriptionId,...r,ref:t})},`DialogDescription`)),mi=`DialogClose`,hi=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(mi,n);return(0,A.jsx)(Tt.button,{type:`button`,...r,ref:t,onClick:Ee(e.onClick,()=>i.onOpenChange(!1))})},`DialogClose`));function gi(e){return e?`open`:`closed`}Wr(gi,`getState`);function _i(...e){return e.filter(Boolean).join(` `)}function vi({className:e,...t}){return(0,A.jsx)(`button`,{className:_i(`ui-button`,e),...t})}function yi({className:e,...t}){return(0,A.jsx)(`span`,{className:_i(`ui-badge`,e),...t})}function bi({open:e,onOpenChange:t,title:n,description:r,children:i}){return(0,A.jsx)(Xr,{open:e,onOpenChange:t,children:(0,A.jsxs)(ei,{children:[(0,A.jsx)(ni,{className:`sheet-overlay`}),(0,A.jsxs)(oi,{className:`sheet-content`,children:[(0,A.jsxs)(`div`,{className:`sheet-head`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(di,{children:n}),r?(0,A.jsx)(pi,{children:r}):null]}),(0,A.jsx)(hi,{asChild:!0,children:(0,A.jsx)(vi,{"aria-label":`关闭面板`,className:`icon-button`,children:(0,A.jsx)(me,{size:18})})})]}),i]})]})})}function xi({title:e,detail:t}){return(0,A.jsxs)(`div`,{className:`empty-state`,children:[(0,A.jsx)(`strong`,{children:e}),t?(0,A.jsx)(`p`,{children:t}):null]})}function Si(e){return e===`builtin`?`内置 Pi`:`用户安装的 Pi`}var Ci={overview:`概览`,conversation:`对话`,configuration:`配置`,reminders:`提醒`,workspace:`工作区`,logs:`日志`},wi=window.__LARKIN_DASHBOARD__||{packageVersion:`dev`,dashboardVersion:`dev`,buildFingerprint:``,csrfCapability:``},Ti=class extends Error{body;constructor(e,t){super(e),this.body=t}};async function Ei(e,t={}){let n=await fetch(e,{cache:`no-store`,...t});if(!n.ok){let e=await n.json().catch(()=>({}));throw new Ti(e.error||`HTTP ${n.status}`,e)}return n.json()}function Di(e,t){if(!(e instanceof Ti)||!e.body.readiness)return e instanceof Error?e.message:t;let n=e.body.readiness;return[n.reason||e.message,n.nextAction?`下一步:${n.nextAction}`:null].filter(Boolean).join(`;`)}async function Oi(e){return Ei(`/api/config`,{method:`PATCH`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":wi.csrfCapability},body:JSON.stringify(e)})}function ki(e,t={}){return Ei(e,{...t,headers:{...t.headers,"X-Larkin-CSRF":wi.csrfCapability}})}function Ai(e){return e==null||!Number.isFinite(e)?`暂无`:e<60?`${e}s 前`:e<3600?`${Math.floor(e/60)}m 前`:e<86400?`${Math.floor(e/3600)}h 前`:`${Math.floor(e/86400)}d 前`}function ji(e){let t=new Date(String(e||``));return Number.isFinite(t.getTime())?t.toLocaleString(`zh-CN`,{hour12:!1}):`未知时间`}function Mi(e){return Number.isFinite(Number(e))?Number(e).toLocaleString(`en-US`):`—`}function Ni(e){return e.issue?`异常`:e.lastActivity?.state?String(e.lastActivity.state):e.running?`空闲`:`离线`}function Pi(){let[e,t]=(0,C.useState)(null),[n,r]=(0,C.useState)(null),[i,a]=(0,C.useState)(!0),[o,s]=(0,C.useState)(!1),c=(0,C.useRef)(Se()),l=(0,C.useRef)(null),u=(0,C.useRef)(null),d=(0,C.useCallback)(async()=>{u.current?.abort();let e=new AbortController;u.current=e;let n=c.current.issue();try{let i=await Ei(`/api/status`,{signal:e.signal});if(!c.current.accepts(n))return;if(wi.buildFingerprint&&i.buildFingerprint&&i.buildFingerprint!==wi.buildFingerprint){window.location.reload();return}t(i),r(null),s(!1)}catch(t){if(e.signal.aborted||!c.current.accepts(n))return;r(t instanceof Error?t.message:String(t)),s(!0)}finally{c.current.accepts(n)&&a(!1)}},[]);return(0,C.useEffect)(()=>{let e=!0,t=async()=>{await d(),e&&(l.current=window.setTimeout(t,3e3))};return t(),()=>{e=!1,l.current!==null&&window.clearTimeout(l.current),u.current?.abort()}},[d]),{data:e,error:n,loading:i,stale:o,retry:d}}function Fi({agent:e}){let t=(e.displayName||e.name||e.agentId).slice(0,2).toUpperCase(),[n,r]=(0,C.useState)(!1);(0,C.useEffect)(()=>r(!1),[e.agentId,e.bot?.hasAvatar]);let i=e.bot?.hasAvatar&&!n;return(0,A.jsx)(`span`,{className:`agent-avatar`,"aria-hidden":`true`,children:i?(0,A.jsx)(`img`,{src:`/api/avatar/${encodeURIComponent(e.agentId)}`,alt:``,onError:()=>r(!0)}):t})}function Ii({agents:e,selectedId:t,onSelect:n,compact:r=!1}){let[i,a]=(0,C.useState)(``),o=(0,C.useMemo)(()=>be(e,i),[e,i]),s=(0,C.useRef)([]),c=(e,t)=>{if(!o.length)return;let n=t===0?e:(e+t+o.length)%o.length;s.current[n]?.focus()};return(0,A.jsxs)(`div`,{className:_i(`sidebar-inner`,r&&`compact-sidebar`),children:[(0,A.jsxs)(`label`,{className:`agent-search`,children:[(0,A.jsx)(de,{size:15,"aria-hidden":`true`}),(0,A.jsx)(`span`,{className:`sr-only`,children:`搜索 Agent`}),(0,A.jsx)(`input`,{value:i,onChange:e=>a(e.target.value),placeholder:`搜索 Agent…`})]}),(0,A.jsxs)(`div`,{className:`agent-list`,role:`listbox`,"aria-label":`Agent 列表`,children:[o.map((e,r)=>(0,A.jsxs)(`button`,{ref:e=>{s.current[r]=e},type:`button`,role:`option`,"aria-selected":e.agentId===t,className:_i(`agent-list-item`,e.agentId===t&&`selected`),onClick:()=>n(e.agentId),onKeyDown:e=>{e.key===`ArrowDown`&&(e.preventDefault(),c(r,1)),e.key===`ArrowUp`&&(e.preventDefault(),c(r,-1)),e.key===`Home`&&(e.preventDefault(),c(0,0)),e.key===`End`&&(e.preventDefault(),c(o.length-1,0))},children:[(0,A.jsx)(Fi,{agent:e}),(0,A.jsxs)(`span`,{className:`agent-list-copy`,children:[(0,A.jsx)(`strong`,{children:e.displayName||e.name}),(0,A.jsxs)(`small`,{children:[e.runtime,` · `,e.model,e.runtimeReadiness?` · ${e.runtimeReadiness.state}`:``]}),(0,A.jsxs)(`span`,{className:`status-line`,children:[(0,A.jsx)(ie,{size:10}),` `,e.connection.state,` · `,Ni(e)]})]}),e.issue?(0,A.jsx)(k,{size:15,className:`danger`,"aria-label":`异常`}):(0,A.jsx)(re,{size:15,"aria-hidden":`true`})]},e.agentId)),o.length?null:(0,A.jsx)(xi,{title:`没有匹配的 Agent`,detail:`可按名称或 App ID 搜索`})]})]})}function Li({open:e,onOpenChange:t,onSaved:n}){let[r,i]=(0,C.useState)(`require`),[a,o]=(0,C.useState)(`require`),[s,c]=(0,C.useState)(!1),[l,u]=(0,C.useState)(null),d=a!==r;return(0,C.useEffect)(()=>{if(!e)return;let t=new AbortController;return c(!0),ki(`/api/config`,{signal:t.signal}).then(e=>{i(e.mentionPolicy),o(e.mentionPolicy),u(null)}).catch(e=>{t.signal.aborted||u(e.message)}).finally(()=>{t.signal.aborted||c(!1)}),()=>t.abort()},[e]),(0,A.jsx)(bi,{open:e,onOpenChange:e=>{!e&&d&&!window.confirm(`全局设置尚未保存,确定放弃草稿吗?`)||t(e)},title:`全局设置`,description:`只管理所有 Agent 共同继承的默认值;高级操作仍使用 CLI。`,children:(0,A.jsxs)(`div`,{className:`settings-form`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`真人群消息默认策略`}),(0,A.jsxs)(`select`,{value:a,onChange:e=>o(e.target.value),disabled:s,children:[(0,A.jsx)(`option`,{value:`require`,children:`require · 群内需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`free · 群内无需 @`})]})]}),(0,A.jsx)(`p`,{className:`field-help`,children:`未单独设置的 Agent 和群会使用这个值。机器人消息仍必须精确 @ 当前 Agent。`}),(0,A.jsxs)(`div`,{className:`form-actions`,children:[(0,A.jsx)(vi,{onClick:()=>o(r),disabled:!d||s,children:`放弃草稿`}),(0,A.jsx)(vi,{className:`primary`,onClick:async()=>{c(!0);try{let e=await Oi({operation:`set-global-mention`,value:a});i(a),u(`已保存 · ${e.applyState} · ${e.revision.slice(0,20)}…`),n()}catch(e){u(e instanceof Error?e.message:String(e))}finally{c(!1)}},disabled:!d||s,children:s?`保存中…`:`保存全局设置`})]}),l?(0,A.jsx)(`p`,{role:`status`,className:`feedback`,children:l}):null]})})}function Ri({config:e,onSave:t,disabled:n}){let[r,i]=(0,C.useState)(``),[a,o]=(0,C.useState)(``),[s,c]=(0,C.useState)(`require`),l=e.knownChats.filter(e=>e.kind===`group`&&e.override!==`inherit`).filter(e=>{let t=r.trim().toLocaleLowerCase();return!t||e.chatId.toLocaleLowerCase().includes(t)||String(e.displayName||`未解析群名`).toLocaleLowerCase().includes(t)});return(0,A.jsxs)(`section`,{className:`group-policy-section`,"aria-labelledby":`group-policy-title`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{id:`group-policy-title`,children:`群策略`}),(0,A.jsx)(`p`,{children:`这里只显示为当前 Agent 单独设置过消息策略的群。`})]}),(0,A.jsxs)(`label`,{className:`table-search`,children:[(0,A.jsx)(de,{size:14}),(0,A.jsx)(`span`,{className:`sr-only`,children:`搜索群`}),(0,A.jsx)(`input`,{value:r,onChange:e=>i(e.target.value),placeholder:`搜索群名或 chat_id`})]})]}),(0,A.jsxs)(`div`,{className:`policy-table-wrap`,children:[(0,A.jsxs)(`table`,{className:`policy-table`,children:[(0,A.jsx)(`thead`,{children:(0,A.jsxs)(`tr`,{children:[(0,A.jsx)(`th`,{children:`群`}),(0,A.jsx)(`th`,{children:`消息策略`}),(0,A.jsx)(`th`,{children:`操作`})]})}),(0,A.jsx)(`tbody`,{children:l.map(e=>(0,A.jsxs)(`tr`,{children:[(0,A.jsxs)(`td`,{children:[(0,A.jsx)(`strong`,{children:e.displayName||e.chatId}),e.displayName?(0,A.jsx)(`code`,{children:e.chatId}):(0,A.jsx)(`small`,{children:`群名暂时不可用`})]}),(0,A.jsx)(`td`,{children:(0,A.jsxs)(`select`,{"aria-label":`${e.displayName||e.chatId} 消息策略`,value:e.override,disabled:n,onChange:n=>void t(e.chatId,n.target.value),children:[(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})}),(0,A.jsx)(`td`,{children:(0,A.jsx)(vi,{disabled:n,onClick:()=>void t(e.chatId,`inherit`),children:`移除特别配置`})})]},e.chatId))})]}),l.length?null:(0,A.jsx)(xi,{title:r?`没有匹配的群`:`还没有单独配置的群`,detail:`新增后,Larkin 会自动解析并缓存群名。`})]}),(0,A.jsxs)(`div`,{className:`chat-add`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`完整 chat_id`}),(0,A.jsx)(`input`,{value:a,onChange:e=>o(e.target.value),placeholder:`oc_QAConfigChat1`})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`消息策略`}),(0,A.jsxs)(`select`,{value:s,onChange:e=>c(e.target.value),children:[(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})]}),(0,A.jsx)(vi,{disabled:n||!/^oc_[A-Za-z0-9_-]+$/.test(a),onClick:()=>void t(a,s).then(()=>o(``)),children:`保存群策略`})]})]})}function zi({agentId:e,onDirtyChange:t,refreshKey:n}){let[r,i]=(0,C.useState)(null),[a,o]=(0,C.useState)({}),[s,c]=(0,C.useState)({}),[l,u]=(0,C.useState)(!0),[d,f]=(0,C.useState)(null),[p,m]=(0,C.useState)(null),h=(0,C.useRef)(Se()),g=(0,C.useRef)(Se()),_=!xe(a,s);(0,C.useEffect)(()=>t(_),[_,t]),(0,C.useEffect)(()=>{let e=e=>{_&&e.preventDefault()};return window.addEventListener(`beforeunload`,e),()=>window.removeEventListener(`beforeunload`,e)},[_]);let v=(0,C.useCallback)(async(t=!1)=>{let n=h.current.issue();u(!0);try{let r=await ki(`/api/config?agent=${encodeURIComponent(e)}`);if(!h.current.accepts(n))return;let a=r.agents[0];if(!a)throw Error(`配置投影中没有当前 Agent`);let s={runtime:a.runtime,model:a.model,effort:a.effort||`default`,mention:a.mention.override};i(r),o(s),(!t||!_)&&c(s)}catch(e){h.current.accepts(n)&&f(e instanceof Error?e.message:String(e))}finally{h.current.accepts(n)&&u(!1)}},[e,_]);(0,C.useEffect)(()=>{v(!1)},[e]),(0,C.useEffect)(()=>{n>0&&v(!0)},[n]);let y=r?.agents[0],b=(e,t)=>c(n=>({...n,[e]:t})),x=String(s.runtime||y?.runtime||``),S=String(s.model||y?.model||`default`);(0,C.useEffect)(()=>{if(m({runtime:x,status:`loading`,models:[]}),!new Set([`pi`,`codex`,`claude`]).has(x)){m({runtime:x,status:`error`,models:[]});return}let t=g.current.issue(),n=new AbortController;return ki(`/api/models/${x}?agent=${encodeURIComponent(e)}`,{signal:n.signal}).then(e=>{g.current.accepts(t)&&m({runtime:x,status:`ready`,models:e.models})}).catch(e=>{!n.signal.aborted&&g.current.accepts(t)&&(m({runtime:x,status:`error`,models:[]}),f(`${x} 模型列表暂时不可用:${e instanceof Error?e.message:String(e)}`))}),()=>n.abort()},[e,x]);let w=p?.runtime===x&&p.status===`ready`,ee=p?.runtime===x?p.status:`loading`,te=w?p.models:[],T=`${S} · 当前配置(模型目录${ee===`error`?`不可用`:`加载中`})`,E=w?te.some(e=>e.id===S)?te:[{id:S,label:`${S} · 当前配置`},...te]:[{id:S,label:T}],ne=w&&S!=="default"&&te.find(e=>e.id===S)?.supportedReasoningEfforts||[],re=s.runtime!==a.runtime||s.model!==a.model||s.effort!==a.effort,ie=()=>Ei(`/api/config/apply`,{method:`POST`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":wi.csrfCapability},body:JSON.stringify({agentId:e})}),ae=async()=>{if(y){u(!0);try{let t=[];s.runtime===a.runtime?s.model!==a.model&&t.push({operation:`set-agent-model`,agentId:e,model:s.model}):t.push({operation:`set-agent-runtime`,agentId:e,runtime:s.runtime,model:s.model}),s.effort!==a.effort&&t.push({operation:`set-agent-effort`,agentId:e,effort:s.effort}),s.mention!==a.mention&&t.push({operation:`set-agent-mention`,agentId:e,value:s.mention});let n=null;for(let e of t)n=await Oi(e);if(re)try{await ie(),f(`配置已保存并应用`)}catch(e){f(`配置已保存但未应用:${Di(e,`稍后重试`)}`)}else f(n?`配置已保存`:`没有需要保存的修改`);await v(!1)}catch(e){f(e instanceof Error?e.message:String(e))}finally{u(!1)}}},oe=async()=>{u(!0);try{await ie(),f(`已应用保存的运行配置`),await v(!0)}catch(e){f(`配置保持已保存但未应用:${Di(e,`稍后重试`)}`)}finally{u(!1)}},se=async(t,n)=>{u(!0);try{let r=await Oi({operation:`set-chat-mention`,agentId:e,chatId:t,value:n});f(`群策略已保存 · ${r.applyState}`),await v(!0)}catch(e){f(e instanceof Error?e.message:String(e))}finally{u(!1)}};if(!y&&l)return(0,A.jsx)(xi,{title:`加载 Agent 配置…`});if(!y)return(0,A.jsx)(xi,{title:`配置不可用`,detail:d||`请重试`});let ce=y.apply.applyState===`pending`?`待应用`:y.apply.applyState===`applied`?`已应用`:`状态未知`;return(0,A.jsxs)(`div`,{className:`configuration-page`,children:[(0,A.jsxs)(`section`,{className:`config-section`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`Agent 配置`}),(0,A.jsxs)(`p`,{children:[`只作用于 `,e,`;运行配置会在安全时机自动应用,Agent 正忙时会保留待处理。`]})]}),(0,A.jsx)(yi,{className:y.apply.applyState===`pending`?`warning`:`success`,children:ce})]}),(0,A.jsxs)(`p`,{className:`cli-guidance`,children:[`这里只提供常用微调。更多配置可以直接告诉当前 Agent,让它运行 `,(0,A.jsx)(`code`,{children:`larkin config --help`}),` 后完成。`]}),(0,A.jsxs)(`div`,{className:`config-grid`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Runtime`}),(0,A.jsx)(`select`,{value:x,disabled:l,onChange:e=>{b(`runtime`,e.target.value),b(`model`,`default`),b(`effort`,`default`)},children:Object.keys(r?.runtimeModels||{}).map(e=>(0,A.jsx)(`option`,{value:e,children:e},e))})]}),x===`pi`?(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Pi 发行版`}),(0,A.jsx)(`input`,{readOnly:!0,disabled:!0,"aria-label":`Pi 发行版`,value:Si(y.piDistribution)})]}):null,(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Model`}),(0,A.jsx)(`select`,{value:S,disabled:l||!w,onChange:e=>{b(`model`,e.target.value),e.target.value==="default"&&b(`effort`,`default`)},children:E.map(e=>(0,A.jsx)(`option`,{value:e.id,children:e.label||e.id},e.id))})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Effort`}),(0,A.jsxs)(`select`,{value:String(s.effort||`default`),disabled:l||!w||S==="default"||!ne.length,onChange:e=>b(`effort`,e.target.value),children:[(0,A.jsx)(`option`,{value:`default`,children:`default · 不指定`}),ne.map(e=>(0,A.jsx)(`option`,{value:e,children:e},e))]})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`群消息策略`}),(0,A.jsxs)(`select`,{value:String(s.mention||`inherit`),disabled:l,onChange:e=>b(`mention`,e.target.value),children:[(0,A.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})]})]}),_||y.apply.applyState===`pending`?(0,A.jsxs)(`div`,{className:`form-actions`,children:[_?(0,A.jsx)(vi,{disabled:l,onClick:()=>c(a),children:`放弃草稿`}):null,_?(0,A.jsx)(vi,{className:`primary`,disabled:l,onClick:ae,children:l?`处理中…`:re?`保存并应用`:`保存 Agent 配置`}):(0,A.jsxs)(vi,{className:`primary`,disabled:l,onClick:oe,children:[(0,A.jsx)(ue,{size:14}),` 应用已保存配置`]})]}):null,d?(0,A.jsx)(`p`,{role:`status`,className:`feedback`,children:d}):null]}),(0,A.jsx)(Ri,{config:y,onSave:se,disabled:l})]})}function Bi({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{};return(0,A.jsxs)(`div`,{className:`overview-page`,children:[(0,A.jsxs)(`div`,{className:`metrics-band`,"aria-label":`Agent 运行指标`,children:[(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ie,{})}),(0,A.jsx)(`span`,{children:`连接`}),(0,A.jsx)(`strong`,{children:e.connection.state}),(0,A.jsx)(`small`,{children:e.connection.reason})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(E,{})}),(0,A.jsx)(`span`,{children:`执行状态`}),(0,A.jsx)(`strong`,{children:Ni(e)}),(0,A.jsx)(`small`,{children:e.lastActivity?Ai(e.lastActivity.ageSec):`尚无活动`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(le,{})}),(0,A.jsx)(`span`,{children:`最近消息`}),(0,A.jsx)(`strong`,{children:e.lastDeliver?.from||`暂无`}),(0,A.jsx)(`small`,{children:e.lastDeliver?`${e.lastDeliver.target||``} · ${Ai(e.lastDeliver.ageSec)}`:`尚无消息记录`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ce,{})}),(0,A.jsx)(`span`,{children:`上下文`}),(0,A.jsx)(`strong`,{children:t.contextPercent==null?`${e.session?.turns||0} turns`:`${Math.round(Number(t.contextPercent))}%`}),(0,A.jsx)(`small`,{children:e.session?`session ${e.session.id.slice(0,12)}…`:`尚未建立 session`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ce,{})}),(0,A.jsx)(`span`,{children:`累计 Token`}),(0,A.jsx)(`strong`,{children:Mi(t.cumulativeTokens)}),(0,A.jsx)(`small`,{children:t.available?`完整 session 累计`:t.reason||`暂无用量`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(E,{})}),(0,A.jsx)(`span`,{children:`最近一轮用量`}),(0,A.jsx)(`strong`,{children:Mi(t.latestTokens)}),(0,A.jsx)(`small`,{children:`输入、缓存与输出合计`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ue,{})}),(0,A.jsx)(`span`,{children:`Compaction`}),(0,A.jsx)(`strong`,{children:Mi(n.count)}),(0,A.jsx)(`small`,{children:n.active?`正在压缩上下文`:n.lastFinishedAt?`最近 ${ji(n.lastFinishedAt)}`:`尚无压缩记录`})]})]}),(0,A.jsxs)(`div`,{className:`overview-sections`,children:[(0,A.jsxs)(`section`,{className:`content-section wide`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`最近动态`}),(0,A.jsx)(`p`,{children:`当前 Agent 的消息、工具和状态转换。`})]}),(0,A.jsx)(yi,{children:e.feed.length})]}),(0,A.jsx)(M,{items:e.feed.slice(0,10)})]}),(0,A.jsxs)(`section`,{className:`content-section`,children:[(0,A.jsx)(`h3`,{children:`运行摘要`}),(0,A.jsxs)(`dl`,{className:`facts`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Runtime`}),(0,A.jsx)(`dd`,{children:e.runtime})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Readiness`}),(0,A.jsx)(`dd`,{children:e.runtimeReadiness?`${e.runtimeReadiness.state}${e.runtimeReadiness.version?` · ${e.runtimeReadiness.version}`:``}${e.runtimeReadiness.executable?` · ${e.runtimeReadiness.executable}`:``}${e.runtimeReadiness.reason?` · ${e.runtimeReadiness.reason}`:``}${e.runtimeReadiness.nextAction?` · ${e.runtimeReadiness.nextAction}`:``}`:`unknown`})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Model`}),(0,A.jsx)(`dd`,{children:e.model})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Effort`}),(0,A.jsx)(`dd`,{children:e.effort||`default`})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`入站`}),(0,A.jsx)(`dd`,{children:e.inbound.state})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`提醒`}),(0,A.jsx)(`dd`,{children:e.activeReminders})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`指示灯`}),(0,A.jsx)(`dd`,{children:e.eyeIndicator.stuck?`疑似卡住`:`${e.eyeIndicator.pendingCount} pending`})]})]})]})]})]})}function M({items:e}){if(!e.length)return(0,A.jsx)(xi,{title:`还没有活动记录`});let t=e=>e.kind===`error`||e.state===`error`?`state-error`:e.kind===`deliver`?`state-deliver`:[`working`,`thinking`,`tool`].includes(String(e.state))?`state-active`:[`idle`,`online`].includes(String(e.state))?`state-idle`:`state-unknown`;return(0,A.jsx)(`ol`,{className:`timeline`,children:e.map((e,n)=>(0,A.jsxs)(`li`,{children:[(0,A.jsx)(`span`,{className:_i(`timeline-dot`,t(e))}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`strong`,{children:e.kind===`deliver`?`收到消息`:e.kind===`error`?`错误`:e.state||`活动`}),(0,A.jsx)(`p`,{children:e.text||e.detail||[e.from,e.target].filter(Boolean).join(` → `)||e.tool||`状态已更新`}),(0,A.jsx)(`time`,{children:ji(e.at)})]})]},`${e.at}-${n}`))})}function N({agent:e}){return e.conversation.length?(0,A.jsx)(`div`,{className:`conversation-list`,children:e.conversation.map((t,n)=>{let r=t.direction===`out`?`已发送`:t.wake?`入站 · 已唤醒`:`旁听 · 未唤醒`;return(0,A.jsxs)(`article`,{className:_i(`conversation-item`,t.direction===`out`?`outbound`:`inbound`,t.direction===`in`&&!t.wake&&`listening`),children:[(0,A.jsxs)(`header`,{children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(yi,{className:t.direction===`in`&&!t.wake?`warning`:t.direction===`out`?`accent`:`success`,children:r}),(0,A.jsx)(`strong`,{children:t.from||(t.direction===`out`?e.displayName:`未知发送者`)})]}),(0,A.jsx)(`time`,{children:ji(t.at)})]}),(0,A.jsx)(`p`,{children:t.text||`[非文本消息]`}),t.target?(0,A.jsx)(`small`,{children:t.target}):null]},`${t.at}-${n}`)})}):(0,A.jsx)(xi,{title:`还没有对话摘录`,detail:`Host 更新本地 conversation projection 后会显示在这里。`})}function Vi({agent:e}){if(!e.remindersList.length)return(0,A.jsx)(xi,{title:`还没有提醒`});let t={scheduled:`待执行`,pending:`待执行`,fired:`已完成`,canceled:`已取消`,cancelled:`已取消`,failed:`失败`};return(0,A.jsx)(`div`,{className:`reminder-list`,children:e.remindersList.map((e,n)=>{let r=e.status||`scheduled`,i=[`fired`,`canceled`,`cancelled`].includes(r);return(0,A.jsxs)(`article`,{className:_i(`status-${r===`cancelled`?`canceled`:r}`,i&&`done`),children:[(0,A.jsx)(ne,{size:16}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`strong`,{children:e.title||`未命名提醒`}),(0,A.jsxs)(`p`,{children:[ji(e.fireAt),e.repeat?` · 重复`:``]})]}),(0,A.jsx)(yi,{className:[`scheduled`,`pending`].includes(r)?`warning`:r===`fired`?`success`:r===`failed`?`danger-badge`:``,children:t[r]||r})]},`${e.fireAt}-${n}`)})})}function Hi({agentId:e}){let[t,n]=(0,C.useState)(null),[r,i]=(0,C.useState)(null),[a,o]=(0,C.useState)(!0),[s,c]=(0,C.useState)(null),l=(0,C.useRef)(Se()),u=(0,C.useCallback)(async t=>{let r=l.current.issue();o(!0);try{let a=await Ei(`/api/workspace?agent=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`);if(!l.current.accepts(r))return;a.kind===`directory`?(n(a),i(null)):i(a),c(null)}catch(e){l.current.accepts(r)&&c(e instanceof Error?e.message:String(e))}finally{l.current.accepts(r)&&o(!1)}},[e]);(0,C.useEffect)(()=>{u(``)},[u]);let d=t?.path.split(`/`).filter(Boolean)||[];return(0,A.jsxs)(`div`,{className:`workspace-browser`,children:[(0,A.jsxs)(`div`,{className:`workspace-side`,children:[(0,A.jsxs)(`nav`,{className:`breadcrumbs`,"aria-label":`工作区路径`,children:[(0,A.jsx)(`button`,{onClick:()=>void u(``),children:`workspace`}),d.map((e,t)=>(0,A.jsxs)(`span`,{children:[(0,A.jsx)(re,{size:12}),(0,A.jsx)(`button`,{onClick:()=>void u(d.slice(0,t+1).join(`/`)),children:e})]},`${e}-${t}`))]}),(0,A.jsxs)(`div`,{className:`workspace-list`,role:`region`,"aria-label":`工作区目录`,children:[t?.entries.map(e=>(0,A.jsxs)(`button`,{onClick:()=>void u(e.path),children:[e.kind===`directory`?(0,A.jsx)(se,{size:15}):(0,A.jsx)(oe,{size:15}),(0,A.jsx)(`span`,{children:e.name}),(0,A.jsx)(`small`,{children:e.kind===`file`?`${e.size} B`:`目录`})]},e.path)),!a&&t&&!t.entries.length?(0,A.jsx)(xi,{title:`空目录`}):null]})]}),(0,A.jsx)(`div`,{className:`workspace-preview`,role:`region`,"aria-label":`文件预览`,children:a?(0,A.jsx)(xi,{title:`读取工作区…`}):s?(0,A.jsx)(xi,{title:`工作区读取失败`,detail:s}):r?(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`header`,{children:[(0,A.jsx)(`strong`,{children:r.name}),(0,A.jsx)(`small`,{children:r.truncated?`仅预览前 1 MB`:`${r.size} B`})]}),r.binary?(0,A.jsx)(xi,{title:`二进制文件不可预览`}):(0,A.jsx)(`pre`,{children:r.content})]}):(0,A.jsx)(xi,{title:`只读工作区`,detail:`选择文件进行预览`})})]})}function Ui({agent:e}){return(0,A.jsxs)(`section`,{className:`logs-section`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`结构化日志`}),(0,A.jsx)(`p`,{children:`最近 30 条本地状态投影;不暴露原始日志文件或路径。`})]}),(0,A.jsx)(yi,{children:e.feed.length})]}),(0,A.jsx)(M,{items:e.feed}),e.recentErrors.length?(0,A.jsxs)(`section`,{className:`error-stack`,children:[(0,A.jsx)(`h4`,{children:`最近错误`}),e.recentErrors.map((e,t)=>(0,A.jsxs)(`div`,{children:[(0,A.jsx)(ae,{size:14}),(0,A.jsx)(`span`,{children:e.text||e.message||`未知错误`}),(0,A.jsx)(`time`,{children:ji(e.at)})]},`${e.at}-${t}`))]}):null]})}function Wi({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{},r=t.contextPercent==null?`上下文 ${e.session?.turns||0} turns`:`上下文 ${Math.round(Number(t.contextPercent))}% · ${Mi(t.latestTokens)} / ${Mi(t.contextWindow)}`;return(0,A.jsxs)(`header`,{className:`agent-header`,children:[(0,A.jsxs)(`div`,{className:`agent-title`,children:[(0,A.jsx)(Fi,{agent:e}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`p`,{children:e.agentId}),(0,A.jsx)(`h1`,{children:e.displayName||e.name})]})]}),(0,A.jsxs)(`div`,{className:`agent-header-summary`,children:[(0,A.jsx)(yi,{className:e.connection.state===`connected`?`success`:e.issue?`danger-badge`:``,children:e.connection.state}),(0,A.jsx)(`span`,{children:e.runtime}),(0,A.jsx)(`span`,{children:e.model}),(0,A.jsx)(`span`,{children:e.effort||`default effort`}),(0,A.jsx)(`span`,{children:r}),(0,A.jsxs)(`span`,{children:[`累计 `,Mi(t.cumulativeTokens),` tokens`]}),(0,A.jsxs)(`span`,{children:[`最近一轮 `,Mi(t.latestTokens),` tokens`]}),(0,A.jsxs)(`span`,{children:[`Compact `,Mi(n.count),` 次 · `,n.active?`active`:`idle`]}),(0,A.jsxs)(`span`,{children:[`最近消息 `,e.lastDeliver?Ai(e.lastDeliver.ageSec):`暂无`]}),(0,A.jsx)(`span`,{children:e.lastActivity?.detail||Ni(e)})]})]})}function Gi(){let e=Pi(),t=(0,C.useMemo)(()=>_e(window.location.search),[]),[n,r]=(0,C.useState)(t.agentId),[i,a]=(0,C.useState)(t.tab),[o,s]=(0,C.useState)(!1),[c,l]=(0,C.useState)(!1),[u,d]=(0,C.useState)(0),[f,p]=(0,C.useState)(!1),[m,h]=(0,C.useState)(null),g=(0,C.useRef)(new Map),_=e.data?.agents||[],v=e.data?.packageVersion||wi.packageVersion,y=_.find(e=>e.agentId===n)||null,b=(0,C.useCallback)((e,t)=>{let n=`${e}:${t}`,r=window.scrollY;g.current.set(n,r);try{sessionStorage.setItem(`larkin-dashboard-scroll:${n}`,String(r))}catch{}},[]),x=(0,C.useCallback)(e=>{if(g.current.has(e))return g.current.get(e)||0;try{let t=Number(sessionStorage.getItem(`larkin-dashboard-scroll:${e}`));return Number.isFinite(t)&&t>=0?t:0}catch{return 0}},[]),S=(0,C.useCallback)((e,t,o=!1)=>{n&&b(n,i);let s=o?`replaceState`:`pushState`;window.history[s](null,``,ve(e,t)),r(e),a(t)},[b,n,i]),w=()=>!f||window.confirm(`Agent 配置尚未保存,确定放弃草稿吗?`),ee=e=>{w()&&(p(!1),S(e,i),l(!1))},te=e=>{e!==`configuration`&&!w()||(e!==`configuration`&&p(!1),S(n,e))};return(0,C.useEffect)(()=>{if(!e.data)return;let t=ye(n,e.data.agents);t!==n&&(h(n?`原 Agent 已不存在,已切换到第一个可用 Agent。`:null),S(t,i,!0))},[e.data,n,i,S]),(0,C.useEffect)(()=>{let e=()=>{let e=_e(window.location.search);if(f&&!window.confirm(`Agent 配置尚未保存,确定离开吗?`)){window.history.pushState(null,``,ve(n,i));return}n&&b(n,i),p(!1),r(e.agentId),a(e.tab)};return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},[f,b,n,i]),(0,C.useLayoutEffect)(()=>{if(!n)return;let e=`${n}:${i}`,t=x(e);window.scrollY!==t&&window.scrollTo({top:t,behavior:`auto`})},[x,n,i]),(0,A.jsxs)(`div`,{className:_i(`app-shell`,i===`workspace`&&`workspace-app-active`),children:[(0,A.jsxs)(`aside`,{className:`desktop-sidebar`,children:[(0,A.jsxs)(`div`,{className:`brand`,children:[(0,A.jsx)(`img`,{src:`/assets/larkin-mark.svg`,alt:``}),(0,A.jsxs)(`strong`,{children:[`Larkin `,(0,A.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]})]}),(0,A.jsx)(Ii,{agents:_,selectedId:n,onSelect:ee}),(0,A.jsx)(`footer`,{children:e.data?.daemon.running?`${_.length} Agents · daemon online`:`daemon offline`})]}),(0,A.jsxs)(`main`,{className:_i(`workspace-shell`,i===`workspace`&&`workspace-route-active`),children:[(0,A.jsxs)(`div`,{className:`app-topbar`,children:[(0,A.jsx)(vi,{className:`mobile-menu`,"aria-label":`打开 Agent 导航`,onClick:()=>l(!0),children:(0,A.jsx)(O,{size:18})}),(0,A.jsxs)(`div`,{children:[(0,A.jsxs)(`strong`,{children:[`Larkin `,(0,A.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]}),(0,A.jsx)(`span`,{children:e.data?.daemon.running?`运行中`:`未运行`}),e.stale?(0,A.jsx)(yi,{className:`warning`,children:`状态陈旧`}):null]}),(0,A.jsxs)(vi,{onClick:()=>s(!0),children:[(0,A.jsx)(fe,{size:15}),` 全局设置`]})]}),e.error?(0,A.jsxs)(`div`,{className:`stale-banner`,role:`alert`,children:[(0,A.jsx)(k,{size:15}),`状态刷新失败,保留最后一次数据:`,e.error,(0,A.jsx)(vi,{onClick:()=>void e.retry(),children:`重试`})]}):null,m?(0,A.jsxs)(`div`,{className:`notice`,role:`status`,children:[m,(0,A.jsx)(vi,{"aria-label":`关闭提示`,onClick:()=>h(null),children:`×`})]}):null,e.loading&&!e.data?(0,A.jsxs)(`div`,{className:`page-loading`,children:[(0,A.jsx)(ue,{className:`spin`}),`加载 Agent 工作台…`]}):y?(0,A.jsxs)(A.Fragment,{children:[(0,A.jsx)(Wi,{agent:y}),(0,A.jsx)(`nav`,{className:`agent-tabs`,"aria-label":`${y.displayName} 内容页签`,role:`tablist`,children:ge.map(e=>(0,A.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":i===e,className:i===e?`active`:``,onClick:()=>te(e),children:[e===`overview`?(0,A.jsx)(ce,{}):e===`conversation`?(0,A.jsx)(le,{}):e===`configuration`?(0,A.jsx)(pe,{}):e===`reminders`?(0,A.jsx)(ne,{}):e===`workspace`?(0,A.jsx)(se,{}):(0,A.jsx)(D,{}),Ci[e]]},e))}),(0,A.jsxs)(`div`,{className:_i(`agent-content`,i===`workspace`&&`workspace-active`),children:[(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`overview`,children:(0,A.jsx)(Bi,{agent:y})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`conversation`,children:(0,A.jsx)(N,{agent:y})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`configuration`,children:(0,A.jsx)(zi,{agentId:y.agentId,onDirtyChange:p,refreshKey:u})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`reminders`,children:(0,A.jsx)(Vi,{agent:y})}),(0,A.jsx)(`section`,{className:`workspace-tab-panel`,role:`tabpanel`,hidden:i!==`workspace`,children:(0,A.jsx)(Hi,{agentId:y.agentId})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`logs`,children:(0,A.jsx)(Ui,{agent:y})})]},y.agentId)]}):(0,A.jsx)(xi,{title:`还没有配置任何 Agent`,detail:`先运行 larkin setup。`})]}),(0,A.jsx)(Li,{open:o,onOpenChange:s,onSaved:()=>d(e=>e+1)}),(0,A.jsx)(bi,{open:c,onOpenChange:l,title:`选择 Agent`,description:`搜索、查看状态并切换当前工作区。`,children:(0,A.jsx)(Ii,{compact:!0,agents:_,selectedId:n,onSelect:ee})})]})}var Ki=document.getElementById(`root`);if(!Ki)throw Error(`dashboard root is missing`);(0,he.createRoot)(Ki).render((0,A.jsx)(C.StrictMode,{children:(0,A.jsx)(Gi,{})}));
|
|
49
|
+
`},Or=0,kr=[];function Ar(e){var t=C.useRef([]),n=C.useRef([0,0]),r=C.useRef(),i=C.useState(Or++)[0],a=C.useState(Qn)[0],o=C.useRef(e);C.useEffect(function(){o.current=e},[e]),C.useEffect(function(){if(e.inert){document.body.classList.add(`block-interactivity-${i}`);var t=Dn([e.lockRef.current],(e.shards||[]).map(Tr),!0).filter(Boolean);return t.forEach(function(e){return e.classList.add(`allow-interactivity-${i}`)}),function(){document.body.classList.remove(`block-interactivity-${i}`),t.forEach(function(e){return e.classList.remove(`allow-interactivity-${i}`)})}}},[e.inert,e.lockRef.current,e.shards]);var s=C.useCallback(function(e,t){if(`touches`in e&&e.touches.length===2||e.type===`wheel`&&e.ctrlKey)return!o.current.allowPinchZoom;var i=Cr(e),a=n.current,s=`deltaX`in e?e.deltaX:a[0]-i[0],c=`deltaY`in e?e.deltaY:a[1]-i[1],l,u=e.target,d=Math.abs(s)>Math.abs(c)?`h`:`v`;if(`touches`in e&&d===`h`&&u.type===`range`)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=gr(d,u);if(!m)return!0;if(m?l=d:(l=d===`v`?`h`:`v`,m=gr(d,u)),!m)return!1;if(!r.current&&`changedTouches`in e&&(s||c)&&(r.current=l),!l)return!0;var h=r.current||l;return Sr(h,t,e,h===`h`?s:c,!0)},[]),c=C.useCallback(function(e){var n=e;if(!(!kr.length||kr[kr.length-1]!==a)){var r=`deltaY`in n?wr(n):Cr(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&Er(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map(Tr).filter(Boolean).filter(function(e){return e.contains(n.target)});(c.length>0?s(n,c[0]):!o.current.noIsolation)&&n.cancelable&&n.preventDefault()}}},[]),l=C.useCallback(function(e,n,r,i){var a={name:e,delta:n,target:r,should:i,shadowParent:jr(r)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),u=C.useCallback(function(e){n.current=Cr(e),r.current=void 0},[]),d=C.useCallback(function(t){l(t.type,wr(t),t.target,s(t,e.lockRef.current))},[]),f=C.useCallback(function(t){l(t.type,Cr(t),t.target,s(t,e.lockRef.current))},[]);C.useEffect(function(){return kr.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(`wheel`,c,dr),document.addEventListener(`touchmove`,c,dr),document.addEventListener(`touchstart`,u,dr),function(){kr=kr.filter(function(e){return e!==a}),document.removeEventListener(`wheel`,c,dr),document.removeEventListener(`touchmove`,c,dr),document.removeEventListener(`touchstart`,u,dr)}},[]);var p=e.removeScrollBar,m=e.inert;return C.createElement(C.Fragment,null,m?C.createElement(a,{styles:Dr(i)}):null,p?C.createElement(cr,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function jr(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var Mr=Vn(Hn,Ar),Nr=C.forwardRef(function(e,t){return C.createElement(Wn,Tn({},e,{ref:t,sideCar:Mr}))});Nr.classNames=Wn.classNames;var Pr=function(e){return typeof document>`u`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},Fr=new WeakMap,Ir=new WeakMap,Lr={},Rr=0,zr=function(e){return e&&(e.host||zr(e.parentNode))},Br=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=zr(t);return n&&e.contains(n)?n:(console.error(`aria-hidden`,t,`in not contained inside`,e,`. Doing nothing`),null)}).filter(function(e){return!!e})},Vr=function(e,t,n,r){var i=Br(t,Array.isArray(e)?e:[e]);Lr[n]||(Lr[n]=new WeakMap);var a=Lr[n],o=[],s=new Set,c=new Set(i),l=function(e){!e||s.has(e)||(s.add(e),l(e.parentNode))};i.forEach(l);var u=function(e){!e||c.has(e)||Array.prototype.forEach.call(e.children,function(e){if(s.has(e))u(e);else try{var t=e.getAttribute(r),i=t!==null&&t!==`false`,c=(Fr.get(e)||0)+1,l=(a.get(e)||0)+1;Fr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&Ir.set(e,!0),l===1&&e.setAttribute(n,`true`),i||e.setAttribute(r,`true`)}catch(t){console.error(`aria-hidden: cannot operate on `,e,t)}})};return u(t),s.clear(),Rr++,function(){o.forEach(function(e){var t=Fr.get(e)-1,i=a.get(e)-1;Fr.set(e,t),a.set(e,i),t||(Ir.has(e)||e.removeAttribute(r),Ir.delete(e)),i||e.removeAttribute(n)}),Rr--,Rr||(Fr=new WeakMap,Fr=new WeakMap,Ir=new WeakMap,Lr={})}},Hr=function(e,t,n){n===void 0&&(n=`data-aria-hidden`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||Pr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(`[aria-live], script`))),Vr(r,i,n,`aria-hidden`)):function(){return null}},Ur=Object.defineProperty,Wr=(e,t)=>Ur(e,`name`,{value:t,configurable:!0}),Gr=`Dialog`,[Kr,qr]=Be(Gr),[Jr,Yr]=Kr(Gr),Xr=Wr(e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:a,modal:o=!0}=e,s=C.useRef(null),c=C.useRef(null),[l,u]=nt({prop:r,defaultProp:i??!1,onChange:a,caller:Gr}),[d,f]=C.useState(0),[p,m]=C.useState(0);return(0,A.jsx)(Jr,{scope:t,triggerRef:s,contentRef:c,contentId:qe(),titleId:qe(),descriptionId:qe(),titlePresent:d>0,descriptionPresent:p>0,setTitleCount:f,setDescriptionCount:m,open:l,onOpenChange:u,onOpenToggle:C.useCallback(()=>u(e=>!e),[u]),modal:o,children:n})},`Dialog`),Zr=`DialogPortal`,[Qr,$r]=Kr(Zr,{forceMount:void 0}),ei=Wr(e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=Yr(Zr,t);return(0,A.jsx)(Qr,{scope:t,forceMount:n,children:C.Children.map(r,e=>(0,A.jsx)(fn,{present:n||a.open,children:(0,A.jsx)(cn,{asChild:!0,container:i,children:e})}))})},`DialogPortal`),ti=`DialogOverlay`,ni=C.forwardRef(Wr(function(e,t){let n=$r(ti,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Yr(ti,e.__scopeDialog);return a.modal?(0,A.jsx)(fn,{present:r||a.open,children:(0,A.jsx)(ii,{...i,ref:t})}):null},`DialogOverlay`)),ri=ut(`DialogOverlay.RemoveScroll`),ii=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(ti,n),a=Fe(t,Lt());return(0,A.jsx)(Nr,{as:ri,allowPinchZoom:!0,shards:[i.contentRef],children:(0,A.jsx)(Tt.div,{"data-state":gi(i.open),...r,ref:a,style:{pointerEvents:`auto`,...r.style}})})},`DialogOverlayImpl`)),ai=`DialogContent`,oi=C.forwardRef(Wr(function(e,t){let n=$r(ai,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Yr(ai,e.__scopeDialog);return(0,A.jsx)(fn,{present:r||a.open,children:a.modal?(0,A.jsx)(si,{...i,ref:t}):(0,A.jsx)(ci,{...i,ref:t})})},`DialogContent`)),si=C.forwardRef(Wr(function(e,t){let n=Yr(ai,e.__scopeDialog),r=C.useRef(null),i=Fe(t,n.contentRef,r);return C.useEffect(()=>{let e=r.current;if(e)return Hr(e)},[]),(0,A.jsx)(li,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:Ee(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:Ee(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:Ee(e.onFocusOutside,e=>e.preventDefault())})},`DialogContentModal`)),ci=C.forwardRef(Wr(function(e,t){let n=Yr(ai,e.__scopeDialog),r=C.useRef(!1),i=C.useRef(!1);return(0,A.jsx)(li,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})},`DialogContentNonModal`)),li=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=Yr(ai,n);return Cn(),(0,A.jsx)(A.Fragment,{children:(0,A.jsx)(Jt,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,A.jsx)(It,{role:`dialog`,id:s.contentId,"aria-describedby":s.descriptionPresent?s.descriptionId:void 0,"aria-labelledby":s.titlePresent?s.titleId:void 0,"data-state":gi(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})},`DialogContentImpl`)),ui=`DialogTitle`,di=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(ui,n),{setTitleCount:a}=i;return He(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(Tt.h2,{id:i.titleId,...r,ref:t})},`DialogTitle`)),fi=`DialogDescription`,pi=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(fi,n),{setDescriptionCount:a}=i;return He(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(Tt.p,{id:i.descriptionId,...r,ref:t})},`DialogDescription`)),mi=`DialogClose`,hi=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(mi,n);return(0,A.jsx)(Tt.button,{type:`button`,...r,ref:t,onClick:Ee(e.onClick,()=>i.onOpenChange(!1))})},`DialogClose`));function gi(e){return e?`open`:`closed`}Wr(gi,`getState`);function _i(...e){return e.filter(Boolean).join(` `)}function vi({className:e,...t}){return(0,A.jsx)(`button`,{className:_i(`ui-button`,e),...t})}function yi({className:e,...t}){return(0,A.jsx)(`span`,{className:_i(`ui-badge`,e),...t})}function bi({open:e,onOpenChange:t,title:n,description:r,children:i}){return(0,A.jsx)(Xr,{open:e,onOpenChange:t,children:(0,A.jsxs)(ei,{children:[(0,A.jsx)(ni,{className:`sheet-overlay`}),(0,A.jsxs)(oi,{className:`sheet-content`,children:[(0,A.jsxs)(`div`,{className:`sheet-head`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(di,{children:n}),r?(0,A.jsx)(pi,{children:r}):null]}),(0,A.jsx)(hi,{asChild:!0,children:(0,A.jsx)(vi,{"aria-label":`关闭面板`,className:`icon-button`,children:(0,A.jsx)(me,{size:18})})})]}),i]})]})})}function xi({title:e,detail:t}){return(0,A.jsxs)(`div`,{className:`empty-state`,children:[(0,A.jsx)(`strong`,{children:e}),t?(0,A.jsx)(`p`,{children:t}):null]})}var Si=[`codex`,`claude`,`pi`,`builtin-pi`],Ci={overview:`概览`,conversation:`对话`,configuration:`配置`,reminders:`提醒`,workspace:`工作区`,logs:`日志`},wi=window.__LARKIN_DASHBOARD__||{packageVersion:`dev`,dashboardVersion:`dev`,buildFingerprint:``,csrfCapability:``},Ti=class extends Error{body;constructor(e,t){super(e),this.body=t}};async function Ei(e,t={}){let n=await fetch(e,{cache:`no-store`,...t});if(!n.ok){let e=await n.json().catch(()=>({}));throw new Ti(e.error||`HTTP ${n.status}`,e)}return n.json()}function Di(e,t){if(!(e instanceof Ti)||!e.body.readiness)return e instanceof Error?e.message:t;let n=e.body.readiness;return[n.reason||e.message,n.nextAction?`下一步:${n.nextAction}`:null].filter(Boolean).join(`;`)}async function Oi(e){return Ei(`/api/config`,{method:`PATCH`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":wi.csrfCapability},body:JSON.stringify(e)})}function ki(e,t={}){return Ei(e,{...t,headers:{...t.headers,"X-Larkin-CSRF":wi.csrfCapability}})}function Ai(e){return e==null||!Number.isFinite(e)?`暂无`:e<60?`${e}s 前`:e<3600?`${Math.floor(e/60)}m 前`:e<86400?`${Math.floor(e/3600)}h 前`:`${Math.floor(e/86400)}d 前`}function ji(e){let t=new Date(String(e||``));return Number.isFinite(t.getTime())?t.toLocaleString(`zh-CN`,{hour12:!1}):`未知时间`}function Mi(e){return Number.isFinite(Number(e))?Number(e).toLocaleString(`en-US`):`—`}function Ni(e){return e.issue?`异常`:e.lastActivity?.state?String(e.lastActivity.state):e.running?`空闲`:`离线`}function Pi(){let[e,t]=(0,C.useState)(null),[n,r]=(0,C.useState)(null),[i,a]=(0,C.useState)(!0),[o,s]=(0,C.useState)(!1),c=(0,C.useRef)(Se()),l=(0,C.useRef)(null),u=(0,C.useRef)(null),d=(0,C.useCallback)(async()=>{u.current?.abort();let e=new AbortController;u.current=e;let n=c.current.issue();try{let i=await Ei(`/api/status`,{signal:e.signal});if(!c.current.accepts(n))return;if(wi.buildFingerprint&&i.buildFingerprint&&i.buildFingerprint!==wi.buildFingerprint){window.location.reload();return}t(i),r(null),s(!1)}catch(t){if(e.signal.aborted||!c.current.accepts(n))return;r(t instanceof Error?t.message:String(t)),s(!0)}finally{c.current.accepts(n)&&a(!1)}},[]);return(0,C.useEffect)(()=>{let e=!0,t=async()=>{await d(),e&&(l.current=window.setTimeout(t,3e3))};return t(),()=>{e=!1,l.current!==null&&window.clearTimeout(l.current),u.current?.abort()}},[d]),{data:e,error:n,loading:i,stale:o,retry:d}}function Fi({agent:e}){let t=(e.displayName||e.name||e.agentId).slice(0,2).toUpperCase(),[n,r]=(0,C.useState)(!1);(0,C.useEffect)(()=>r(!1),[e.agentId,e.bot?.hasAvatar]);let i=e.bot?.hasAvatar&&!n;return(0,A.jsx)(`span`,{className:`agent-avatar`,"aria-hidden":`true`,children:i?(0,A.jsx)(`img`,{src:`/api/avatar/${encodeURIComponent(e.agentId)}`,alt:``,onError:()=>r(!0)}):t})}function Ii({agents:e,selectedId:t,onSelect:n,compact:r=!1}){let[i,a]=(0,C.useState)(``),o=(0,C.useMemo)(()=>be(e,i),[e,i]),s=(0,C.useRef)([]),c=(e,t)=>{if(!o.length)return;let n=t===0?e:(e+t+o.length)%o.length;s.current[n]?.focus()};return(0,A.jsxs)(`div`,{className:_i(`sidebar-inner`,r&&`compact-sidebar`),children:[(0,A.jsxs)(`label`,{className:`agent-search`,children:[(0,A.jsx)(de,{size:15,"aria-hidden":`true`}),(0,A.jsx)(`span`,{className:`sr-only`,children:`搜索 Agent`}),(0,A.jsx)(`input`,{value:i,onChange:e=>a(e.target.value),placeholder:`搜索 Agent…`})]}),(0,A.jsxs)(`div`,{className:`agent-list`,role:`listbox`,"aria-label":`Agent 列表`,children:[o.map((e,r)=>(0,A.jsxs)(`button`,{ref:e=>{s.current[r]=e},type:`button`,role:`option`,"aria-selected":e.agentId===t,className:_i(`agent-list-item`,e.agentId===t&&`selected`),onClick:()=>n(e.agentId),onKeyDown:e=>{e.key===`ArrowDown`&&(e.preventDefault(),c(r,1)),e.key===`ArrowUp`&&(e.preventDefault(),c(r,-1)),e.key===`Home`&&(e.preventDefault(),c(0,0)),e.key===`End`&&(e.preventDefault(),c(o.length-1,0))},children:[(0,A.jsx)(Fi,{agent:e}),(0,A.jsxs)(`span`,{className:`agent-list-copy`,children:[(0,A.jsx)(`strong`,{children:e.displayName||e.name}),(0,A.jsxs)(`small`,{children:[e.runtime,` · `,e.model,e.runtimeReadiness?` · ${e.runtimeReadiness.state}`:``]}),(0,A.jsxs)(`span`,{className:`status-line`,children:[(0,A.jsx)(ie,{size:10}),` `,e.connection.state,` · `,Ni(e)]})]}),e.issue?(0,A.jsx)(k,{size:15,className:`danger`,"aria-label":`异常`}):(0,A.jsx)(re,{size:15,"aria-hidden":`true`})]},e.agentId)),o.length?null:(0,A.jsx)(xi,{title:`没有匹配的 Agent`,detail:`可按名称或 App ID 搜索`})]})]})}function Li({open:e,onOpenChange:t,onSaved:n}){let[r,i]=(0,C.useState)(`require`),[a,o]=(0,C.useState)(`require`),[s,c]=(0,C.useState)(!1),[l,u]=(0,C.useState)(null),d=a!==r;return(0,C.useEffect)(()=>{if(!e)return;let t=new AbortController;return c(!0),ki(`/api/config`,{signal:t.signal}).then(e=>{i(e.mentionPolicy),o(e.mentionPolicy),u(null)}).catch(e=>{t.signal.aborted||u(e.message)}).finally(()=>{t.signal.aborted||c(!1)}),()=>t.abort()},[e]),(0,A.jsx)(bi,{open:e,onOpenChange:e=>{!e&&d&&!window.confirm(`全局设置尚未保存,确定放弃草稿吗?`)||t(e)},title:`全局设置`,description:`只管理所有 Agent 共同继承的默认值;高级操作仍使用 CLI。`,children:(0,A.jsxs)(`div`,{className:`settings-form`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`真人群消息默认策略`}),(0,A.jsxs)(`select`,{value:a,onChange:e=>o(e.target.value),disabled:s,children:[(0,A.jsx)(`option`,{value:`require`,children:`require · 群内需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`free · 群内无需 @`})]})]}),(0,A.jsx)(`p`,{className:`field-help`,children:`未单独设置的 Agent 和群会使用这个值。机器人消息仍必须精确 @ 当前 Agent。`}),(0,A.jsxs)(`div`,{className:`form-actions`,children:[(0,A.jsx)(vi,{onClick:()=>o(r),disabled:!d||s,children:`放弃草稿`}),(0,A.jsx)(vi,{className:`primary`,onClick:async()=>{c(!0);try{let e=await Oi({operation:`set-global-mention`,value:a});i(a),u(`已保存 · ${e.applyState} · ${e.revision.slice(0,20)}…`),n()}catch(e){u(e instanceof Error?e.message:String(e))}finally{c(!1)}},disabled:!d||s,children:s?`保存中…`:`保存全局设置`})]}),l?(0,A.jsx)(`p`,{role:`status`,className:`feedback`,children:l}):null]})})}function Ri({config:e,onSave:t,disabled:n}){let[r,i]=(0,C.useState)(``),[a,o]=(0,C.useState)(``),[s,c]=(0,C.useState)(`require`),l=e.knownChats.filter(e=>e.kind===`group`&&e.override!==`inherit`).filter(e=>{let t=r.trim().toLocaleLowerCase();return!t||e.chatId.toLocaleLowerCase().includes(t)||String(e.displayName||`未解析群名`).toLocaleLowerCase().includes(t)});return(0,A.jsxs)(`section`,{className:`group-policy-section`,"aria-labelledby":`group-policy-title`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{id:`group-policy-title`,children:`群策略`}),(0,A.jsx)(`p`,{children:`这里只显示为当前 Agent 单独设置过消息策略的群。`})]}),(0,A.jsxs)(`label`,{className:`table-search`,children:[(0,A.jsx)(de,{size:14}),(0,A.jsx)(`span`,{className:`sr-only`,children:`搜索群`}),(0,A.jsx)(`input`,{value:r,onChange:e=>i(e.target.value),placeholder:`搜索群名或 chat_id`})]})]}),(0,A.jsxs)(`div`,{className:`policy-table-wrap`,children:[(0,A.jsxs)(`table`,{className:`policy-table`,children:[(0,A.jsx)(`thead`,{children:(0,A.jsxs)(`tr`,{children:[(0,A.jsx)(`th`,{children:`群`}),(0,A.jsx)(`th`,{children:`消息策略`}),(0,A.jsx)(`th`,{children:`操作`})]})}),(0,A.jsx)(`tbody`,{children:l.map(e=>(0,A.jsxs)(`tr`,{children:[(0,A.jsxs)(`td`,{children:[(0,A.jsx)(`strong`,{children:e.displayName||e.chatId}),e.displayName?(0,A.jsx)(`code`,{children:e.chatId}):(0,A.jsx)(`small`,{children:`群名暂时不可用`})]}),(0,A.jsx)(`td`,{children:(0,A.jsxs)(`select`,{"aria-label":`${e.displayName||e.chatId} 消息策略`,value:e.override,disabled:n,onChange:n=>void t(e.chatId,n.target.value),children:[(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})}),(0,A.jsx)(`td`,{children:(0,A.jsx)(vi,{disabled:n,onClick:()=>void t(e.chatId,`inherit`),children:`移除特别配置`})})]},e.chatId))})]}),l.length?null:(0,A.jsx)(xi,{title:r?`没有匹配的群`:`还没有单独配置的群`,detail:`新增后,Larkin 会自动解析并缓存群名。`})]}),(0,A.jsxs)(`div`,{className:`chat-add`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`完整 chat_id`}),(0,A.jsx)(`input`,{value:a,onChange:e=>o(e.target.value),placeholder:`oc_QAConfigChat1`})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`消息策略`}),(0,A.jsxs)(`select`,{value:s,onChange:e=>c(e.target.value),children:[(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})]}),(0,A.jsx)(vi,{disabled:n||!/^oc_[A-Za-z0-9_-]+$/.test(a),onClick:()=>void t(a,s).then(()=>o(``)),children:`保存群策略`})]})]})}function zi({agentId:e,onDirtyChange:t,refreshKey:n}){let[r,i]=(0,C.useState)(null),[a,o]=(0,C.useState)({}),[s,c]=(0,C.useState)({}),[l,u]=(0,C.useState)(!0),[d,f]=(0,C.useState)(null),[p,m]=(0,C.useState)(null),h=(0,C.useRef)(Se()),g=(0,C.useRef)(Se()),_=!xe(a,s);(0,C.useEffect)(()=>t(_),[_,t]),(0,C.useEffect)(()=>{let e=e=>{_&&e.preventDefault()};return window.addEventListener(`beforeunload`,e),()=>window.removeEventListener(`beforeunload`,e)},[_]);let v=(0,C.useCallback)(async(t=!1)=>{let n=h.current.issue();u(!0);try{let r=await ki(`/api/config?agent=${encodeURIComponent(e)}`);if(!h.current.accepts(n))return;let a=r.agents[0];if(!a)throw Error(`配置投影中没有当前 Agent`);let s={runtime:a.runtimeOption||a.runtime,model:a.model,effort:a.effort||`default`,mention:a.mention.override};i(r),o(s),(!t||!_)&&c(s)}catch(e){h.current.accepts(n)&&f(e instanceof Error?e.message:String(e))}finally{h.current.accepts(n)&&u(!1)}},[e,_]);(0,C.useEffect)(()=>{v(!1)},[e]),(0,C.useEffect)(()=>{n>0&&v(!0)},[n]);let y=r?.agents[0],b=(e,t)=>c(n=>({...n,[e]:t})),x=String(s.runtime||y?.runtime||``),S=String(s.model||y?.model||`default`);(0,C.useEffect)(()=>{if(m({runtime:x,status:`loading`,models:[]}),!new Set([`pi`,`builtin-pi`,`codex`,`claude`]).has(x)){m({runtime:x,status:`error`,models:[]});return}let t=g.current.issue(),n=new AbortController;return ki(`/api/models/${x}?agent=${encodeURIComponent(e)}`,{signal:n.signal}).then(e=>{g.current.accepts(t)&&m({runtime:x,status:`ready`,models:e.models})}).catch(e=>{!n.signal.aborted&&g.current.accepts(t)&&(m({runtime:x,status:`error`,models:[]}),f(`${x} 模型列表暂时不可用:${e instanceof Error?e.message:String(e)}`))}),()=>n.abort()},[e,x]);let w=p?.runtime===x&&p.status===`ready`,ee=p?.runtime===x?p.status:`loading`,te=w?p.models:[],T=`${S} · 当前配置(模型目录${ee===`error`?`不可用`:`加载中`})`,E=w?te.some(e=>e.id===S)?te:[{id:S,label:`${S} · 当前配置`},...te]:[{id:S,label:T}],ne=w&&S!=="default"&&te.find(e=>e.id===S)?.supportedReasoningEfforts||[],re=s.runtime!==a.runtime||s.model!==a.model||s.effort!==a.effort,ie=()=>Ei(`/api/config/apply`,{method:`POST`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":wi.csrfCapability},body:JSON.stringify({agentId:e})}),ae=async()=>{if(y){u(!0);try{let t=[];s.runtime===a.runtime?s.model!==a.model&&t.push({operation:`set-agent-model`,agentId:e,model:s.model}):t.push({operation:`set-agent-runtime`,agentId:e,runtime:s.runtime,model:s.model}),s.effort!==a.effort&&t.push({operation:`set-agent-effort`,agentId:e,effort:s.effort}),s.mention!==a.mention&&t.push({operation:`set-agent-mention`,agentId:e,value:s.mention});let n=null;for(let e of t)n=await Oi(e);if(re)try{await ie(),f(`配置已保存并应用`)}catch(e){f(`配置已保存但未应用:${Di(e,`稍后重试`)}`)}else f(n?`配置已保存`:`没有需要保存的修改`);await v(!1)}catch(e){f(e instanceof Error?e.message:String(e))}finally{u(!1)}}},oe=async()=>{u(!0);try{await ie(),f(`已应用保存的运行配置`),await v(!0)}catch(e){f(`配置保持已保存但未应用:${Di(e,`稍后重试`)}`)}finally{u(!1)}},se=async(t,n)=>{u(!0);try{let r=await Oi({operation:`set-chat-mention`,agentId:e,chatId:t,value:n});f(`群策略已保存 · ${r.applyState}`),await v(!0)}catch(e){f(e instanceof Error?e.message:String(e))}finally{u(!1)}};if(!y&&l)return(0,A.jsx)(xi,{title:`加载 Agent 配置…`});if(!y)return(0,A.jsx)(xi,{title:`配置不可用`,detail:d||`请重试`});let ce=y.apply.applyState===`pending`?`待应用`:y.apply.applyState===`applied`?`已应用`:`状态未知`;return(0,A.jsxs)(`div`,{className:`configuration-page`,children:[(0,A.jsxs)(`section`,{className:`config-section`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`Agent 配置`}),(0,A.jsxs)(`p`,{children:[`只作用于 `,e,`;运行配置会在安全时机自动应用,Agent 正忙时会保留待处理。`]})]}),(0,A.jsx)(yi,{className:y.apply.applyState===`pending`?`warning`:`success`,children:ce})]}),(0,A.jsxs)(`p`,{className:`cli-guidance`,children:[`这里只提供常用微调。更多配置可以直接告诉当前 Agent,让它运行 `,(0,A.jsx)(`code`,{children:`larkin config --help`}),` 后完成。`]}),(0,A.jsxs)(`div`,{className:`config-grid`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Runtime`}),(0,A.jsx)(`select`,{value:x,disabled:l,onChange:e=>{b(`runtime`,e.target.value),b(`model`,`default`),b(`effort`,`default`)},children:(r?.runtimeOptions||Si).map(e=>(0,A.jsx)(`option`,{value:e,children:e},e))})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Model`}),(0,A.jsx)(`select`,{value:S,disabled:l||!w,onChange:e=>{b(`model`,e.target.value),e.target.value==="default"&&b(`effort`,`default`)},children:E.map(e=>(0,A.jsx)(`option`,{value:e.id,children:e.label||e.id},e.id))})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Effort`}),(0,A.jsxs)(`select`,{value:String(s.effort||`default`),disabled:l||!w||S==="default"||!ne.length,onChange:e=>b(`effort`,e.target.value),children:[(0,A.jsx)(`option`,{value:`default`,children:`default · 不指定`}),ne.map(e=>(0,A.jsx)(`option`,{value:e,children:e},e))]})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`群消息策略`}),(0,A.jsxs)(`select`,{value:String(s.mention||`inherit`),disabled:l,onChange:e=>b(`mention`,e.target.value),children:[(0,A.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})]})]}),_||y.apply.applyState===`pending`?(0,A.jsxs)(`div`,{className:`form-actions`,children:[_?(0,A.jsx)(vi,{disabled:l,onClick:()=>c(a),children:`放弃草稿`}):null,_?(0,A.jsx)(vi,{className:`primary`,disabled:l,onClick:ae,children:l?`处理中…`:re?`保存并应用`:`保存 Agent 配置`}):(0,A.jsxs)(vi,{className:`primary`,disabled:l,onClick:oe,children:[(0,A.jsx)(ue,{size:14}),` 应用已保存配置`]})]}):null,d?(0,A.jsx)(`p`,{role:`status`,className:`feedback`,children:d}):null]}),(0,A.jsx)(Ri,{config:y,onSave:se,disabled:l})]})}function Bi({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{};return(0,A.jsxs)(`div`,{className:`overview-page`,children:[(0,A.jsxs)(`div`,{className:`metrics-band`,"aria-label":`Agent 运行指标`,children:[(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ie,{})}),(0,A.jsx)(`span`,{children:`连接`}),(0,A.jsx)(`strong`,{children:e.connection.state}),(0,A.jsx)(`small`,{children:e.connection.reason})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(E,{})}),(0,A.jsx)(`span`,{children:`执行状态`}),(0,A.jsx)(`strong`,{children:Ni(e)}),(0,A.jsx)(`small`,{children:e.lastActivity?Ai(e.lastActivity.ageSec):`尚无活动`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(le,{})}),(0,A.jsx)(`span`,{children:`最近消息`}),(0,A.jsx)(`strong`,{children:e.lastDeliver?.from||`暂无`}),(0,A.jsx)(`small`,{children:e.lastDeliver?`${e.lastDeliver.target||``} · ${Ai(e.lastDeliver.ageSec)}`:`尚无消息记录`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ce,{})}),(0,A.jsx)(`span`,{children:`上下文`}),(0,A.jsx)(`strong`,{children:t.contextPercent==null?`${e.session?.turns||0} turns`:`${Math.round(Number(t.contextPercent))}%`}),(0,A.jsx)(`small`,{children:e.session?`session ${e.session.id.slice(0,12)}…`:`尚未建立 session`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ce,{})}),(0,A.jsx)(`span`,{children:`累计 Token`}),(0,A.jsx)(`strong`,{children:Mi(t.cumulativeTokens)}),(0,A.jsx)(`small`,{children:t.available?`完整 session 累计`:t.reason||`暂无用量`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(E,{})}),(0,A.jsx)(`span`,{children:`最近一轮用量`}),(0,A.jsx)(`strong`,{children:Mi(t.latestTokens)}),(0,A.jsx)(`small`,{children:`输入、缓存与输出合计`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ue,{})}),(0,A.jsx)(`span`,{children:`Compaction`}),(0,A.jsx)(`strong`,{children:Mi(n.count)}),(0,A.jsx)(`small`,{children:n.active?`正在压缩上下文`:n.lastFinishedAt?`最近 ${ji(n.lastFinishedAt)}`:`尚无压缩记录`})]})]}),(0,A.jsxs)(`div`,{className:`overview-sections`,children:[(0,A.jsxs)(`section`,{className:`content-section wide`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`最近动态`}),(0,A.jsx)(`p`,{children:`当前 Agent 的消息、工具和状态转换。`})]}),(0,A.jsx)(yi,{children:e.feed.length})]}),(0,A.jsx)(M,{items:e.feed.slice(0,10)})]}),(0,A.jsxs)(`section`,{className:`content-section`,children:[(0,A.jsx)(`h3`,{children:`运行摘要`}),(0,A.jsxs)(`dl`,{className:`facts`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Runtime`}),(0,A.jsx)(`dd`,{children:e.runtime})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Readiness`}),(0,A.jsx)(`dd`,{children:e.runtimeReadiness?`${e.runtimeReadiness.state}${e.runtimeReadiness.version?` · ${e.runtimeReadiness.version}`:``}${e.runtimeReadiness.executable?` · ${e.runtimeReadiness.executable}`:``}${e.runtimeReadiness.reason?` · ${e.runtimeReadiness.reason}`:``}${e.runtimeReadiness.nextAction?` · ${e.runtimeReadiness.nextAction}`:``}`:`unknown`})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Model`}),(0,A.jsx)(`dd`,{children:e.model})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Effort`}),(0,A.jsx)(`dd`,{children:e.effort||`default`})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`入站`}),(0,A.jsx)(`dd`,{children:e.inbound.state})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`提醒`}),(0,A.jsx)(`dd`,{children:e.activeReminders})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`指示灯`}),(0,A.jsx)(`dd`,{children:e.eyeIndicator.stuck?`疑似卡住`:`${e.eyeIndicator.pendingCount} pending`})]})]})]})]})]})}function M({items:e}){if(!e.length)return(0,A.jsx)(xi,{title:`还没有活动记录`});let t=e=>e.kind===`error`||e.state===`error`?`state-error`:e.kind===`deliver`?`state-deliver`:[`working`,`thinking`,`tool`].includes(String(e.state))?`state-active`:[`idle`,`online`].includes(String(e.state))?`state-idle`:`state-unknown`;return(0,A.jsx)(`ol`,{className:`timeline`,children:e.map((e,n)=>(0,A.jsxs)(`li`,{children:[(0,A.jsx)(`span`,{className:_i(`timeline-dot`,t(e))}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`strong`,{children:e.kind===`deliver`?`收到消息`:e.kind===`error`?`错误`:e.state||`活动`}),(0,A.jsx)(`p`,{children:e.text||e.detail||[e.from,e.target].filter(Boolean).join(` → `)||e.tool||`状态已更新`}),(0,A.jsx)(`time`,{children:ji(e.at)})]})]},`${e.at}-${n}`))})}function N({agent:e}){return e.conversation.length?(0,A.jsx)(`div`,{className:`conversation-list`,children:e.conversation.map((t,n)=>{let r=t.direction===`out`?`已发送`:t.wake?`入站 · 已唤醒`:`旁听 · 未唤醒`;return(0,A.jsxs)(`article`,{className:_i(`conversation-item`,t.direction===`out`?`outbound`:`inbound`,t.direction===`in`&&!t.wake&&`listening`),children:[(0,A.jsxs)(`header`,{children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(yi,{className:t.direction===`in`&&!t.wake?`warning`:t.direction===`out`?`accent`:`success`,children:r}),(0,A.jsx)(`strong`,{children:t.from||(t.direction===`out`?e.displayName:`未知发送者`)})]}),(0,A.jsx)(`time`,{children:ji(t.at)})]}),(0,A.jsx)(`p`,{children:t.text||`[非文本消息]`}),t.target?(0,A.jsx)(`small`,{children:t.target}):null]},`${t.at}-${n}`)})}):(0,A.jsx)(xi,{title:`还没有对话摘录`,detail:`Host 更新本地 conversation projection 后会显示在这里。`})}function Vi({agent:e}){if(!e.remindersList.length)return(0,A.jsx)(xi,{title:`还没有提醒`});let t={scheduled:`待执行`,pending:`待执行`,fired:`已完成`,canceled:`已取消`,cancelled:`已取消`,failed:`失败`};return(0,A.jsx)(`div`,{className:`reminder-list`,children:e.remindersList.map((e,n)=>{let r=e.status||`scheduled`,i=[`fired`,`canceled`,`cancelled`].includes(r);return(0,A.jsxs)(`article`,{className:_i(`status-${r===`cancelled`?`canceled`:r}`,i&&`done`),children:[(0,A.jsx)(ne,{size:16}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`strong`,{children:e.title||`未命名提醒`}),(0,A.jsxs)(`p`,{children:[ji(e.fireAt),e.repeat?` · 重复`:``]})]}),(0,A.jsx)(yi,{className:[`scheduled`,`pending`].includes(r)?`warning`:r===`fired`?`success`:r===`failed`?`danger-badge`:``,children:t[r]||r})]},`${e.fireAt}-${n}`)})})}function Hi({agentId:e}){let[t,n]=(0,C.useState)(null),[r,i]=(0,C.useState)(null),[a,o]=(0,C.useState)(!0),[s,c]=(0,C.useState)(null),l=(0,C.useRef)(Se()),u=(0,C.useCallback)(async t=>{let r=l.current.issue();o(!0);try{let a=await Ei(`/api/workspace?agent=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`);if(!l.current.accepts(r))return;a.kind===`directory`?(n(a),i(null)):i(a),c(null)}catch(e){l.current.accepts(r)&&c(e instanceof Error?e.message:String(e))}finally{l.current.accepts(r)&&o(!1)}},[e]);(0,C.useEffect)(()=>{u(``)},[u]);let d=t?.path.split(`/`).filter(Boolean)||[];return(0,A.jsxs)(`div`,{className:`workspace-browser`,children:[(0,A.jsxs)(`div`,{className:`workspace-side`,children:[(0,A.jsxs)(`nav`,{className:`breadcrumbs`,"aria-label":`工作区路径`,children:[(0,A.jsx)(`button`,{onClick:()=>void u(``),children:`workspace`}),d.map((e,t)=>(0,A.jsxs)(`span`,{children:[(0,A.jsx)(re,{size:12}),(0,A.jsx)(`button`,{onClick:()=>void u(d.slice(0,t+1).join(`/`)),children:e})]},`${e}-${t}`))]}),(0,A.jsxs)(`div`,{className:`workspace-list`,role:`region`,"aria-label":`工作区目录`,children:[t?.entries.map(e=>(0,A.jsxs)(`button`,{onClick:()=>void u(e.path),children:[e.kind===`directory`?(0,A.jsx)(se,{size:15}):(0,A.jsx)(oe,{size:15}),(0,A.jsx)(`span`,{children:e.name}),(0,A.jsx)(`small`,{children:e.kind===`file`?`${e.size} B`:`目录`})]},e.path)),!a&&t&&!t.entries.length?(0,A.jsx)(xi,{title:`空目录`}):null]})]}),(0,A.jsx)(`div`,{className:`workspace-preview`,role:`region`,"aria-label":`文件预览`,children:a?(0,A.jsx)(xi,{title:`读取工作区…`}):s?(0,A.jsx)(xi,{title:`工作区读取失败`,detail:s}):r?(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`header`,{children:[(0,A.jsx)(`strong`,{children:r.name}),(0,A.jsx)(`small`,{children:r.truncated?`仅预览前 1 MB`:`${r.size} B`})]}),r.binary?(0,A.jsx)(xi,{title:`二进制文件不可预览`}):(0,A.jsx)(`pre`,{children:r.content})]}):(0,A.jsx)(xi,{title:`只读工作区`,detail:`选择文件进行预览`})})]})}function Ui({agent:e}){return(0,A.jsxs)(`section`,{className:`logs-section`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`结构化日志`}),(0,A.jsx)(`p`,{children:`最近 30 条本地状态投影;不暴露原始日志文件或路径。`})]}),(0,A.jsx)(yi,{children:e.feed.length})]}),(0,A.jsx)(M,{items:e.feed}),e.recentErrors.length?(0,A.jsxs)(`section`,{className:`error-stack`,children:[(0,A.jsx)(`h4`,{children:`最近错误`}),e.recentErrors.map((e,t)=>(0,A.jsxs)(`div`,{children:[(0,A.jsx)(ae,{size:14}),(0,A.jsx)(`span`,{children:e.text||e.message||`未知错误`}),(0,A.jsx)(`time`,{children:ji(e.at)})]},`${e.at}-${t}`))]}):null]})}function Wi({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{},r=t.contextPercent==null?`上下文 ${e.session?.turns||0} turns`:`上下文 ${Math.round(Number(t.contextPercent))}% · ${Mi(t.latestTokens)} / ${Mi(t.contextWindow)}`;return(0,A.jsxs)(`header`,{className:`agent-header`,children:[(0,A.jsxs)(`div`,{className:`agent-title`,children:[(0,A.jsx)(Fi,{agent:e}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`p`,{children:e.agentId}),(0,A.jsx)(`h1`,{children:e.displayName||e.name})]})]}),(0,A.jsxs)(`div`,{className:`agent-header-summary`,children:[(0,A.jsx)(yi,{className:e.connection.state===`connected`?`success`:e.issue?`danger-badge`:``,children:e.connection.state}),(0,A.jsx)(`span`,{children:e.runtime}),(0,A.jsx)(`span`,{children:e.model}),(0,A.jsx)(`span`,{children:e.effort||`default effort`}),(0,A.jsx)(`span`,{children:r}),(0,A.jsxs)(`span`,{children:[`累计 `,Mi(t.cumulativeTokens),` tokens`]}),(0,A.jsxs)(`span`,{children:[`最近一轮 `,Mi(t.latestTokens),` tokens`]}),(0,A.jsxs)(`span`,{children:[`Compact `,Mi(n.count),` 次 · `,n.active?`active`:`idle`]}),(0,A.jsxs)(`span`,{children:[`最近消息 `,e.lastDeliver?Ai(e.lastDeliver.ageSec):`暂无`]}),(0,A.jsx)(`span`,{children:e.lastActivity?.detail||Ni(e)})]})]})}function Gi(){let e=Pi(),t=(0,C.useMemo)(()=>_e(window.location.search),[]),[n,r]=(0,C.useState)(t.agentId),[i,a]=(0,C.useState)(t.tab),[o,s]=(0,C.useState)(!1),[c,l]=(0,C.useState)(!1),[u,d]=(0,C.useState)(0),[f,p]=(0,C.useState)(!1),[m,h]=(0,C.useState)(null),g=(0,C.useRef)(new Map),_=e.data?.agents||[],v=e.data?.packageVersion||wi.packageVersion,y=_.find(e=>e.agentId===n)||null,b=(0,C.useCallback)((e,t)=>{let n=`${e}:${t}`,r=window.scrollY;g.current.set(n,r);try{sessionStorage.setItem(`larkin-dashboard-scroll:${n}`,String(r))}catch{}},[]),x=(0,C.useCallback)(e=>{if(g.current.has(e))return g.current.get(e)||0;try{let t=Number(sessionStorage.getItem(`larkin-dashboard-scroll:${e}`));return Number.isFinite(t)&&t>=0?t:0}catch{return 0}},[]),S=(0,C.useCallback)((e,t,o=!1)=>{n&&b(n,i);let s=o?`replaceState`:`pushState`;window.history[s](null,``,ve(e,t)),r(e),a(t)},[b,n,i]),w=()=>!f||window.confirm(`Agent 配置尚未保存,确定放弃草稿吗?`),ee=e=>{w()&&(p(!1),S(e,i),l(!1))},te=e=>{e!==`configuration`&&!w()||(e!==`configuration`&&p(!1),S(n,e))};return(0,C.useEffect)(()=>{if(!e.data)return;let t=ye(n,e.data.agents);t!==n&&(h(n?`原 Agent 已不存在,已切换到第一个可用 Agent。`:null),S(t,i,!0))},[e.data,n,i,S]),(0,C.useEffect)(()=>{let e=()=>{let e=_e(window.location.search);if(f&&!window.confirm(`Agent 配置尚未保存,确定离开吗?`)){window.history.pushState(null,``,ve(n,i));return}n&&b(n,i),p(!1),r(e.agentId),a(e.tab)};return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},[f,b,n,i]),(0,C.useLayoutEffect)(()=>{if(!n)return;let e=`${n}:${i}`,t=x(e);window.scrollY!==t&&window.scrollTo({top:t,behavior:`auto`})},[x,n,i]),(0,A.jsxs)(`div`,{className:_i(`app-shell`,i===`workspace`&&`workspace-app-active`),children:[(0,A.jsxs)(`aside`,{className:`desktop-sidebar`,children:[(0,A.jsxs)(`div`,{className:`brand`,children:[(0,A.jsx)(`img`,{src:`/assets/larkin-mark.svg`,alt:``}),(0,A.jsxs)(`strong`,{children:[`Larkin `,(0,A.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]})]}),(0,A.jsx)(Ii,{agents:_,selectedId:n,onSelect:ee}),(0,A.jsx)(`footer`,{children:e.data?.daemon.running?`${_.length} Agents · daemon online`:`daemon offline`})]}),(0,A.jsxs)(`main`,{className:_i(`workspace-shell`,i===`workspace`&&`workspace-route-active`),children:[(0,A.jsxs)(`div`,{className:`app-topbar`,children:[(0,A.jsx)(vi,{className:`mobile-menu`,"aria-label":`打开 Agent 导航`,onClick:()=>l(!0),children:(0,A.jsx)(O,{size:18})}),(0,A.jsxs)(`div`,{children:[(0,A.jsxs)(`strong`,{children:[`Larkin `,(0,A.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]}),(0,A.jsx)(`span`,{children:e.data?.daemon.running?`运行中`:`未运行`}),e.stale?(0,A.jsx)(yi,{className:`warning`,children:`状态陈旧`}):null]}),(0,A.jsxs)(vi,{onClick:()=>s(!0),children:[(0,A.jsx)(fe,{size:15}),` 全局设置`]})]}),e.error?(0,A.jsxs)(`div`,{className:`stale-banner`,role:`alert`,children:[(0,A.jsx)(k,{size:15}),`状态刷新失败,保留最后一次数据:`,e.error,(0,A.jsx)(vi,{onClick:()=>void e.retry(),children:`重试`})]}):null,m?(0,A.jsxs)(`div`,{className:`notice`,role:`status`,children:[m,(0,A.jsx)(vi,{"aria-label":`关闭提示`,onClick:()=>h(null),children:`×`})]}):null,e.loading&&!e.data?(0,A.jsxs)(`div`,{className:`page-loading`,children:[(0,A.jsx)(ue,{className:`spin`}),`加载 Agent 工作台…`]}):y?(0,A.jsxs)(A.Fragment,{children:[(0,A.jsx)(Wi,{agent:y}),(0,A.jsx)(`nav`,{className:`agent-tabs`,"aria-label":`${y.displayName} 内容页签`,role:`tablist`,children:ge.map(e=>(0,A.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":i===e,className:i===e?`active`:``,onClick:()=>te(e),children:[e===`overview`?(0,A.jsx)(ce,{}):e===`conversation`?(0,A.jsx)(le,{}):e===`configuration`?(0,A.jsx)(pe,{}):e===`reminders`?(0,A.jsx)(ne,{}):e===`workspace`?(0,A.jsx)(se,{}):(0,A.jsx)(D,{}),Ci[e]]},e))}),(0,A.jsxs)(`div`,{className:_i(`agent-content`,i===`workspace`&&`workspace-active`),children:[(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`overview`,children:(0,A.jsx)(Bi,{agent:y})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`conversation`,children:(0,A.jsx)(N,{agent:y})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`configuration`,children:(0,A.jsx)(zi,{agentId:y.agentId,onDirtyChange:p,refreshKey:u})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`reminders`,children:(0,A.jsx)(Vi,{agent:y})}),(0,A.jsx)(`section`,{className:`workspace-tab-panel`,role:`tabpanel`,hidden:i!==`workspace`,children:(0,A.jsx)(Hi,{agentId:y.agentId})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`logs`,children:(0,A.jsx)(Ui,{agent:y})})]},y.agentId)]}):(0,A.jsx)(xi,{title:`还没有配置任何 Agent`,detail:`先运行 larkin setup。`})]}),(0,A.jsx)(Li,{open:o,onOpenChange:s,onSaved:()=>d(e=>e+1)}),(0,A.jsx)(bi,{open:c,onOpenChange:l,title:`选择 Agent`,description:`搜索、查看状态并切换当前工作区。`,children:(0,A.jsx)(Ii,{compact:!0,agents:_,selectedId:n,onSelect:ee})})]})}var Ki=document.getElementById(`root`);if(!Ki)throw Error(`dashboard root is missing`);(0,he.createRoot)(Ki).render((0,A.jsx)(C.StrictMode,{children:(0,A.jsx)(Gi,{})}));
|
package/dist/platform/config.cjs
CHANGED
|
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.toUserRuntime = exports.runtimeOptionTarget = exports.runtimeOptionOf = exports.isUserRuntime = exports.isAdapterRuntime = exports.fromUserRuntime = exports.RUNTIME_OPTIONS = void 0;
|
|
39
40
|
exports.loadRuntimeModels = loadRuntimeModels;
|
|
40
41
|
exports.defaultModelFor = defaultModelFor;
|
|
41
42
|
exports.resolveConfigDir = resolveConfigDir;
|
|
@@ -63,8 +64,18 @@ const path = __importStar(require("node:path"));
|
|
|
63
64
|
const root_layout_js_1 = require("./root-layout.cjs");
|
|
64
65
|
const secure_metadata_js_1 = require("./secure-metadata.cjs");
|
|
65
66
|
const runtime_model_catalog_js_1 = require("../runtime/runtime-model-catalog.cjs");
|
|
67
|
+
const pi_provider_config_js_1 = require("../runtime/pi-provider-config.cjs");
|
|
68
|
+
const user_runtime_js_1 = require("../runtime/user-runtime.cjs");
|
|
66
69
|
const process_inspect_cjs_1 = __importDefault(require("./process-inspect.cjs"));
|
|
67
70
|
const pi_profile_migration_js_1 = require("../runtime/pi-profile-migration.cjs");
|
|
71
|
+
var user_runtime_js_2 = require("../runtime/user-runtime.cjs");
|
|
72
|
+
Object.defineProperty(exports, "RUNTIME_OPTIONS", { enumerable: true, get: function () { return user_runtime_js_2.RUNTIME_OPTIONS; } });
|
|
73
|
+
Object.defineProperty(exports, "fromUserRuntime", { enumerable: true, get: function () { return user_runtime_js_2.fromUserRuntime; } });
|
|
74
|
+
Object.defineProperty(exports, "isAdapterRuntime", { enumerable: true, get: function () { return user_runtime_js_2.isAdapterRuntime; } });
|
|
75
|
+
Object.defineProperty(exports, "isUserRuntime", { enumerable: true, get: function () { return user_runtime_js_2.isUserRuntime; } });
|
|
76
|
+
Object.defineProperty(exports, "runtimeOptionOf", { enumerable: true, get: function () { return user_runtime_js_2.runtimeOptionOf; } });
|
|
77
|
+
Object.defineProperty(exports, "runtimeOptionTarget", { enumerable: true, get: function () { return user_runtime_js_2.runtimeOptionTarget; } });
|
|
78
|
+
Object.defineProperty(exports, "toUserRuntime", { enumerable: true, get: function () { return user_runtime_js_2.toUserRuntime; } });
|
|
68
79
|
const TOP_FIELDS_V3 = new Set(["version", "serverId", "activeAgent", "agents"]);
|
|
69
80
|
const TOP_FIELDS_V4 = new Set(["version", "serverId", "mentionPolicy", "activeAgent", "agents"]);
|
|
70
81
|
const AGENT_FIELDS_V3 = new Set(["runtime", "model", "effort", "noMentionChats", "createdAt"]);
|
|
@@ -105,7 +116,8 @@ function loadRuntimeModels() {
|
|
|
105
116
|
return runtimeModels;
|
|
106
117
|
}
|
|
107
118
|
function defaultModelFor(runtime) {
|
|
108
|
-
const
|
|
119
|
+
const adapter = (0, user_runtime_js_1.isUserRuntime)(runtime) ? (0, user_runtime_js_1.fromUserRuntime)(runtime).runtime : runtime;
|
|
120
|
+
const models = loadRuntimeModels()[adapter];
|
|
109
121
|
if (!models)
|
|
110
122
|
throw new Error(`runtime 模型目录不存在 runtime: ${runtime}`);
|
|
111
123
|
return models[0].id;
|
|
@@ -153,7 +165,7 @@ function validateStoredAgent(key, agent, version) {
|
|
|
153
165
|
assertAllowedFields(agent, version === 3 ? AGENT_FIELDS_V3 : AGENT_FIELDS_V4, `Agent ${key}`);
|
|
154
166
|
if (typeof agent.runtime !== "string" || !agent.runtime)
|
|
155
167
|
throw new Error(`Agent ${key}.runtime 必须是非空字符串`);
|
|
156
|
-
if (!
|
|
168
|
+
if (!(0, user_runtime_js_1.isAdapterRuntime)(agent.runtime))
|
|
157
169
|
throw new Error(`Agent ${key}.runtime 未知:${agent.runtime}`);
|
|
158
170
|
if (typeof agent.model !== "string" || !agent.model)
|
|
159
171
|
throw new Error(`Agent ${key}.model 必须是非空字符串`);
|
|
@@ -632,12 +644,23 @@ function applyMutation(config, mutation) {
|
|
|
632
644
|
agent.piDistribution = mutation.distribution;
|
|
633
645
|
}
|
|
634
646
|
else if (mutation.kind === "set-agent-runtime") {
|
|
635
|
-
if (!
|
|
647
|
+
if (!(0, user_runtime_js_1.isUserRuntime)(mutation.runtime))
|
|
636
648
|
throw new Error(`未知 runtime:${mutation.runtime}`);
|
|
637
|
-
|
|
638
|
-
|
|
649
|
+
const stored = (0, user_runtime_js_1.fromUserRuntime)(mutation.runtime);
|
|
650
|
+
if (stored.piDistribution === "builtin") {
|
|
651
|
+
try {
|
|
652
|
+
(0, pi_provider_config_js_1.assertBuiltinPiAgentDirectory)((0, pi_provider_config_js_1.piAgentDirectory)(config.configDir, mutation.agentId));
|
|
653
|
+
}
|
|
654
|
+
catch (error) {
|
|
655
|
+
throw new Error(`无法切换到 builtin-pi:${error instanceof Error ? error.message : String(error)}。请先运行 larkin setup 并为该 Agent 选择 builtin-pi 完成 provider 配置`);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
assertModel(stored.runtime, mutation.model || "default");
|
|
659
|
+
agent.runtime = stored.runtime;
|
|
639
660
|
agent.model = mutation.model || "default";
|
|
640
|
-
if (
|
|
661
|
+
if (stored.piDistribution)
|
|
662
|
+
agent.piDistribution = stored.piDistribution;
|
|
663
|
+
else
|
|
641
664
|
delete agent.piDistribution;
|
|
642
665
|
delete agent.effort;
|
|
643
666
|
}
|
|
@@ -1105,7 +1128,7 @@ function commitSetupConfig(env, expectedRevision, nextStored) {
|
|
|
1105
1128
|
function safeConfigView(config, onlyAgentId, chatId, applyState) {
|
|
1106
1129
|
const entries = onlyAgentId ? [[onlyAgentId, config.agents[onlyAgentId]]] : Object.entries(config.agents);
|
|
1107
1130
|
const agents = entries.filter((entry) => Boolean(entry[1])).map(([agentId, agent]) => ({
|
|
1108
|
-
agentId, runtime: agent.runtime, model: agent.model, piDistribution: agent.piDistribution ?? (agent.runtime === "pi" ? "external" : null), effort: agent.effort ?? null,
|
|
1131
|
+
agentId, runtime: agent.runtime, runtimeOption: (0, user_runtime_js_1.toUserRuntime)(agent.runtime, agent.piDistribution), model: agent.model, piDistribution: agent.piDistribution ?? (agent.runtime === "pi" ? "external" : null), effort: agent.effort ?? null,
|
|
1109
1132
|
mention: {
|
|
1110
1133
|
override: agent.mentionPolicy ?? "inherit",
|
|
1111
1134
|
effective: agent.mentionPolicy ?? config.mentionPolicy,
|
package/dist/platform/config.mjs
CHANGED
|
@@ -5,8 +5,11 @@ import * as path from "node:path";
|
|
|
5
5
|
import { TargetRootLayout, resolveConfigDir as resolveRootConfigDir } from "./root-layout.mjs";
|
|
6
6
|
import { exactMode, fsyncDirectoryOf } from "./secure-metadata.mjs";
|
|
7
7
|
import { CURRENT_RUNTIME_MODELS } from "../runtime/runtime-model-catalog.mjs";
|
|
8
|
+
import { assertBuiltinPiAgentDirectory, piAgentDirectory } from "../runtime/pi-provider-config.mjs";
|
|
9
|
+
import { fromUserRuntime, isAdapterRuntime, isUserRuntime, toUserRuntime } from "../runtime/user-runtime.mjs";
|
|
8
10
|
import processInspect from "./process-inspect.cjs";
|
|
9
11
|
import { applyPiProfileMigration, assertPiProfileMigrationAfterState, preparePiProfileMigration, releasePiProfileMigrationLock, rollbackPiProfileMigration, validatePiProfileMigrationState, } from "../runtime/pi-profile-migration.mjs";
|
|
12
|
+
export { RUNTIME_OPTIONS, fromUserRuntime, isAdapterRuntime, isUserRuntime, runtimeOptionOf, runtimeOptionTarget, toUserRuntime, } from "../runtime/user-runtime.mjs";
|
|
10
13
|
const TOP_FIELDS_V3 = new Set(["version", "serverId", "activeAgent", "agents"]);
|
|
11
14
|
const TOP_FIELDS_V4 = new Set(["version", "serverId", "mentionPolicy", "activeAgent", "agents"]);
|
|
12
15
|
const AGENT_FIELDS_V3 = new Set(["runtime", "model", "effort", "noMentionChats", "createdAt"]);
|
|
@@ -47,7 +50,8 @@ export function loadRuntimeModels() {
|
|
|
47
50
|
return runtimeModels;
|
|
48
51
|
}
|
|
49
52
|
export function defaultModelFor(runtime) {
|
|
50
|
-
const
|
|
53
|
+
const adapter = isUserRuntime(runtime) ? fromUserRuntime(runtime).runtime : runtime;
|
|
54
|
+
const models = loadRuntimeModels()[adapter];
|
|
51
55
|
if (!models)
|
|
52
56
|
throw new Error(`runtime 模型目录不存在 runtime: ${runtime}`);
|
|
53
57
|
return models[0].id;
|
|
@@ -95,7 +99,7 @@ function validateStoredAgent(key, agent, version) {
|
|
|
95
99
|
assertAllowedFields(agent, version === 3 ? AGENT_FIELDS_V3 : AGENT_FIELDS_V4, `Agent ${key}`);
|
|
96
100
|
if (typeof agent.runtime !== "string" || !agent.runtime)
|
|
97
101
|
throw new Error(`Agent ${key}.runtime 必须是非空字符串`);
|
|
98
|
-
if (!
|
|
102
|
+
if (!isAdapterRuntime(agent.runtime))
|
|
99
103
|
throw new Error(`Agent ${key}.runtime 未知:${agent.runtime}`);
|
|
100
104
|
if (typeof agent.model !== "string" || !agent.model)
|
|
101
105
|
throw new Error(`Agent ${key}.model 必须是非空字符串`);
|
|
@@ -574,12 +578,23 @@ function applyMutation(config, mutation) {
|
|
|
574
578
|
agent.piDistribution = mutation.distribution;
|
|
575
579
|
}
|
|
576
580
|
else if (mutation.kind === "set-agent-runtime") {
|
|
577
|
-
if (!
|
|
581
|
+
if (!isUserRuntime(mutation.runtime))
|
|
578
582
|
throw new Error(`未知 runtime:${mutation.runtime}`);
|
|
579
|
-
|
|
580
|
-
|
|
583
|
+
const stored = fromUserRuntime(mutation.runtime);
|
|
584
|
+
if (stored.piDistribution === "builtin") {
|
|
585
|
+
try {
|
|
586
|
+
assertBuiltinPiAgentDirectory(piAgentDirectory(config.configDir, mutation.agentId));
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
throw new Error(`无法切换到 builtin-pi:${error instanceof Error ? error.message : String(error)}。请先运行 larkin setup 并为该 Agent 选择 builtin-pi 完成 provider 配置`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
assertModel(stored.runtime, mutation.model || "default");
|
|
593
|
+
agent.runtime = stored.runtime;
|
|
581
594
|
agent.model = mutation.model || "default";
|
|
582
|
-
if (
|
|
595
|
+
if (stored.piDistribution)
|
|
596
|
+
agent.piDistribution = stored.piDistribution;
|
|
597
|
+
else
|
|
583
598
|
delete agent.piDistribution;
|
|
584
599
|
delete agent.effort;
|
|
585
600
|
}
|
|
@@ -1047,7 +1062,7 @@ export function commitSetupConfig(env, expectedRevision, nextStored) {
|
|
|
1047
1062
|
export function safeConfigView(config, onlyAgentId, chatId, applyState) {
|
|
1048
1063
|
const entries = onlyAgentId ? [[onlyAgentId, config.agents[onlyAgentId]]] : Object.entries(config.agents);
|
|
1049
1064
|
const agents = entries.filter((entry) => Boolean(entry[1])).map(([agentId, agent]) => ({
|
|
1050
|
-
agentId, runtime: agent.runtime, model: agent.model, piDistribution: agent.piDistribution ?? (agent.runtime === "pi" ? "external" : null), effort: agent.effort ?? null,
|
|
1065
|
+
agentId, runtime: agent.runtime, runtimeOption: toUserRuntime(agent.runtime, agent.piDistribution), model: agent.model, piDistribution: agent.piDistribution ?? (agent.runtime === "pi" ? "external" : null), effort: agent.effort ?? null,
|
|
1051
1066
|
mention: {
|
|
1052
1067
|
override: agent.mentionPolicy ?? "inherit",
|
|
1053
1068
|
effective: agent.mentionPolicy ?? config.mentionPolicy,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CURRENT_RUNTIME_MODELS = void 0;
|
|
4
|
-
//
|
|
5
|
-
// these placeholders/fallbacks at runtime with their machine-readable catalogs.
|
|
4
|
+
// Authored catalog keys are adapter ids only. builtin-pi is a user-facing sibling, not a stored runtime.
|
|
5
|
+
// Pi, Codex and Claude replace these placeholders/fallbacks at runtime with their machine-readable catalogs.
|
|
6
6
|
// Claude's supported stdin control-channel list is the live authority; its entries below
|
|
7
7
|
// remain explicit compatibility candidates for config loading and discovery failures.
|
|
8
|
+
const PI_AUTHORED_MODELS = [{ id: "default", label: "default", verified: "dynamic" }];
|
|
8
9
|
exports.CURRENT_RUNTIME_MODELS = {
|
|
9
10
|
codex: [
|
|
10
11
|
{ id: "default", label: "default", verified: "dynamic" },
|
|
@@ -39,5 +40,5 @@ exports.CURRENT_RUNTIME_MODELS = {
|
|
|
39
40
|
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6 · 静态候选", verified: "authored-candidate" },
|
|
40
41
|
{ id: "claude-haiku-4-5", label: "Claude Haiku 4.5 · 静态候选", verified: "authored-candidate" },
|
|
41
42
|
],
|
|
42
|
-
pi: [
|
|
43
|
+
pi: [...PI_AUTHORED_MODELS],
|
|
43
44
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
// these placeholders/fallbacks at runtime with their machine-readable catalogs.
|
|
1
|
+
// Authored catalog keys are adapter ids only. builtin-pi is a user-facing sibling, not a stored runtime.
|
|
2
|
+
// Pi, Codex and Claude replace these placeholders/fallbacks at runtime with their machine-readable catalogs.
|
|
3
3
|
// Claude's supported stdin control-channel list is the live authority; its entries below
|
|
4
4
|
// remain explicit compatibility candidates for config loading and discovery failures.
|
|
5
|
+
const PI_AUTHORED_MODELS = [{ id: "default", label: "default", verified: "dynamic" }];
|
|
5
6
|
export const CURRENT_RUNTIME_MODELS = {
|
|
6
7
|
codex: [
|
|
7
8
|
{ id: "default", label: "default", verified: "dynamic" },
|
|
@@ -36,5 +37,5 @@ export const CURRENT_RUNTIME_MODELS = {
|
|
|
36
37
|
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6 · 静态候选", verified: "authored-candidate" },
|
|
37
38
|
{ id: "claude-haiku-4-5", label: "Claude Haiku 4.5 · 静态候选", verified: "authored-candidate" },
|
|
38
39
|
],
|
|
39
|
-
pi: [
|
|
40
|
+
pi: [...PI_AUTHORED_MODELS],
|
|
40
41
|
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ADAPTER_RUNTIMES = exports.USER_RUNTIMES = exports.RUNTIME_OPTIONS = void 0;
|
|
4
|
+
exports.isUserRuntime = isUserRuntime;
|
|
5
|
+
exports.isAdapterRuntime = isAdapterRuntime;
|
|
6
|
+
exports.runtimeOptionOf = runtimeOptionOf;
|
|
7
|
+
exports.toUserRuntime = toUserRuntime;
|
|
8
|
+
exports.runtimeOptionTarget = runtimeOptionTarget;
|
|
9
|
+
exports.fromUserRuntime = fromUserRuntime;
|
|
10
|
+
exports.piCatalogDistributionForUserRuntime = piCatalogDistributionForUserRuntime;
|
|
11
|
+
exports.RUNTIME_OPTIONS = ["codex", "claude", "pi", "builtin-pi"];
|
|
12
|
+
exports.USER_RUNTIMES = exports.RUNTIME_OPTIONS;
|
|
13
|
+
exports.ADAPTER_RUNTIMES = ["pi", "codex", "claude"];
|
|
14
|
+
function isUserRuntime(value) {
|
|
15
|
+
return value === "pi" || value === "builtin-pi" || value === "codex" || value === "claude";
|
|
16
|
+
}
|
|
17
|
+
function isAdapterRuntime(value) {
|
|
18
|
+
return value === "pi" || value === "codex" || value === "claude";
|
|
19
|
+
}
|
|
20
|
+
/** Project stored adapter + distribution to the user-facing sibling id. Legacy `runtime=pi` without distribution is `pi`, never builtin. */
|
|
21
|
+
function runtimeOptionOf(input) {
|
|
22
|
+
const { runtime, piDistribution } = input;
|
|
23
|
+
if (runtime === "codex" || runtime === "claude" || runtime === "builtin-pi")
|
|
24
|
+
return runtime;
|
|
25
|
+
if (runtime === "pi")
|
|
26
|
+
return piDistribution === "builtin" ? "builtin-pi" : "pi";
|
|
27
|
+
return runtime;
|
|
28
|
+
}
|
|
29
|
+
function toUserRuntime(runtime, piDistribution) {
|
|
30
|
+
return runtimeOptionOf({ runtime, piDistribution });
|
|
31
|
+
}
|
|
32
|
+
/** Persist a user-facing sibling as adapter id + distribution. Adapter id stays `pi` for both Pi siblings. */
|
|
33
|
+
function runtimeOptionTarget(option) {
|
|
34
|
+
if (!isUserRuntime(option))
|
|
35
|
+
throw new Error(`未知 runtime:${option}`);
|
|
36
|
+
if (option === "builtin-pi")
|
|
37
|
+
return { runtime: "pi", piDistribution: "builtin" };
|
|
38
|
+
if (option === "pi")
|
|
39
|
+
return { runtime: "pi", piDistribution: "external" };
|
|
40
|
+
return { runtime: option };
|
|
41
|
+
}
|
|
42
|
+
function fromUserRuntime(userRuntime) {
|
|
43
|
+
return runtimeOptionTarget(userRuntime);
|
|
44
|
+
}
|
|
45
|
+
function piCatalogDistributionForUserRuntime(userRuntime) {
|
|
46
|
+
if (userRuntime === "builtin-pi")
|
|
47
|
+
return "builtin";
|
|
48
|
+
if (userRuntime === "pi")
|
|
49
|
+
return "external";
|
|
50
|
+
throw new Error(`非 Pi sibling runtime:${userRuntime}`);
|
|
51
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const RUNTIME_OPTIONS = ["codex", "claude", "pi", "builtin-pi"];
|
|
2
|
+
export const USER_RUNTIMES = RUNTIME_OPTIONS;
|
|
3
|
+
export const ADAPTER_RUNTIMES = ["pi", "codex", "claude"];
|
|
4
|
+
export function isUserRuntime(value) {
|
|
5
|
+
return value === "pi" || value === "builtin-pi" || value === "codex" || value === "claude";
|
|
6
|
+
}
|
|
7
|
+
export function isAdapterRuntime(value) {
|
|
8
|
+
return value === "pi" || value === "codex" || value === "claude";
|
|
9
|
+
}
|
|
10
|
+
/** Project stored adapter + distribution to the user-facing sibling id. Legacy `runtime=pi` without distribution is `pi`, never builtin. */
|
|
11
|
+
export function runtimeOptionOf(input) {
|
|
12
|
+
const { runtime, piDistribution } = input;
|
|
13
|
+
if (runtime === "codex" || runtime === "claude" || runtime === "builtin-pi")
|
|
14
|
+
return runtime;
|
|
15
|
+
if (runtime === "pi")
|
|
16
|
+
return piDistribution === "builtin" ? "builtin-pi" : "pi";
|
|
17
|
+
return runtime;
|
|
18
|
+
}
|
|
19
|
+
export function toUserRuntime(runtime, piDistribution) {
|
|
20
|
+
return runtimeOptionOf({ runtime, piDistribution });
|
|
21
|
+
}
|
|
22
|
+
/** Persist a user-facing sibling as adapter id + distribution. Adapter id stays `pi` for both Pi siblings. */
|
|
23
|
+
export function runtimeOptionTarget(option) {
|
|
24
|
+
if (!isUserRuntime(option))
|
|
25
|
+
throw new Error(`未知 runtime:${option}`);
|
|
26
|
+
if (option === "builtin-pi")
|
|
27
|
+
return { runtime: "pi", piDistribution: "builtin" };
|
|
28
|
+
if (option === "pi")
|
|
29
|
+
return { runtime: "pi", piDistribution: "external" };
|
|
30
|
+
return { runtime: option };
|
|
31
|
+
}
|
|
32
|
+
export function fromUserRuntime(userRuntime) {
|
|
33
|
+
return runtimeOptionTarget(userRuntime);
|
|
34
|
+
}
|
|
35
|
+
export function piCatalogDistributionForUserRuntime(userRuntime) {
|
|
36
|
+
if (userRuntime === "builtin-pi")
|
|
37
|
+
return "builtin";
|
|
38
|
+
if (userRuntime === "pi")
|
|
39
|
+
return "external";
|
|
40
|
+
throw new Error(`非 Pi sibling runtime:${userRuntime}`);
|
|
41
|
+
}
|
|
@@ -719,10 +719,10 @@ Lark 必须用 --tenant lark:授权二维码是官方 /page/cli(begin 仍走
|
|
|
719
719
|
const setupModel = flag("--model");
|
|
720
720
|
if (piDistributionFlag === "builtin") {
|
|
721
721
|
if (!setupProvider && !setupBaseUrl) {
|
|
722
|
-
throw new Error(`builtin-pi 需要 --provider <id>(${PI_PROVIDER_PRESETS.map((p) => p.id).join("|")}|custom)或 --base-url;或改用 --runtime
|
|
722
|
+
throw new Error(`builtin-pi 需要 --provider <id>(${PI_PROVIDER_PRESETS.map((p) => p.id).join("|")}|custom)或 --base-url;或改用 --runtime pi 使用已有 pi 环境`);
|
|
723
723
|
}
|
|
724
724
|
if (!setupApiKey)
|
|
725
|
-
throw new Error("builtin-pi 需要 --api-key;或改用 --runtime
|
|
725
|
+
throw new Error("builtin-pi 需要 --api-key;或改用 --runtime pi 使用已有 pi 登录");
|
|
726
726
|
// 自定义网关(--base-url)走 custom 预设:pi 会把 baseUrl 作为 provider 端点写入配置,
|
|
727
727
|
// 而不是用预设厂商目录(否则会打到预设默认地址,如 api.openai.com)。
|
|
728
728
|
const presetId = setupBaseUrl ? "custom" : (setupProvider ?? "custom");
|
|
@@ -763,7 +763,7 @@ Lark 必须用 --tenant lark:授权二维码是官方 /page/cli(begin 仍走
|
|
|
763
763
|
say(`[setup 2/5] ✓ 内置 Pi provider 已配置(${presetId}${setupBaseUrl ? " / custom" : ""})`);
|
|
764
764
|
}
|
|
765
765
|
else if (piDistributionFlag === "external" && (setupProvider || setupApiKey || setupBaseUrl)) {
|
|
766
|
-
throw new Error("
|
|
766
|
+
throw new Error("pi 使用已有 pi 环境与登录,不接受 --provider/--api-key/--base-url");
|
|
767
767
|
}
|
|
768
768
|
const stagedBotFile = path.join(botsDir, `.${id}.${process.pid}.tmp`);
|
|
769
769
|
try {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import { stdin, stdout } from "node:process";
|
|
3
|
+
import { toUserRuntime } from "../runtime/user-runtime.mjs";
|
|
3
4
|
import { PI_PROVIDER_PRESETS, resolveBuiltinPiProviderSetupSelection, } from "../runtime/pi-provider-config.mjs";
|
|
4
5
|
function number(value, fallback, max) {
|
|
5
6
|
const parsed = value.trim() ? Number(value.trim()) : fallback;
|
|
@@ -54,20 +55,20 @@ export async function collectBuiltinPiChoice(questioner, services) {
|
|
|
54
55
|
}
|
|
55
56
|
export async function collectSetupAgentChoice(questioner, prior, services) {
|
|
56
57
|
if (prior) {
|
|
57
|
-
const keep = (await questioner.ask(`已有 Agent:${prior.runtime}/${prior.model}。直接回车保留;输入 c 才修改:`)).trim().toLowerCase();
|
|
58
|
+
const keep = (await questioner.ask(`已有 Agent:${toUserRuntime(prior.runtime, prior.piDistribution)}/${prior.model}。直接回车保留;输入 c 才修改:`)).trim().toLowerCase();
|
|
58
59
|
if (!keep)
|
|
59
60
|
return null;
|
|
60
61
|
if (keep !== "c")
|
|
61
62
|
throw new Error("请输入 c 修改,或直接回车保留现有配置");
|
|
62
63
|
}
|
|
63
|
-
const runtimeChoice = number(await questioner.ask("选择 Agent:\n 1.
|
|
64
|
-
if (runtimeChoice === 2)
|
|
65
|
-
return { runtime: "codex" };
|
|
64
|
+
const runtimeChoice = number(await questioner.ask("选择 Agent:\n 1. builtin-pi(Larkin 捆绑,推荐)\n 2. pi(本机官方 pi CLI)\n 3. Codex\n 4. Claude Code\n> "), 1, 4);
|
|
66
65
|
if (runtimeChoice === 3)
|
|
66
|
+
return { runtime: "codex" };
|
|
67
|
+
if (runtimeChoice === 4)
|
|
67
68
|
return { runtime: "claude" };
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return
|
|
69
|
+
if (runtimeChoice === 2)
|
|
70
|
+
return { runtime: "pi", distribution: "external" };
|
|
71
|
+
return collectBuiltinPiChoice(questioner, services);
|
|
71
72
|
}
|
|
72
73
|
export async function recoverUnavailableExternalPi(initial, questioner, probe, report = () => { }, services) {
|
|
73
74
|
let choice = initial;
|
|
@@ -77,8 +78,8 @@ export async function recoverUnavailableExternalPi(initial, questioner, probe, r
|
|
|
77
78
|
const readiness = await probe();
|
|
78
79
|
if (readiness.state === "ready")
|
|
79
80
|
return choice;
|
|
80
|
-
report(`外置 Pi ${readiness.state}:${readiness.reason || "prerequisite unavailable"};${readiness.nextAction || "
|
|
81
|
-
const action = number(await questioner.ask("外置 Pi 当前不可用:\n 1.
|
|
81
|
+
report(`外置 Pi ${readiness.state}:${readiness.reason || "prerequisite unavailable"};${readiness.nextAction || "可切换到 builtin-pi"}`);
|
|
82
|
+
const action = number(await questioner.ask("外置 Pi 当前不可用:\n 1. 切换到 builtin-pi(推荐)\n 2. 重新选择 Agent\n 3. 取消 setup(保留原配置)\n> "), 1, 3);
|
|
82
83
|
if (action === 1)
|
|
83
84
|
return collectBuiltinPiChoice(questioner, services);
|
|
84
85
|
if (action === 3)
|
|
@@ -209,11 +209,11 @@ function formatExternalPiSetupFailure(error, options) {
|
|
|
209
209
|
const alternatives = catalogModelsFromError(error);
|
|
210
210
|
const parts = [];
|
|
211
211
|
if (/external Pi (?:auth\.json|models\.json|settings\.json) is required|external Pi profile|external Pi auth\.json has an unsafe mode|external Pi models\.json has an unsafe mode|external Pi settings\.json has an unsafe mode/.test(original)) {
|
|
212
|
-
parts.push("
|
|
212
|
+
parts.push("pi requires the official file-backed Pi profile containing auth.json (0600), models.json, and settings.json, normally after `pi` login and default-model selection. Environment-variable-only credentials are unsupported.");
|
|
213
213
|
}
|
|
214
214
|
if (original)
|
|
215
215
|
parts.push(original);
|
|
216
|
-
parts.push("Fix the official `pi` login and default model, then rerun `larkin setup --runtime
|
|
216
|
+
parts.push("Fix the official `pi` login and default model, then rerun `larkin setup --runtime pi`.");
|
|
217
217
|
if (options.agentExisted) {
|
|
218
218
|
parts.push("This Agent already exists; after a successful setup you can switch models with `larkin model <provider>/<model>`.");
|
|
219
219
|
}
|