@surething/cockpit 1.0.258 → 1.0.260
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/.next-prod/BUILD_ID +1 -1
- package/.next-prod/app-path-routes-manifest.json +2 -2
- package/.next-prod/build-manifest.json +2 -2
- package/.next-prod/cockpit-build.json +1 -1
- package/.next-prod/prerender-manifest.json +4 -4
- package/.next-prod/server/app/_global-error/page.js.nft.json +1 -1
- package/.next-prod/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/_global-error.html +1 -1
- package/.next-prod/server/app/_global-error.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found/page.js +1 -1
- package/.next-prod/server/app/_not-found/page.js.nft.json +1 -1
- package/.next-prod/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/_not-found.html +2 -2
- package/.next-prod/server/app/_not-found.rsc +3 -3
- package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/.next-prod/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next-prod/server/app/api/agent/test/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/bash/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/codex/route.js +1 -1
- package/.next-prod/server/app/api/chat/codex/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/deepseek/route.js +1 -1
- package/.next-prod/server/app/api/chat/deepseek/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/glm/route.js +1 -1
- package/.next-prod/server/app/api/chat/glm/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/kimi/route.js +1 -1
- package/.next-prod/server/app/api/chat/kimi/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/ollama/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/route.js +1 -1
- package/.next-prod/server/app/api/chat/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/stop/route.js +1 -1
- package/.next-prod/server/app/api/chat/stop/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/claude-stats/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/commands/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/comments/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/columns/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/export/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/schemas/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/deepseek/balance/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/deepseek/credentials/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/deepseek/models/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/dev/spans/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/extension/version/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/file/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/blame/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/clipboard/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/copy/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/delete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/expanded/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/index/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/init/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/paste/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/read/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/readdir/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/recent/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/save/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/search/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/stat/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/text/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/branch-diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/branches/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/commit-diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/commits/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/current-branch/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/discard/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/stage/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/status/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/unstage/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/worktree/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/glm/credentials/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/glm/models/route.js +1 -1
- package/.next-prod/server/app/api/glm/models/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/glm/usage/route.js +1 -1
- package/.next-prod/server/app/api/glm/usage/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/global-state/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/health/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/html-apps/[id]/route.js +1 -1
- package/.next-prod/server/app/api/html-apps/[id]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/html-apps/route.js +1 -1
- package/.next-prod/server/app/api/html-apps/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/load/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/save/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/shutdown/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/kimi/credentials/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/kimi/models/route.js +1 -1
- package/.next-prod/server/app/api/kimi/models/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/kimi/usage/route.js +1 -1
- package/.next-prod/server/app/api/kimi/usage/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/definition/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/hover/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/references/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/status/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/warmup/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/columns/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/export/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/schemas/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/schema/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/note/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/ollama/config/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/ollama/models/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/ollama/start/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/open-cursor/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/open-vscode/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/pick-folder/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/pinned-sessions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/project-settings/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/project-state/route.js +1 -1
- package/.next-prod/server/app/api/project-state/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/affected/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/callees/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/callers/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/coedit/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/context/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/file/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/file-functions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/impact/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/related/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/risk/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/search/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projects/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/prompts/config/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/push/public-key/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/push/subscribe/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/push/unsubscribe/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/command/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/delete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/get/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/keys/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/set/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/comments/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/replies/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/identify/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/order/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/share-info/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/users/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/scheduled-tasks/route.js +2 -2
- package/.next-prod/server/app/api/scheduled-tasks/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/services/config/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/services/scripts/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session/[sessionId]/fork/route.js +1 -1
- package/.next-prod/server/app/api/session/[sessionId]/fork/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session/[sessionId]/history/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session-by-path/route.js +1 -1
- package/.next-prod/server/app/api/session-by-path/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/projects/[encodedPath]/route.js +1 -1
- package/.next-prod/server/app/api/sessions/projects/[encodedPath]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/projects/route.js +1 -1
- package/.next-prod/server/app/api/sessions/projects/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/settings/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/[id]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/content/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/snapshots/diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/snapshots/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/aliases/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/autocomplete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/bubble-order/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/env/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/history/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/version/route.js.nft.json +1 -1
- package/.next-prod/server/app/apps/[...path]/route.js +1 -1
- package/.next-prod/server/app/apps/[...path]/route.js.nft.json +1 -1
- package/.next-prod/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next-prod/server/app/m/page.js +1 -1
- package/.next-prod/server/app/m/page.js.nft.json +1 -1
- package/.next-prod/server/app/m/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/manifest.webmanifest/route.js.nft.json +1 -1
- package/.next-prod/server/app/page.js +1 -1
- package/.next-prod/server/app/page.js.nft.json +1 -1
- package/.next-prod/server/app/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/project/page.js +1 -1
- package/.next-prod/server/app/project/page.js.nft.json +1 -1
- package/.next-prod/server/app/project/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/review/[id]/page.js +1 -1
- package/.next-prod/server/app/review/[id]/page.js.nft.json +1 -1
- package/.next-prod/server/app/review/[id]/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app-paths-manifest.json +2 -2
- package/.next-prod/server/chunks/151.js +1 -0
- package/.next-prod/server/chunks/3286.js +448 -294
- package/.next-prod/server/chunks/{8557.js → 3641.js} +5 -5
- package/.next-prod/server/chunks/5750.js +16 -9
- package/.next-prod/server/chunks/6495.js +1 -1
- package/.next-prod/server/chunks/6548.js +1 -1
- package/.next-prod/server/chunks/816.js +2 -0
- package/.next-prod/server/chunks/841.js +12 -10
- package/.next-prod/server/chunks/9352.js +4 -4
- package/.next-prod/server/chunks/9830.js +1 -1
- package/.next-prod/server/middleware-build-manifest.js +1 -1
- package/.next-prod/server/pages/404.html +2 -2
- package/.next-prod/server/pages/500.html +1 -1
- package/.next-prod/server/server-reference-manifest.json +1 -1
- package/.next-prod/static/chunks/1157-164bcf046a0a9b15.js +32 -0
- package/.next-prod/static/chunks/1372-d16d68b1b03a345a.js +15 -0
- package/.next-prod/static/chunks/{9592-a657fb9eb995b64d.js → 4333-9e6709e96d3ec973.js} +7 -7
- package/.next-prod/static/chunks/{225-75b2e22f496b56a1.js → 6160-958532439d324185.js} +2 -2
- package/.next-prod/static/chunks/6784-09dffdb0e8ca7fb3.js +381 -0
- package/.next-prod/static/chunks/app/{layout-bec1cf218bc88d81.js → layout-ab52fc1a83af968b.js} +1 -1
- package/.next-prod/static/chunks/app/m/page-7dcf56a65528216f.js +1 -0
- package/.next-prod/static/chunks/app/{page-5e6dbc9731415743.js → page-5de8b6584e328a6f.js} +1 -1
- package/.next-prod/static/chunks/app/project/{page-5e6dbc9731415743.js → page-5de8b6584e328a6f.js} +1 -1
- package/.next-prod/static/css/83c134c954630942.css +1 -0
- package/.next-prod/trace +14 -14
- package/.next-prod/trace-build +1 -1
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/apps/devkit/app.jsx +728 -0
- package/apps/devkit/index.html +178 -0
- package/apps/devkit/md5.js +98 -0
- package/dist/{chunk-L5TWVHYB.mjs → chunk-B5LXMESP.mjs} +19 -9
- package/dist/{chunk-YEZ3U3F2.mjs → chunk-CLPJYIFC.mjs} +1 -1
- package/dist/{chunk-RSRPFWNL.mjs → chunk-TU3GL2HK.mjs} +1 -27
- package/dist/httpApi.mjs +2 -2
- package/dist/scheduledTasks.mjs +692 -483
- package/dist/{server-UZPDELUK.mjs → server-2UJDVZ32.mjs} +2 -2
- package/dist/wsServer.mjs +5 -4
- package/package.json +1 -1
- package/public/agent-icons/claude.svg +1 -0
- package/public/agent-icons/codex.svg +1 -0
- package/public/agent-icons/deepseek.svg +1 -0
- package/public/agent-icons/glm.svg +1 -0
- package/public/agent-icons/kimi.svg +1 -0
- package/public/agent-icons/ollama.svg +1 -0
- package/.next-prod/static/chunks/1372-cc9afa045dc4eb5e.js +0 -15
- package/.next-prod/static/chunks/6784-500e233d3e8f6b41.js +0 -381
- package/.next-prod/static/chunks/7482-8478af06d5aa0972.js +0 -25
- package/.next-prod/static/chunks/app/m/page-aff83d3f5f4047c5.js +0 -1
- package/.next-prod/static/css/2334aed02a409f57.css +0 -1
- /package/.next-prod/static/{ZFNy0XuBWptMqEQGi7GTy → 4Jgc-wu2w_CC8iS5G1QZf}/_buildManifest.js +0 -0
- /package/.next-prod/static/{ZFNy0XuBWptMqEQGi7GTy → 4Jgc-wu2w_CC8iS5G1QZf}/_ssgManifest.js +0 -0
package/dist/scheduledTasks.mjs
CHANGED
|
@@ -14,18 +14,15 @@ import {
|
|
|
14
14
|
stashCodexRollout,
|
|
15
15
|
stashTranscript,
|
|
16
16
|
updateGlobalState
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-B5LXMESP.mjs";
|
|
18
18
|
import {
|
|
19
19
|
AgentError,
|
|
20
20
|
AppRuntime,
|
|
21
21
|
COCKPIT_DIR,
|
|
22
22
|
DEEPSEEK_CREDENTIALS_FILE,
|
|
23
|
-
DEEPSEEK_DIR,
|
|
24
23
|
Effect_exports,
|
|
25
24
|
GLM_CREDENTIALS_FILE,
|
|
26
|
-
GLM_DIR,
|
|
27
25
|
KIMI_CREDENTIALS_FILE,
|
|
28
|
-
KIMI_DIR,
|
|
29
26
|
SCHEDULED_TASKS_FILE,
|
|
30
27
|
SETTINGS_FILE,
|
|
31
28
|
SKILLS_FILE,
|
|
@@ -37,11 +34,8 @@ import {
|
|
|
37
34
|
getBuiltinSessionsRoot,
|
|
38
35
|
getClaudeSessionPath,
|
|
39
36
|
getDeepseekBuiltinSessionPath,
|
|
40
|
-
getDeepseekSessionPath,
|
|
41
37
|
getGlmBuiltinSessionPath,
|
|
42
|
-
getGlmSessionPath,
|
|
43
38
|
getKimiBuiltinSessionPath,
|
|
44
|
-
getKimiSessionPath,
|
|
45
39
|
getOllamaSessionPath,
|
|
46
40
|
getSessionFilePath,
|
|
47
41
|
mutateJsonFile,
|
|
@@ -51,7 +45,7 @@ import {
|
|
|
51
45
|
settlePendingRecord,
|
|
52
46
|
withFileLock,
|
|
53
47
|
writeJsonFile
|
|
54
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-TU3GL2HK.mjs";
|
|
55
49
|
import {
|
|
56
50
|
__export
|
|
57
51
|
} from "./chunk-7P6ASYW6.mjs";
|
|
@@ -64,6 +58,17 @@ function defaultRegionForLanguage(language) {
|
|
|
64
58
|
return language === "en" ? "global" : "cn";
|
|
65
59
|
}
|
|
66
60
|
|
|
61
|
+
// packages/shared/utils/src/outputProgress.ts
|
|
62
|
+
function estimateOutputUnits(text) {
|
|
63
|
+
let cjk = 0;
|
|
64
|
+
let other = 0;
|
|
65
|
+
for (const ch of text) {
|
|
66
|
+
if (/[\u3040-\u30ff\u3400-\u9fff\uf900-\ufaff\u{20000}-\u{2a6df}\uac00-\ud7af]/u.test(ch)) cjk += 1;
|
|
67
|
+
else other += 1;
|
|
68
|
+
}
|
|
69
|
+
return Math.max(1, Math.ceil(cjk + other / 4));
|
|
70
|
+
}
|
|
71
|
+
|
|
67
72
|
// packages/feature/agent/src/shared/toolMutation.ts
|
|
68
73
|
var READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
69
74
|
"Read",
|
|
@@ -535,7 +540,7 @@ description: "\u5B8C\u6574\u4EE3\u7801\u5BA1\u67E5:\u9759\u6001 + \u52A8\u6001\u
|
|
|
535
540
|
|
|
536
541
|
subagent prompt \u6A21\u677F:\`\u8BFB\u672C skill (cr/SKILL.md),\u53EA\u5BF9 <\u4F60\u90A3\u5757(Part A \u5168\u90E8 / \u67D0\u4E2A\u52A8\u6001\u5207\u7247)> \u5E94\u7528\u5BF9\u5E94 Part,\u81EA\u884C triage,\u6309\u7EDF\u4E00\u683C\u5F0F\u8F93\u51FA findings\`\u3002
|
|
537
542
|
|
|
538
|
-
**Synthesis(\u4E3B\u4F1A\u8BDD,\u53EA\u6574\u7406\u4E0D\u91CD\u5224)**:\u6C47\u603B\u5404 subagent \u7684 findings \u2192 \u53BB\u91CD(\u540C\u6839\u56E0\u6807"\u5171\u72AF")\u2192 \u5F71\u54CD \xD7 \u6982\u7387\u6392\u5E8F \u2192 \u4E00\u4EFD\u62A5\u544A + \u68AF\u5EA6\u56FE\u3002**\u7981\u6B62\u7528\u4E3B\u4F1A\u8BDD\u7684\u5F00\u53D1\u4E0A\u4E0B\u6587\u7ED9\u4EFB\u4F55 finding \u6D17\u767D / \u964D\u7EA7**\u2014\u2014subagent \u600E\u4E48\u5224\u5C31\u600E\u4E48\u6536\u3002\u8981\u8D28\u7591\u67D0\u6761,**\u91CD\u65B0 spawn \u4E00\u4E2A\u5E72\u51C0 subagent \u590D\u6838**,\u800C\u4E0D\u662F\u4E3B\u4F1A\u8BDD\u81EA\u5DF1\u62CD\u3002
|
|
543
|
+
**Synthesis(\u4E3B\u4F1A\u8BDD,\u53EA\u6574\u7406\u4E0D\u91CD\u5224)**:\u6C47\u603B\u5404 subagent \u7684 findings \u2192 \u53BB\u91CD(\u540C\u6839\u56E0\u6807"\u5171\u72AF")\u2192 \u5F71\u54CD \xD7 \u6982\u7387\u6392\u5E8F \u2192 \u4E00\u4EFD\u62A5\u544A + \u68AF\u5EA6\u56FE\u3002**\u7981\u6B62\u7528\u4E3B\u4F1A\u8BDD\u7684\u5F00\u53D1\u4E0A\u4E0B\u6587\u7ED9\u4EFB\u4F55 finding \u6D17\u767D / \u964D\u7EA7**\u2014\u2014subagent \u600E\u4E48\u5224\u5C31\u600E\u4E48\u6536\u3002\u8981\u8D28\u7591\u67D0\u6761,**\u91CD\u65B0 spawn \u4E00\u4E2A\u5E72\u51C0 subagent \u590D\u6838**,\u800C\u4E0D\u662F\u4E3B\u4F1A\u8BDD\u81EA\u5DF1\u62CD\u3002\u4E3B\u4F1A\u8BDD\u53EF\u4EE5\u628A\u7ED3\u679C\u6574\u7406\u6210\u77ED\u8868,\u4F46**\u5C55\u5F00\u8BE6\u60C5\u5FC5\u987B\u4FDD\u7559 subagent \u7684\u5173\u952E\u8BC1\u636E\u5BC6\u5EA6**:\u52A8\u6001 finding \u7684\u72B6\u6001\u56FE / \u65F6\u95F4\u7EBF / \u53D8\u66F4\u8F68\u8FF9 / \u53CD\u4F8B\u8DEF\u5F84,\u9759\u6001 finding \u7684\u4E09\u89D2\u6821\u9A8C\u8BC1\u636E,\u90FD\u8981\u642C\u5230\u5BF9\u5E94\u7F16\u53F7\u8BE6\u60C5\u91CC\u3002\u5141\u8BB8\u53BB\u91CD\u548C\u5220\u6389\u91CD\u590D\u63AA\u8F9E,\u4E0D\u5141\u8BB8\u628A subagent \u7684\u8BE6\u7EC6\u6A21\u578B\u538B\u6210\u4E00\u53E5\u6458\u8981\u3002
|
|
539
544
|
|
|
540
545
|
> \u53D6\u820D:\u9ED8\u8BA4 2-subagent \u65E2\u9694\u79BB\u5F00\u53D1\u6C61\u67D3\u3001\u53C8\u6D88\u6389\u9759\u6001\u644A\u8584\u52A8\u6001;hard \u7684 per-slice fan-out \u662F\u989D\u5916\u6DF1\u5EA6(\u5B9E\u6D4B\u628A image/video \u6316\u5230 \u{1F534} + \u591A subagent \u72EC\u7ACB\u6536\u655B\u63D0\u7F6E\u4FE1\u5EA6),\u8D35\u4E14\u6162,\u7531\u7528\u6237\u663E\u5F0F\u9009\u7528(\u4E0D\u81EA\u52A8\u5347\u6863)\u3002
|
|
541
546
|
|
|
@@ -625,22 +630,31 @@ correctness \u8FD9\u4E2A"\u672A\u77E5\u70B9",\u9760 **\u610F\u56FE / \u8F93\u516
|
|
|
625
630
|
|
|
626
631
|
---
|
|
627
632
|
|
|
628
|
-
## \u4EA7\u51FA findings(\
|
|
633
|
+
## \u4EA7\u51FA findings(\u7B80\u8868 + \u8BE6\u60C5)
|
|
634
|
+
|
|
635
|
+
\u4E25\u91CD\u5EA6 = **\u5F71\u54CD \xD7 \u6982\u7387**,\u76F4\u63A5\u7ED9\u4E58\u79EF\u7ED3\u8BBA,\u522B\u8BA9\u8BFB\u8005\u81EA\u5DF1\u6362\u7B97\u3002\u4E24\u8DEF findings \u5408\u5E76\u53BB\u91CD\u540E,\u5148\u8F93\u51FA\u4E00\u5F20**\u77ED\u5217\u8868\u683C**\u7528\u4E8E\u5FEB\u901F\u626B\u63CF,\u518D\u6309\u7F16\u53F7\u5C55\u5F00\u8BC1\u636E\u3002\u4E0D\u8981\u628A\u957F\u8BC1\u636E\u585E\u8FDB\u8868\u683C cell,\u5426\u5219\u804A\u5929\u7A97\u53E3\u4F1A\u628A\u5217\u6324\u6210\u96BE\u8BFB\u7684\u7A84\u5217\u3002
|
|
636
|
+
|
|
637
|
+
| # | \u4E25\u91CD\u5EA6 | \u4F4D\u7F6E | \u95EE\u9898\u540E\u679C | \u5904\u7406\u65B9\u5411 |
|
|
638
|
+
|---|---|---|---|---|
|
|
639
|
+
| 1 | \u{1F534}/\u{1F7E1}/\u26AA | \`file:line\` | \u4E0D\u4FEE\u4F1A\u53D1\u751F\u4EC0\u4E48(\u4E00\u53E5\u5927\u767D\u8BDD\u540E\u679C,\u4E0D\u5E26\u672F\u8BED) | \u4E00\u53E5\u8BDD\u8BF4\u660E\u4FEE\u590D\u65B9\u5411 |
|
|
629
640
|
|
|
630
|
-
\
|
|
641
|
+
\u7136\u540E\u6309\u8868\u683C\u7F16\u53F7\u5C55\u5F00:
|
|
631
642
|
|
|
632
643
|
\`\`\`
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
644
|
+
### #1 <\u4E00\u53E5\u8BDD\u540E\u679C>
|
|
645
|
+
\u5F71\u54CD: \u574F\u5230\u4EC0\u4E48\u7A0B\u5EA6 + \u574F\u7ED9\u8C01(\u5177\u4F53:\u54EA\u7C7B\u7528\u6237 / \u79DF\u6237 / \u8C03\u7528\u65B9,\u800C\u975E\u6CDB\u6307"\u7528\u6237")
|
|
646
|
+
\u6982\u7387: \u591A\u5927\u6982\u7387\u53D1\u751F + \u53D6\u51B3\u4E8E\u4EC0\u4E48;\u82E5\u5F85\u9A8C\u8BC1,\u5199\u660E\u5982\u4F55\u5347\u964D\u6863
|
|
647
|
+
\u8BC1\u636E: \u9759\u6001 \u2192 \u4E09\u89D2\u6821\u9A8C\u91CC\u54EA\u4E2A\u53C2\u7167\u5BF9\u4E0D\u4E0A(\u610F\u56FE / \u8F93\u5165\u57DF / \u5468\u906D);\u52A8\u6001 \u2192 \u4FDD\u7559 subagent \u7ED9\u51FA\u7684\u72B6\u6001\u56FE / \u65F6\u95F4\u7EBF / \u53D8\u66F4\u8F68\u8FF9 / \u53CD\u4F8B\u8DEF\u5F84,\u4E0D\u8981\u53EA\u6458\u8981\u6210\u4E00\u53E5
|
|
648
|
+
\u6B63\u89E3: \u5E94\u8BE5\u662F\u4EC0\u4E48 / \u88AB\u7834\u574F\u7684\u4E0D\u53D8\u91CF(\u672F\u8BED\u653E\u8FD9\u91CC)
|
|
638
649
|
\`\`\`
|
|
639
650
|
|
|
640
|
-
- \u{1F534} \u9AD8\u6982\u7387 \xD7 \u5927\u5F71\u54CD(\u5361\u53D1\u5E03) | \u{1F7E1} \u8BE5\u4FEE\u4F46\u4E0D\u6D41\u8840 | \u26AA \u987A\u624B\u3002\u964D\u5E8F\u8F93\u51FA\u3002
|
|
641
|
-
- \
|
|
642
|
-
- \
|
|
643
|
-
- \
|
|
651
|
+
- \u{1F534} \u9AD8\u6982\u7387 \xD7 \u5927\u5F71\u54CD(\u5361\u53D1\u5E03) | \u{1F7E1} \u8BE5\u4FEE\u4F46\u4E0D\u6D41\u8840 | \u26AA \u987A\u624B\u3002\u77ED\u5217\u8868\u683C\u6309\u4E25\u91CD\u5EA6\u964D\u5E8F\u8F93\u51FA\u3002
|
|
652
|
+
- \`\u95EE\u9898\u540E\u679C\` \u53EA\u5199**\u540E\u679C**,\u4E0D\u5199\u6280\u672F\u539F\u56E0(\u539F\u56E0\u653E\u5C55\u5F00\u8BE6\u60C5\u7684 \`\u8BC1\u636E\` / \`\u6B63\u89E3\`)\u3002
|
|
653
|
+
- \`\u4F4D\u7F6E\` \u7528\u53EF\u5B9A\u4F4D\u7684 \`file:line\`;\u5982\u679C\u662F\u5408\u5E76\u540E\u7684\u5171\u72AF finding,\u8868\u683C\u7528\u6700\u6838\u5FC3\u4F4D\u7F6E,\u5728\u5C55\u5F00\u8BE6\u60C5\u7684 \`\u8BC1\u636E\` \u4E2D\u8865\u5145\u5176\u5B83\u4F4D\u7F6E\u3002
|
|
654
|
+
- \`\u5904\u7406\u65B9\u5411\` \u5728\u77ED\u8868\u91CC\u53EA\u5199\u4E00\u53E5,\u5B8C\u6574 \`\u6B63\u89E3\` \u653E\u5C55\u5F00\u8BE6\u60C5\u3002
|
|
655
|
+
- \u5C55\u5F00\u8BE6\u60C5\u662F\u4FE1\u606F\u5B8C\u6574\u9762:\u5982\u679C subagent \u5DF2\u7ED9\u51FA\u52A8\u6001\u6A21\u578B\u3001\u65F6\u95F4\u7EBF\u3001\u8DEF\u5F84\u7F16\u53F7\u6216\u5173\u952E\u4EE3\u7801\u4F4D\u7F6E,\u6700\u7EC8\u62A5\u544A\u5FC5\u987B\u4FDD\u7559\u8FD9\u4E9B\u7EC6\u8282;\u77ED\u8868\u53EF\u4EE5\u7B80,\u8BE6\u60C5\u4E0D\u80FD\u628A\u8BC1\u636E\u538B\u6CA1\u3002
|
|
656
|
+
- \u591A\u6761\u540C\u6839\u56E0 \u2192 \u5728 \`\u95EE\u9898\u540E\u679C\` \u6216\u5C55\u5F00\u8BE6\u60C5\u7684 \`\u8BC1\u636E\` \u6807 **"\u5171\u72AF"** \u5E76\u5408\u5E76\u6210\u4E00\u6761,\u522B\u540C\u4E00\u95EE\u9898\u62A5\u4E24\u904D(\u5C24\u5176\u9759\u6001/\u52A8\u6001\u5BF9\u540C\u4E00\u884C\u5404\u770B\u4E00\u9762\u65F6)\u3002
|
|
657
|
+
- \u8868\u683C\u540E\u53EA\u5C55\u5F00\u6BCF\u6761 finding \u4E00\u6B21;\u4E0D\u8981\u518D\u91CD\u590D\u4E00\u5F20 7 \u5217\u957F\u8868\u3002
|
|
644
658
|
- \u6536\u5C3E\u7ED9\u4E00\u5F20 **\u5F71\u54CD \xD7 \u6982\u7387** \u68AF\u5EA6\u56FE:
|
|
645
659
|
|
|
646
660
|
\`\`\`
|
|
@@ -681,7 +695,7 @@ Three tiers by scale (**all enter at least one clean subagent**). **You pick bet
|
|
|
681
695
|
|
|
682
696
|
Subagent prompt template: \`Read this skill (cr/SKILL.md); apply only the matching Part to <your chunk (all of Part A / one dynamic slice)>, triage it yourself, and output findings in the unified format\`.
|
|
683
697
|
|
|
684
|
-
**Synthesis (main session, organize only \u2014 never re-judge)**: gather each subagent's findings \u2192 dedup (mark same-root-cause as "accomplices") \u2192 sort by impact \xD7 probability \u2192 one report + gradient chart. **It is forbidden to use the main session's dev context to whitewash / downgrade any finding** \u2014 take what the subagent judged as-is. To dispute one, **spawn a fresh clean subagent to re-check**, not the main session deciding on its own.
|
|
698
|
+
**Synthesis (main session, organize only \u2014 never re-judge)**: gather each subagent's findings \u2192 dedup (mark same-root-cause as "accomplices") \u2192 sort by impact \xD7 probability \u2192 one report + gradient chart. **It is forbidden to use the main session's dev context to whitewash / downgrade any finding** \u2014 take what the subagent judged as-is. To dispute one, **spawn a fresh clean subagent to re-check**, not the main session deciding on its own. The main session may organize the result into a short summary table, but the **expanded details must preserve the subagent's key evidence density**: for dynamic findings, carry over the state diagram / timeline / change trajectory / counterexample path; for static findings, carry over the triangulation evidence. Deduplication and wording cleanup are allowed; compressing a detailed subagent model into a one-line summary is not.
|
|
685
699
|
|
|
686
700
|
> Trade-off: the default 2-subagent both isolates dev pollution and removes static-dilutes-dynamic; hard's per-slice fan-out is extra depth (in practice it dug image/video up to \u{1F534}, and multiple independently-converging subagents raised confidence), expensive and slow, selected explicitly by the user (never auto-escalated).
|
|
687
701
|
|
|
@@ -771,22 +785,31 @@ These two diagrams are the evidence \u2014 many bugs (a 6-second gap, a key crea
|
|
|
771
785
|
|
|
772
786
|
---
|
|
773
787
|
|
|
774
|
-
## Produce findings (
|
|
788
|
+
## Produce findings (summary table + details)
|
|
775
789
|
|
|
776
|
-
Severity = **impact \xD7 probability**; give the product conclusion directly, don't make the reader do the math. Merge and dedup findings from both tracks,
|
|
790
|
+
Severity = **impact \xD7 probability**; give the product conclusion directly, don't make the reader do the math. Merge and dedup findings from both tracks. First output a **short summary table** for scanning, then expand each finding by number. Do not put long evidence into table cells; chat windows squeeze wide Markdown tables into unreadable narrow columns.
|
|
791
|
+
|
|
792
|
+
| # | Severity | Location | Consequence | Direction |
|
|
793
|
+
|---|---|---|---|---|
|
|
794
|
+
| 1 | \u{1F534}/\u{1F7E1}/\u26AA | \`file:line\` | What happens if unfixed (one plain-language consequence, no jargon) | One-sentence repair direction |
|
|
795
|
+
|
|
796
|
+
Then expand by table number:
|
|
777
797
|
|
|
778
798
|
\`\`\`
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
799
|
+
### #1 <one-line consequence>
|
|
800
|
+
Impact: how bad + to whom (specific: which users / tenants / callers, not a vague "users")
|
|
801
|
+
Probability: how likely + what it depends on; if uncertain, say how to raise/lower the tier
|
|
802
|
+
Evidence: static \u2192 which triangulation reference fails (intent / input domain / surroundings); dynamic \u2192 preserve the subagent's state diagram / timeline / change trajectory / counterexample path, not a one-line summary
|
|
803
|
+
Fix: what it should be / the broken invariant (jargon goes here)
|
|
784
804
|
\`\`\`
|
|
785
805
|
|
|
786
|
-
- \u{1F534} high probability \xD7 big impact (blocks release) | \u{1F7E1} should fix but not bleeding | \u26AA nice-to-have.
|
|
787
|
-
-
|
|
788
|
-
-
|
|
789
|
-
-
|
|
806
|
+
- \u{1F534} high probability \xD7 big impact (blocks release) | \u{1F7E1} should fix but not bleeding | \u26AA nice-to-have. Sort summary table rows in descending severity.
|
|
807
|
+
- \`Consequence\` states the **outcome**, not the technical cause (cause goes in the expanded \`Evidence\` / \`Fix\`).
|
|
808
|
+
- \`Location\` must be a precise \`file:line\`; for a merged accomplice finding, use the core location in the table and mention other locations in the expanded \`Evidence\`.
|
|
809
|
+
- Keep \`Direction\` in the summary table to one sentence; put the complete \`Fix\` in the expanded detail.
|
|
810
|
+
- Expanded details are the complete information surface: if a subagent provided a dynamic model, timeline, path labels, or key code locations, the final report must carry those details forward; the summary table may be short, the detail must not erase evidence.
|
|
811
|
+
- Multiple same-root-cause \u2192 mark as **"accomplices"** in \`Consequence\` or expanded \`Evidence\` and merge into one finding; don't report the same problem twice (especially when static/dynamic each see one side of the same line).
|
|
812
|
+
- Expand each finding only once after the table; don't also emit a 7-column long table.
|
|
790
813
|
- Close with an **impact \xD7 probability** gradient chart:
|
|
791
814
|
|
|
792
815
|
\`\`\`
|
|
@@ -1170,349 +1193,485 @@ Finally emit [Overall recap: end-to-end interaction verification + total deliver
|
|
|
1170
1193
|
// packages/feature/agent/src/server/lib/htmlPrompt.ts
|
|
1171
1194
|
var HTML_PROMPT_ZH = `---
|
|
1172
1195
|
name: html
|
|
1173
|
-
description: "\u751F\u6210\u53EF\u4EA4\u4E92\u7684
|
|
1174
|
-
argument-hint: "\u63CF\u8FF0\u4F60\u60F3\u8981\u7684
|
|
1196
|
+
description: "\u751F\u6210\u53EF\u4EA4\u4E92\u7684\u672C\u5730 React \u5E94\u7528\uFF1A\u5185\u7F6E cockpit bash SDK\uFF0C\u6267\u884C\u547D\u4EE4\u53D6/\u6539\u6570\u636E"
|
|
1197
|
+
argument-hint: "\u63CF\u8FF0\u4F60\u60F3\u8981\u7684\u5E94\u7528"
|
|
1175
1198
|
---
|
|
1176
1199
|
|
|
1177
|
-
# \u751F\u6210 Cockpit
|
|
1200
|
+
# \u751F\u6210 Cockpit \u5E94\u7528\uFF08\u672C\u5730 React \u9875\u9762\uFF09
|
|
1178
1201
|
|
|
1179
|
-
\u4E00\u4E2A\
|
|
1180
|
-
\
|
|
1202
|
+
\u4E00\u4E2A**\u672C\u5730\u5E94\u7528**\uFF0C\u4E0D\u662F\u4E00\u6B21\u6027\u9875\u9762\uFF1A\u9884\u89C8\u65F6\u6CE8\u5165\u4E86\u5168\u5C40 \`window.cockpit\` SDK \u2014\u2014 \u672C\u8D28\u5C31\u662F Bash \u5DE5\u5177
|
|
1203
|
+
\u66B4\u9732\u7ED9\u9875\u9762\uFF0C\u6309\u94AE\u80FD \`curl\`\u3001\u8BFB\u5199\u6587\u4EF6\u3001\u8DD1\u811A\u672C\uFF1B\u6570\u636E\u843D\u76D8\u5728 app \u76EE\u5F55\u91CC\uFF0C\u91CD\u5F00\u5373\u7528\u3002
|
|
1181
1204
|
|
|
1182
|
-
|
|
1183
|
-
\u8868\u5355\uFF09\uFF1B\u53EA\u6709**\u6781\u7B80\u5355\u7684\u5355\u89C6\u56FE\u3001\u6216\u7EAF\u9759\u6001\u4E00\u6B21\u6027\u9875**\u624D\u9000\u56DE\u5355\u6587\u4EF6\u5185\u8054 HTML\u3002\u4E24\u79CD\u6A21\u5F0F\u5171\u7528\u4E0B\u9762
|
|
1184
|
-
\u8FD9\u5957\u5951\u7EA6\u3002
|
|
1205
|
+
\u96F6\u6784\u5EFA\uFF1AReact / Babel \u4ECE\u540C\u6E90\u7684 \`/html-lib\` \u52A0\u8F7D\uFF0C\u4E0D\u6253\u5305\u3001\u4E0D\u88C5\u4F9D\u8D56\u3001\u79BB\u7EBF\u53EF\u7528\u3002
|
|
1185
1206
|
|
|
1186
|
-
## \u6D41\u7A0B\uFF1A\u5148\u5BF9\u9F50\uFF0C\u786E\u8BA4\u540E\u518D\u5199\uFF08\u91CD\u8981\uFF09
|
|
1207
|
+
## \u4E00\u3001\u6D41\u7A0B\uFF1A\u5148\u5BF9\u9F50\uFF0C\u786E\u8BA4\u540E\u518D\u5199\uFF08\u91CD\u8981\uFF09
|
|
1187
1208
|
|
|
1188
1209
|
**\u522B\u4E00\u4E0A\u6765\u5C31\u5199\u6587\u4EF6**\uFF0C\u5206\u4E09\u6B65\uFF1A
|
|
1189
1210
|
|
|
1190
|
-
1. **\u5BF9\u9F50\u9700\u6C42** \u2014\u2014 \u5148\u590D\u8FF0\u4F60\u5BF9\u8FD9\u4E2A
|
|
1191
|
-
\
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
\
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
## \u5951\u7EA6\uFF1Awindow.cockpit\uFF08\u4E24\u6A21\u5F0F\u5171\u7528\uFF0C\u53EA\u6B64\u4E00\u5904\uFF09
|
|
1211
|
+
1. **\u5BF9\u9F50\u9700\u6C42** \u2014\u2014 \u5148\u590D\u8FF0\u4F60\u5BF9\u8FD9\u4E2A\u5E94\u7528\u7684\u7406\u89E3\uFF08\u505A\u4EC0\u4E48\u3001\u6570\u636E\u4ECE\u54EA\u6765\u3001\u5173\u952E\u529F\u80FD\u4E0E\u4EA4\u4E92\uFF09\u3002\u4EC5\u5F53\u9700\u6C42\u6709
|
|
1212
|
+
\u6B67\u4E49\u6216\u5B58\u5728\u591A\u79CD\u5408\u7406\u89E3\u8BFB\u65F6\u624D\u56DE\u95EE\uFF1B\u5DF2\u7ECF\u6E05\u695A\u5C31\u76F4\u63A5\u7ED9\u4E00\u6BB5\u7B80\u77ED\u7684\u7406\u89E3\u6458\u8981\uFF0C\u4E0D\u5FC5\u9010\u6761\u8FFD\u95EE\u3002
|
|
1213
|
+
2. **\u544A\u77E5\u5B58\u653E\u76EE\u5F55** \u2014\u2014 \u5B9A\u51FA \`<\u76EE\u5F55>/<name>/\`\uFF0C\u660E\u786E\u544A\u8BC9\u7528\u6237\u51C6\u5907\u5B58\u54EA\u3001\u53EB\u4EC0\u4E48\u540D\uFF1B\u7528\u6237\u53EF\u5426\u51B3\u6216\u6539\uFF1A
|
|
1214
|
+
- \`<name>\` **\u4E00\u5F8B\u7531\u4F60\u6309\u9700\u6C42\u8D77\u540D**\uFF08\u77ED\u6A2A\u7EBF\u5C0F\u5199\uFF0C\u4E0E \`cockpit-name\` \u4E00\u81F4\uFF09\uFF0C\u7528\u6237\u53EA\u63CF\u8FF0\u9700\u6C42\u3001
|
|
1215
|
+
\u4E0D\u8D1F\u8D23\u8D77\u540D\u3002
|
|
1216
|
+
- \u7528\u6237\u5728\u9700\u6C42\u91CC\u7ED9\u4E86\u76EE\u5F55 \u2192 \`<\u7528\u6237\u7ED9\u7684\u76EE\u5F55>/<name>/\`\uFF1B\u6CA1\u7ED9 \u2192 **\u5F53\u524D\u804A\u5929\u5DE5\u4F5C\u76EE\u5F55**\uFF08\u672C\u6B21\u4F1A\u8BDD\u7684
|
|
1217
|
+
cwd\uFF09\u4E0B\u7684 \`<name>/\`\u3002
|
|
1218
|
+
- \u4E00\u4E2A app \u7684\u6240\u6709\u6587\u4EF6\u90FD\u5728\u8FD9\u4E2A\u76EE\u5F55\u91CC\uFF0C\u6570\u636E\u6309\u7B2C\u56DB\u8282\u5206\u5230 \`cache/\` \`state/\` \`out/\`\u3002\u522B\u628A\u6587\u4EF6
|
|
1219
|
+
\u6563\u843D\u5230\u522B\u5904\uFF0C\u4E5F\u522B\u81EA\u9020 \`.cockpit-apps\` \u4E4B\u7C7B\u76EE\u5F55\u3002
|
|
1220
|
+
3. **\u786E\u8BA4\u540E\u624D\u5199** \u2014\u2014 \u7B49\u7528\u6237\u660E\u786E\u540C\u610F\uFF08\u786E\u8BA4 / \u5F00\u59CB / \u5199\u5427 / go \u7B49\u4EFB\u610F\u80AF\u5B9A\u8868\u793A\uFF09\u518D \`Write\` \u843D\u76D8\u3002
|
|
1221
|
+
\u786E\u8BA4\u4E4B\u524D\u53EA\u8BA8\u8BBA\u3001\u4E0D\u5199\u6587\u4EF6\u3002
|
|
1202
1222
|
|
|
1203
|
-
\
|
|
1204
|
-
|
|
1205
|
-
- \`cockpit.cwd: string\` \u2014\u2014 \u5F53\u524D\u6587\u4EF6\u6240\u5728\u76EE\u5F55\uFF0C\u76F8\u5BF9\u8DEF\u5F84\u547D\u4EE4\u9ED8\u8BA4\u5728\u6B64\u6267\u884C\u3002
|
|
1206
|
-
- \`cockpit.bash(command, opts?)\` \u2014\u2014 \u6267\u884C\u4E00\u6761 bash \u547D\u4EE4\uFF08\`command\` \u662F\u6574\u6761 shell \u4E32\uFF09\uFF1A
|
|
1207
|
-
- **\u524D\u53F0**\uFF08\u9ED8\u8BA4\uFF0C\u77ED/\u79BB\u6563\u547D\u4EE4\uFF09\u2192 \`Promise<{ stdout, stderr, exitCode }>\`\uFF0C\`await\` \u540E\u4E00\u6B21\u6027\u62FF\u5168\u90E8\u8F93\u51FA\u3002
|
|
1208
|
-
- **\u540E\u53F0**\uFF08\`opts.background: true\`\uFF0C\u957F/\u5B9E\u65F6\u547D\u4EE4\uFF09\u2192 \`{ kill() }\`\uFF0C\u7ECF\u56DE\u8C03\u6D41\u5F0F\u8F93\u51FA\uFF1A
|
|
1209
|
-
\`opts = { background:true, cwd?, onOutput?, onStderr?, onExit?, onError? }\`\u3002\u5927/\u6301\u7EED\u8F93\u51FA\u5FC5\u987B\u7528\u540E\u53F0\uFF08\u524D\u53F0\u5168\u7F13\u5B58\u5728\u5185\u5B58\uFF09\u3002
|
|
1210
|
-
|
|
1211
|
-
\u5199\u8FD9\u7C7B\u9875\u9762\u5FC5\u987B\u5B88\u7684\u51E0\u6761\uFF1A
|
|
1212
|
-
|
|
1213
|
-
1. **\u6570\u636E\u53EA\u8D70 \`cockpit.bash\`**\uFF08\`curl\` \u53D6\u3001\`curl -X POST\` \u6539\u3001\u6216\u8BFB\u5199\u6587\u4EF6\uFF09\uFF0C**\u7EDD\u4E0D\u7528
|
|
1214
|
-
\`fetch(\u5916\u90E8URL)\`** \u2014\u2014 \u540C\u6E90\u6C99\u7BB1\u4F1A CORS \u62E6\u622A\u3002
|
|
1215
|
-
2. **\u4E24\u7C7B\u5931\u8D25\u90FD\u8981\u5904\u7406**\uFF08\u524D\u53F0\uFF09\uFF1A\`try/catch\` \u6293\u5230\u7684**\u629B\u5F02\u5E38 = spawn/\u57FA\u5EFA\u5931\u8D25**\uFF08\u574F\u8DEF\u5F84\u3001
|
|
1216
|
-
\u8FDE\u63A5\u65AD\uFF09\uFF1B\u6CA1\u629B\u4F46 **\`exitCode !== 0\` = \u547D\u4EE4\u8DD1\u4E86\u4F46\u5931\u8D25**\uFF0C\u5C55\u793A \`stderr\` \u6216 stdout \u91CC\u7684\u9519\u8BEF
|
|
1217
|
-
\u4F53\uFF0C\u522B\u53EA\u5199"\u5931\u8D25"\u3002\u540E\u53F0\u5BF9\u5E94 \`onError\`\uFF08\u57FA\u5EFA\uFF09+ \`onExit\` \u975E\u96F6 \`code\`\uFF08\u547D\u4EE4\uFF09\u3002
|
|
1218
|
-
3. **\`command\` \u662F shell \u4E32**\uFF1A\u52A8\u6001\u503C/\u7528\u6237\u8F93\u5165\u62FC\u8FDB\u53BB\u524D\u5FC5\u987B\u6821\u9A8C\u6216\u8F6C\u4E49\uFF08shell \u5143\u5B57\u7B26\u4F1A\u6CE8\u5165\uFF09\uFF1B
|
|
1219
|
-
\u80FD\u7528\u56FA\u5B9A\u547D\u4EE4\u5C31\u522B\u88F8\u62FC\u3002
|
|
1220
|
-
4. **\u8D44\u6E90\u5F15\u7528**\uFF1A\u540C\u7EA7\u76F8\u5BF9\uFF08\`./app.jsx\`\u3001\`./style.css\`\u3001\u56FE\u7247\uFF09\u548C CDN \u7EDD\u5BF9 URL \u90FD\u80FD\u52A0\u8F7D\uFF1B\u6839
|
|
1221
|
-
\u76F8\u5BF9\uFF08\`/assets/x.css\`\uFF09\u4F1A 404\u3002
|
|
1222
|
-
|
|
1223
|
-
meta \u5934\uFF08\u52A1\u5FC5\u5199\u4E0A\uFF0C\u4F9B HTML \u9762\u677F\u505A\u5361\u7247\u3001console \`/name\` \u6253\u5F00\uFF09\uFF1A
|
|
1224
|
-
|
|
1225
|
-
\`\`\`html
|
|
1226
|
-
<title>\u9875\u9762\u6807\u9898</title>
|
|
1227
|
-
<meta name="cockpit-name" content="short-name"> <!-- /name \u77ED\u540D\uFF0C\u552F\u4E00\u3001\u53EA\u7528\u5B57\u6BCD\u6570\u5B57-_ -->
|
|
1228
|
-
<meta name="description" content="\u4E00\u53E5\u8BDD\u8BF4\u660E">
|
|
1229
|
-
<meta name="cockpit-icon" content="\u{1F50D}"> <!-- emoji \u6216\u56FE\u6807 url\uFF0C\u53EF\u9009 -->
|
|
1230
|
-
<meta name="cockpit-theme" content="auto"> <!-- \u542F\u7528\u53EF\u62D6\u52A8\u7684\u4EAE/\u6697\u5207\u6362\u6309\u94AE\uFF1Bauto=\u9996\u6B21\u8DDF\u968F\u7CFB\u7EDF\uFF0C\u53EF\u586B light/dark -->
|
|
1231
|
-
\`\`\`
|
|
1223
|
+
\u5373\u4FBF\u7528\u6237\u5728 \`/html\` \u540E\u5DF2\u7ECF\u628A\u9700\u6C42\u548C\u76EE\u5F55\u90FD\u5199\u6E05\u695A\u4E86\uFF0C\u4E5F\u8981\u5148\u505C\u4E00\u8F6E\uFF0C\u7ED9\u51FA\u300C\u7406\u89E3 + \u76EE\u5F55 + \u4E00\u53E5\u300E\u786E\u8BA4
|
|
1224
|
+
\u5C31\u5F00\u59CB\u5199?\u300F\u300D\u7B49\u5F85\u786E\u8BA4\u2014\u2014\u4F46\u8FD9\u4E00\u8F6E\u8981**\u8F7B\u91CF**\uFF0C\u522B\u53CD\u590D\u8FFD\u95EE\u3002
|
|
1232
1225
|
|
|
1233
|
-
## \
|
|
1226
|
+
## \u4E8C\u3001\u9AA8\u67B6\uFF1A\u4E09\u4E2A\u6587\u4EF6\uFF0C\u7167\u6284
|
|
1234
1227
|
|
|
1235
|
-
\u5916\u58F3\u56FA\u5B9A\u8FD9\u6837\u5199\uFF0C\
|
|
1228
|
+
\`index.html\` \u2014\u2014 \u5916\u58F3\u56FA\u5B9A\u8FD9\u6837\u5199\uFF0C\u522B\u81EA\u7531\u53D1\u6325\uFF1A
|
|
1236
1229
|
|
|
1237
1230
|
\`\`\`html
|
|
1238
|
-
|
|
1239
|
-
<
|
|
1240
|
-
<
|
|
1241
|
-
<
|
|
1242
|
-
<
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1231
|
+
<!doctype html>
|
|
1232
|
+
<html>
|
|
1233
|
+
<head>
|
|
1234
|
+
<meta charset="utf-8">
|
|
1235
|
+
<title>\u9875\u9762\u6807\u9898</title>
|
|
1236
|
+
<meta name="cockpit-name" content="short-name"> <!-- /name \u77ED\u540D\uFF0C\u552F\u4E00\u3001\u53EA\u7528\u5B57\u6BCD\u6570\u5B57-_ -->
|
|
1237
|
+
<meta name="description" content="\u4E00\u53E5\u8BDD\u8BF4\u660E">
|
|
1238
|
+
<meta name="cockpit-icon" content="\u{1F50D}"> <!-- emoji \u6216\u56FE\u6807 url\uFF0C\u53EF\u9009 -->
|
|
1239
|
+
<meta name="cockpit-theme" content="auto"> <!-- \u4EAE/\u6697\u5207\u6362\uFF0C\u89C1\u7B2C\u4E94\u8282 -->
|
|
1240
|
+
<link rel="stylesheet" href="/html-lib/theme.css">
|
|
1241
|
+
</head>
|
|
1242
|
+
<body>
|
|
1243
|
+
<div id="root"></div>
|
|
1244
|
+
<script src="/html-lib/react.production.min.js"></script>
|
|
1245
|
+
<script src="/html-lib/react-dom.production.min.js"></script>
|
|
1246
|
+
<script src="/html-lib/babel.min.js"></script>
|
|
1247
|
+
<script>
|
|
1248
|
+
(async () => {
|
|
1249
|
+
const src = await (await fetch('./app.jsx')).text();
|
|
1250
|
+
// \u5FC5\u987B classic runtime\uFF1A\u9ED8\u8BA4 automatic \u4F1A\u6CE8\u5165 import react/jsx-runtime\uFF0C
|
|
1251
|
+
// \u4F5C\u4E3A\u666E\u901A\u811A\u672C\u6CE8\u5165\u4F1A\u62A5 "Cannot use import statement outside a module"
|
|
1252
|
+
const { code } = Babel.transform(src, { presets: [['react', { runtime: 'classic' }]] });
|
|
1253
|
+
const s = document.createElement('script'); s.textContent = code; document.body.appendChild(s);
|
|
1254
|
+
})();
|
|
1255
|
+
</script>
|
|
1256
|
+
</body>
|
|
1257
|
+
</html>
|
|
1251
1258
|
\`\`\`
|
|
1252
1259
|
|
|
1253
|
-
|
|
1254
|
-
- \`app.jsx\` \u7528\u5168\u5C40 \`React\`\uFF08\`const { useState } = React;\`\uFF09\uFF0C\u672B\u5C3E
|
|
1255
|
-
\`ReactDOM.createRoot(document.getElementById('root')).render(<App/>)\`\u3002
|
|
1256
|
-
- \u6837\u5F0F\u9ED8\u8BA4\u5957 **Cockpit \u4E3B\u9898**\uFF1A\`<link rel="stylesheet" href="/html-lib/theme.css">\`\uFF0C\u76F4\u63A5\u7528
|
|
1257
|
-
\u8BED\u4E49\u53D8\u91CF\uFF08\`var(--background)\` / \`--foreground\` / \`--card\` / \`--brand\` / \`--muted-foreground\` /
|
|
1258
|
-
\`--border\` / \`--destructive\`\uFF1B\u534A\u900F\u660E\u7528 \`hsl(var(--green-9) / .12)\`\uFF09\u3002\u4E0A\u9762 meta \u5934\u91CC\u7684
|
|
1259
|
-
\`cockpit-theme\` \u8BA9\u9884\u89C8\u5E26\u4E0A\u4E00\u4E2A\u6D6E\u52A8\u4EAE/\u6697\u5207\u6362\u6309\u94AE\uFF08\u9ED8\u8BA4\u53F3\u4E0A\u89D2\uFF0C\u7528\u6237\u53EF\u62D6\u52A8\u5438\u9644\u5230\u4EFB\u610F\u4E00\u89D2\uFF0C
|
|
1260
|
-
\u6240\u4EE5\u4E0D\u5FC5\u4E3A\u5B83\u5728\u53F3\u4E0A\u89D2\u7559\u7A7A\u4F4D\uFF09\uFF1B**\u9ED8\u8BA4\u8DDF\u968F Cockpit \u5BBF\u4E3B\u4E3B\u9898**\uFF08Cockpit \u5916\u6253\u5F00\u65F6\u6309
|
|
1261
|
-
meta \u7684 auto/light/dark\uFF09\uFF0C\u624B\u52A8\u5207\u6362\u540E**\u6309 app \u8BB0\u4F4F\u9009\u62E9\uFF08\u8DE8\u5237\u65B0\uFF0C\u4F18\u5148\u4E8E\u8DDF\u968F\uFF09**\uFF0C\u4E5F\u53EF\u8C03
|
|
1262
|
-
\`cockpit.toggleTheme()\`\u3002**\u522B\u81EA\u62CD\u8C03\u8272\u677F\u3001\u522B\u4E0A Tailwind**\uFF1Bapp \u7279\u6709\u7EC6\u8282\u518D\u8865\u4E00\u5C0F\u6BB5 \`<style>\`\u3002
|
|
1260
|
+
meta \u5934\u56DB\u884C\u90FD\u8981\u5199\uFF1AHTML \u9762\u677F\u9760\u5B83\u505A\u5361\u7247\uFF0Cconsole \u9760 \`cockpit-name\` \u7528 \`/name\` \u6253\u5F00\u3002
|
|
1263
1261
|
|
|
1264
|
-
React \
|
|
1262
|
+
\`app.jsx\` \u2014\u2014 \u5168\u5C40 \`React\`\uFF0C\u672B\u5C3E render\u3002**\u6253\u5F00\u53EA\u8BFB\u7F13\u5B58\uFF0C\u70B9\u5237\u65B0\u624D\u56DE\u6E90**\uFF1A
|
|
1265
1263
|
|
|
1266
1264
|
\`\`\`jsx
|
|
1265
|
+
const { useState, useEffect, useCallback } = React;
|
|
1266
|
+
|
|
1267
1267
|
function App() {
|
|
1268
|
-
const [
|
|
1269
|
-
const
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1268
|
+
const [data, setData] = useState(null);
|
|
1269
|
+
const [busy, setBusy] = useState(false);
|
|
1270
|
+
const [err, setErr] = useState(null);
|
|
1271
|
+
|
|
1272
|
+
const load = useCallback(async (refresh) => {
|
|
1273
|
+
setBusy(true); setErr(null);
|
|
1274
|
+
try {
|
|
1275
|
+
const arg = btoa(JSON.stringify({ refresh: !!refresh })); // \u7ED3\u6784\u5316\u5165\u53C2\u8D70 base64
|
|
1276
|
+
const { stdout, stderr, exitCode } = await cockpit.bash(\`node ./api.mjs \${arg}\`);
|
|
1277
|
+
if (exitCode !== 0) throw new Error(stderr || '\u811A\u672C\u5931\u8D25'); // \u547D\u4EE4\u8DD1\u4E86\u4F46\u5931\u8D25
|
|
1278
|
+
const d = JSON.parse(stdout);
|
|
1279
|
+
if (d.error && !d.stale) throw new Error(d.error);
|
|
1280
|
+
setData(d);
|
|
1281
|
+
if (d.stale) setErr(d.error); // \u6709\u65E7\u6570\u636E\u53EF\u663E\u793A
|
|
1282
|
+
} catch (e) {
|
|
1283
|
+
setErr(e.message); // \u629B\u5F02\u5E38 = spawn/\u57FA\u5EFA\u5931\u8D25\u3002\u5DF2\u6709 data \u5C31\u7559\u7740\uFF0C\u522B\u6E05\u6210\u9519\u8BEF\u9875
|
|
1284
|
+
} finally { setBusy(false); }
|
|
1285
|
+
}, []);
|
|
1286
|
+
|
|
1287
|
+
useEffect(() => { load(false); }, [load]); // \u6253\u5F00\uFF1A\u53EA\u8BFB\u7F13\u5B58\uFF0C0 \u6B21\u56DE\u6E90
|
|
1288
|
+
|
|
1289
|
+
if (!data) return <div>{err ?? '\u52A0\u8F7D\u4E2D\u2026'}</div>;
|
|
1290
|
+
return (
|
|
1291
|
+
<div>
|
|
1292
|
+
<span>{data.cached ? '\u7F13\u5B58' : '\u5DF2\u66F4\u65B0'} \xB7 {new Date(data.fetchedAt).toLocaleString('zh-CN')}</span>
|
|
1293
|
+
<button disabled={busy} onClick={() => load(true)}>{busy ? '\u5237\u65B0\u4E2D\u2026' : '\u5237\u65B0'}</button>
|
|
1294
|
+
{err && <em>\u5237\u65B0\u5931\u8D25\uFF0C\u4E0B\u9762\u662F\u65E7\u6570\u636E\uFF1A{err}</em>}
|
|
1295
|
+
{/* \u6E32\u67D3 data */}
|
|
1296
|
+
</div>
|
|
1297
|
+
);
|
|
1274
1298
|
}
|
|
1275
|
-
\`\`\`
|
|
1276
1299
|
|
|
1277
|
-
|
|
1300
|
+
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
|
1301
|
+
\`\`\`
|
|
1278
1302
|
|
|
1279
|
-
\
|
|
1280
|
-
\u5199\u6210\u540C\u76EE\u5F55**\u811A\u672C\u6587\u4EF6**\uFF0C\`cockpit.bash\` \u8C03\u5B83\u2014\u2014HTML/JSX \u7BA1\u5C55\u793A\uFF0C\u811A\u672C\u5F53\u540E\u7AEF\u5904\u7406\u5668\u3002
|
|
1303
|
+
\`api.mjs\` \u2014\u2014 \u540E\u7AEF\u5904\u7406\u5668\uFF1A\u53D6\u6570\u3001\u89E3\u6790\u3001\u843D\u76D8\u90FD\u5728\u8FD9\u91CC\uFF0Cstdout \u5410\u4E00\u4E2A JSON\uFF1A
|
|
1281
1304
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
- \u811A\u672C\u7528 stdout \u8FD4\u56DE JSON\uFF0C\u524D\u7AEF \`JSON.parse\`\u3002\u5148 \`Write\` \u51FA\u811A\u672C\uFF0C\u518D\u5728\u9875\u9762\u8C03\u5B83\u3002
|
|
1305
|
+
\`\`\`js
|
|
1306
|
+
import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';
|
|
1307
|
+
import { join, dirname } from 'node:path';
|
|
1308
|
+
import { fileURLToPath } from 'node:url';
|
|
1287
1309
|
|
|
1288
|
-
|
|
1310
|
+
const DIR = dirname(fileURLToPath(import.meta.url));
|
|
1311
|
+
const CACHE = join(DIR, 'cache/data.json');
|
|
1312
|
+
const VERSION = 1; // payload \u7ED3\u6784\u4E00\u6539\u5C31 bump\uFF0C\u5426\u5219\u8001\u7F13\u5B58\u5582\u7ED9\u65B0\u524D\u7AEF
|
|
1289
1313
|
|
|
1290
|
-
|
|
1314
|
+
const args = JSON.parse(Buffer.from(process.argv[2] || '', 'base64').toString() || '{}');
|
|
1291
1315
|
|
|
1292
|
-
|
|
1316
|
+
function readCache() {
|
|
1317
|
+
if (!existsSync(CACHE)) return null;
|
|
1318
|
+
const c = JSON.parse(readFileSync(CACHE, 'utf8'));
|
|
1319
|
+
return c.v === VERSION ? c : null; // \u7248\u672C\u4E0D\u7B26 = \u4F5C\u5E9F\uFF0C\u5F53\u6CA1\u6709
|
|
1320
|
+
}
|
|
1321
|
+
function writeCache(payload) {
|
|
1322
|
+
mkdirSync(dirname(CACHE), { recursive: true });
|
|
1323
|
+
const tmp = \`\${CACHE}.\${process.pid}.tmp\`; // \u539F\u5B50\u5199\uFF1A\u4E2D\u9014\u88AB\u6740\u4E0D\u4F1A\u7559\u4E0B\u534A\u4E2A JSON
|
|
1324
|
+
writeFileSync(tmp, JSON.stringify({ v: VERSION, fetchedAt: new Date().toISOString(), payload }));
|
|
1325
|
+
renameSync(tmp, CACHE);
|
|
1326
|
+
}
|
|
1293
1327
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1328
|
+
try {
|
|
1329
|
+
const hit = args.refresh ? null : readCache();
|
|
1330
|
+
if (hit) {
|
|
1331
|
+
process.stdout.write(JSON.stringify({ ...hit.payload, cached: true, fetchedAt: hit.fetchedAt }));
|
|
1332
|
+
} else {
|
|
1333
|
+
const payload = await fetchFresh(args); // \u2190 \u4F60\u7684\u53D6\u6570\u903B\u8F91\uFF08curl / \u67E5\u5E93 / \u8DD1\u547D\u4EE4\uFF09
|
|
1334
|
+
writeCache(payload);
|
|
1335
|
+
process.stdout.write(JSON.stringify({ ...payload, cached: false, fetchedAt: new Date().toISOString() }));
|
|
1336
|
+
}
|
|
1337
|
+
} catch (e) {
|
|
1338
|
+
const hit = readCache(); // \u5237\u65B0\u5931\u8D25\uFF1A\u56DE\u843D\u65E7\u7F13\u5B58\u5E76\u6807\u6CE8\uFF0C\u522B\u8BA9\u9875\u9762\u7A7A\u6389
|
|
1339
|
+
process.stdout.write(JSON.stringify(hit
|
|
1340
|
+
? { ...hit.payload, cached: true, stale: true, error: e.message, fetchedAt: hit.fetchedAt }
|
|
1341
|
+
: { error: e.message }));
|
|
1342
|
+
}
|
|
1308
1343
|
\`\`\`
|
|
1309
1344
|
|
|
1310
|
-
\
|
|
1345
|
+
- \u4E00\u6761\u547D\u4EE4\u5C31\u591F\u3001\u6570\u636E\u4E5F\u4E0D\u503C\u5F97\u7559\uFF08\u7EAF\u67E5\u770B\u5F53\u524D\u72B6\u6001\uFF09\u65F6\uFF0C\`api.mjs\` \u53EF\u4EE5\u7701\u6389\uFF0C\u76F4\u63A5
|
|
1346
|
+
\`cockpit.bash("curl -s \u2026")\`\uFF1B\u4F46\u53EA\u8981\u6570\u636E\u8981\u7559\u4E0B\u6765\uFF0C\u5C31\u7528\u8FD9\u4E2A\u9AA8\u67B6\uFF0C\u522B\u81EA\u5DF1\u53D1\u660E\u4E00\u5957\u3002
|
|
1347
|
+
- \u6362\u8BED\u8A00\u540C\u7406\uFF1A\`python3 ./api.py\` / \`bash ./api.sh\`\uFF08\u663E\u5F0F\u89E3\u91CA\u5668\uFF0C\u514D \`chmod +x\`\uFF09\uFF1B\`node\` /
|
|
1348
|
+
\`python3\` \u62FF\u4E0D\u51C6\u5C31\u5148 \`command -v\` \u63A2\u6D4B\uFF0C\u9000\u56DE shell\u3002
|
|
1349
|
+
- \u7F13\u5B58 key \u8981\u542B**\u5F71\u54CD\u7ED3\u679C\u7684\u53C2\u6570**\uFF08\u65F6\u95F4\u8303\u56F4\u3001\u6293\u53D6\u6DF1\u5EA6\u2026\uFF09\uFF1A\u628A \`cache/data.json\` \u6362\u6210
|
|
1350
|
+
\`cache/<param>.json\` \u6216\u5728\u6587\u4EF6\u91CC\u6309 key \u5206\u6876\uFF0C\u5426\u5219\u4F1A\u62FF 7 \u5929\u7684\u7F13\u5B58\u53BB\u6E32\u67D3 30 \u5929\u7684\u89C6\u56FE\u3002
|
|
1311
1351
|
|
|
1312
|
-
|
|
1313
|
-
<pre id="log"></pre>
|
|
1314
|
-
<script>
|
|
1315
|
-
const box = document.getElementById('log');
|
|
1316
|
-
const h = cockpit.bash("tail -f ./build.log", {
|
|
1317
|
-
background: true,
|
|
1318
|
-
onOutput: c => { box.textContent += c; },
|
|
1319
|
-
onExit: code => { box.textContent += '\\n[\u9000\u51FA ' + code + ']'; },
|
|
1320
|
-
onError: msg => { box.textContent += '\\n[\u9519\u8BEF ' + msg + ']'; },
|
|
1321
|
-
});
|
|
1322
|
-
// \u9700\u8981\u65F6 h.kill()
|
|
1323
|
-
</script>
|
|
1324
|
-
\`\`\`
|
|
1352
|
+
## \u4E09\u3001\u5951\u7EA6\uFF1Awindow.cockpit
|
|
1325
1353
|
|
|
1326
|
-
|
|
1354
|
+
\u9875\u9762\u52A0\u8F7D\u65F6 SDK \u5DF2\u5C31\u7EEA\uFF0C\u65E0\u9700\u5F15\u5165\u4EFB\u4F55\u5E93\uFF1A
|
|
1327
1355
|
|
|
1328
|
-
\
|
|
1329
|
-
\
|
|
1330
|
-
\
|
|
1356
|
+
- \`cockpit.cwd: string\` \u2014\u2014 \u5F53\u524D\u6587\u4EF6\u6240\u5728\u76EE\u5F55\uFF0C\u76F8\u5BF9\u8DEF\u5F84\u547D\u4EE4\u9ED8\u8BA4\u5728\u6B64\u6267\u884C\u3002
|
|
1357
|
+
- \`cockpit.bash(command, opts?)\` \u2014\u2014 \u6267\u884C\u4E00\u6761 bash \u547D\u4EE4\uFF08\`command\` \u662F\u6574\u6761 shell \u4E32\uFF09\uFF1A
|
|
1358
|
+
- **\u524D\u53F0**\uFF08\u9ED8\u8BA4\uFF0C\u77ED/\u79BB\u6563\u547D\u4EE4\uFF09\u2192 \`Promise<{ stdout, stderr, exitCode }>\`\uFF0C\`await\` \u540E\u4E00\u6B21\u62FF\u5168\u90E8\u8F93\u51FA\u3002
|
|
1359
|
+
- **\u540E\u53F0**\uFF08\`opts.background: true\`\uFF0C\u957F/\u5B9E\u65F6\u547D\u4EE4\uFF09\u2192 \`{ kill() }\`\uFF0C\u7ECF\u56DE\u8C03\u6D41\u5F0F\u8F93\u51FA\u3002
|
|
1360
|
+
\u5927/\u6301\u7EED\u8F93\u51FA**\u5FC5\u987B**\u7528\u540E\u53F0\u2014\u2014\u524D\u53F0\u4F1A\u628A\u8F93\u51FA\u5168\u7F13\u5B58\u5728\u5185\u5B58\u91CC\u3002
|
|
1361
|
+
|
|
1362
|
+
\`\`\`js
|
|
1363
|
+
const h = cockpit.bash("npm run build", {
|
|
1364
|
+
background: true,
|
|
1365
|
+
onOutput: c => append(c),
|
|
1366
|
+
onStderr: c => append(c),
|
|
1367
|
+
onExit: code => append('\\n[\u9000\u51FA ' + code + ']'), // \u547D\u4EE4\u8DD1\u5B8C\u4E86\uFF0Ccode \u975E\u96F6 = \u5931\u8D25
|
|
1368
|
+
onError: msg => append('\\n[\u9519\u8BEF ' + msg + ']'), // spawn / \u57FA\u5EFA\u5931\u8D25
|
|
1369
|
+
});
|
|
1370
|
+
// \u7EC4\u4EF6\u5378\u8F7D\u6216\u7528\u6237\u4E2D\u6B62\u65F6 h.kill()
|
|
1371
|
+
\`\`\`
|
|
1331
1372
|
|
|
1332
|
-
|
|
1333
|
-
- \u7528\u6237\u6CA1\u7ED9 \u2192 \u5B58\u5230**\u5F53\u524D\u804A\u5929\u5DE5\u4F5C\u76EE\u5F55**\uFF08\u672C\u6B21\u4F1A\u8BDD\u7684 cwd\uFF09\u4E0B\u7684 \`<name>/\`
|
|
1373
|
+
\u5FC5\u5B88\u7684\u51E0\u6761\uFF1A
|
|
1334
1374
|
|
|
1335
|
-
\
|
|
1336
|
-
\
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1375
|
+
1. **\u6570\u636E\u53EA\u8D70 \`cockpit.bash\`**\uFF08\`curl\` \u53D6\u3001\`curl -X POST\` \u6539\u3001\u6216\u8BFB\u5199\u6587\u4EF6\uFF09\uFF0C**\u7EDD\u4E0D\u7528
|
|
1376
|
+
\`fetch(\u5916\u90E8URL)\`** \u2014\u2014 \u540C\u6E90\u6C99\u7BB1\u4F1A CORS \u62E6\u622A\u3002\uFF08\`fetch('./app.jsx')\` \u8FD9\u79CD\u540C\u76EE\u5F55\u76F8\u5BF9\u8BF7\u6C42\u6CA1\u95EE\u9898\u3002\uFF09
|
|
1377
|
+
2. **\u4E24\u7C7B\u5931\u8D25\u90FD\u8981\u5904\u7406**\uFF1A\`try/catch\` \u6293\u5230\u7684**\u629B\u5F02\u5E38 = spawn/\u57FA\u5EFA\u5931\u8D25**\uFF08\u574F\u8DEF\u5F84\u3001\u8FDE\u63A5\u65AD\uFF09\uFF1B\u6CA1\u629B\u4F46
|
|
1378
|
+
**\`exitCode !== 0\` = \u547D\u4EE4\u8DD1\u4E86\u4F46\u5931\u8D25**\uFF0C\u8981\u5C55\u793A \`stderr\` \u6216 stdout \u91CC\u7684\u9519\u8BEF\u4F53\uFF0C\u522B\u53EA\u5199"\u5931\u8D25"\u3002
|
|
1379
|
+
\u540E\u53F0\u5BF9\u5E94 \`onError\`\uFF08\u57FA\u5EFA\uFF09+ \`onExit\` \u975E\u96F6 \`code\`\uFF08\u547D\u4EE4\uFF09\u3002
|
|
1380
|
+
3. **\`command\` \u662F shell \u4E32**\uFF1A\u52A8\u6001\u503C/\u7528\u6237\u8F93\u5165\u62FC\u8FDB\u53BB\u524D\u5FC5\u987B\u6821\u9A8C\u6216\u8F6C\u4E49\uFF08shell \u5143\u5B57\u7B26\u4F1A\u6CE8\u5165\uFF09\uFF1B\u7ED3\u6784\u5316
|
|
1381
|
+
\u5165\u53C2\u4E00\u5F8B\u8D70 base64\uFF08\u89C1\u9AA8\u67B6\uFF09\uFF0C\u522B\u62FC\u5B57\u7B26\u4E32\uFF1B\u5199\u5E93\u7528**\u53C2\u6570\u5316**\u8BED\u53E5\u3002
|
|
1382
|
+
4. **\u8D44\u6E90\u5F15\u7528**\uFF1A\u540C\u7EA7\u76F8\u5BF9\uFF08\`./app.jsx\`\u3001\`./style.css\`\u3001\u56FE\u7247\uFF09\u548C CDN \u7EDD\u5BF9 URL \u90FD\u80FD\u52A0\u8F7D\uFF1B\u6839\u76F8\u5BF9
|
|
1383
|
+
\uFF08\`/assets/x.css\`\uFF09\u4F1A 404 \u2014\u2014 \u670D\u52A1\u7AEF\u53EA\u6258\u7BA1 \`/html-lib/*\` \u548C \`/apps/*\`\u3002
|
|
1342
1384
|
|
|
1343
|
-
|
|
1385
|
+
## \u56DB\u3001\u672C\u5730\u6570\u636E\uFF1A\u4E09\u7C7B\u76EE\u5F55
|
|
1344
1386
|
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
\`curl\`, read/write files, and run scripts.
|
|
1387
|
+
\u6570\u636E\u8981\u843D\u76D8\u3001\u8981\u91CD\u5F00\u5373\u7528\u2014\u2014**\u522B\u6BCF\u6B21\u6253\u5F00\u90FD\u91CD\u65B0\u62C9\u4E00\u904D**\u3002\u6240\u6709\u6301\u4E45\u5316\u8D70 \`api.mjs\`\uFF0Capp \u76EE\u5F55\u4E0B\u6309
|
|
1388
|
+
\u300C\u80FD\u4E0D\u80FD\u91CD\u5EFA\u300D\u5206\u4E09\u7C7B\uFF0C\u522B\u5E73\u94FA\u5728\u4E00\u8D77\uFF1A
|
|
1348
1389
|
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
contract below.
|
|
1390
|
+
- \`cache/\` \u2014\u2014 \u80FD\u91CD\u5EFA\u7684\uFF08\u63A5\u53E3\u8FD4\u56DE\u3001\u539F\u59CB dump\u3001\u6D3E\u751F\u7D22\u5F15\uFF09\u3002\u5220\u4E86\u53EA\u662F\u6162\u3002**\u6574\u76EE\u5F55\u5199\u8FDB \`.gitignore\`**\u3002
|
|
1391
|
+
- \`state/\` \u2014\u2014 \u91CD\u5EFA\u4E0D\u51FA\u6765\u7684\uFF08\u6BCF\u65E5\u5FEB\u7167\u3001\u7528\u6237\u624B\u5DE5\u7EF4\u62A4\u7684\u6E05\u5355\uFF09\u3002\u6E90\u63A5\u53E3\u7ED9\u4E0D\u4E86\u7B2C\u4E8C\u6B21\uFF0C\u5220\u4E86\u6C38\u4E45\u4E22\u3002**\u5165\u5E93**\u3002
|
|
1392
|
+
- \`out/\` \u2014\u2014 \u7ED9\u4EBA\u770B\u7684\u4EA7\u7269\uFF08\u62A5\u544A\u3001\u5BFC\u51FA\uFF09\uFF0C\u6309\u65E5\u671F\u547D\u540D\uFF0C\u7559\u6863\u3002
|
|
1353
1393
|
|
|
1354
|
-
|
|
1394
|
+
\u5206\u7C7B\u7684\u610F\u4E49\u5168\u5728 git \u7B56\u7565\u4E0A\uFF1A\u76EE\u5F55\u5206\u4E86\u4F46 \`cache/\` \u6CA1 ignore\u3001\`state/\` \u6CA1\u5165\u5E93\uFF0C\u7B49\u4E8E\u6CA1\u5206\u3002
|
|
1355
1395
|
|
|
1356
|
-
|
|
1396
|
+
\u9AA8\u67B6\u91CC\u5DF2\u7ECF\u505A\u6389\u7684\uFF08\u7248\u672C\u53F7\u3001\u539F\u5B50\u5199\u3001\`refresh\` \u53C2\u6570\u3001\u53D6\u6570\u65F6\u95F4\u6233\u3001\u5931\u8D25\u56DE\u843D\u65E7\u6570\u636E\uFF09\u4E0D\u518D\u91CD\u590D\uFF0C\u5269\u4E0B\u8FD9\u4E9B
|
|
1397
|
+
\u8981\u4F60\u81EA\u5DF1\u5B88\uFF1A
|
|
1357
1398
|
|
|
1358
|
-
1.
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
(confirm / start / go / "write it" \u2014 any affirmative). Until then, only discuss \u2014 write
|
|
1367
|
-
nothing to disk.
|
|
1399
|
+
1. **\u4F1A\u589E\u957F\u7684\u76EE\u5F55\u5FC5\u987B\u6709\u4E0A\u9650\uFF0C\u5E76\u5728\u811A\u672C\u91CC\u6267\u884C**\uFF08\u7559 N \u6761 / \u8D85 X MB \u5C31\u6E05\uFF09\u3002\u53EA\u5199\u5728\u6CE8\u91CA\u91CC\u7684\u4E0A\u9650\u7B49\u4E8E
|
|
1400
|
+
\u6CA1\u6709\uFF0C\u6CA1\u4EBA\u770B\u7BA1\u7684 dump \u76EE\u5F55\u6DA8\u5230 GB \u7EA7\u662F\u5E38\u6001\u3002
|
|
1401
|
+
2. **\u843D\u76D8\u6587\u4EF6\u91CC\u4E0D\u5199\u7EDD\u5BF9\u8DEF\u5F84**\uFF08\u5B58\u76F8\u5BF9 app \u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\uFF0C\u5426\u5219\u76EE\u5F55\u4E00\u632A\u7D22\u5F15\u5168\u5E9F\uFF1B\u542B\u90AE\u7BB1\u7B49\u9690\u79C1\u7684\u843D\u76D8
|
|
1402
|
+
\u4E00\u5F8B ignore\u3002
|
|
1403
|
+
3. **\u7559\u4E00\u4E2A\u6E05\u7A7A\u5165\u53E3**\uFF08\`api.mjs\` \u52A0\u4E2A \`clear_cache\` \u6A21\u5F0F + \u754C\u9762\u6309\u94AE\uFF09\uFF0C\u5426\u5219\u7528\u6237\u53EA\u80FD\u53BB\u6587\u4EF6\u7BA1\u7406\u5668\u91CC
|
|
1404
|
+
\u731C\u54EA\u4E2A\u6587\u4EF6\u80FD\u5220\u3002
|
|
1405
|
+
4. \u8981\u7528\u522B\u7684 app \u7684\u51ED\u636E / \u5DE5\u5177\u811A\u672C\u65F6\u8D70 \`../<app>/\` \u76F8\u5BF9\u5F15\u7528\uFF0C\u5E76**\u5148\u63A2\u6D4B\u5B58\u5728\u518D\u7528**\uFF1B\u5931\u8D25\u7ED9\u4E00\u53E5\u7167\u7740\u505A
|
|
1406
|
+
\u5C31\u80FD\u4FEE\u7684\u63D0\u793A\uFF08\u300C\u53BB xxx \u91CD\u65B0\u767B\u5F55\u300D\uFF09\uFF0C\u522B\u628A\u539F\u59CB\u62A5\u9519\u7529\u7ED9\u7528\u6237\u3002
|
|
1368
1407
|
|
|
1369
|
-
|
|
1370
|
-
still stop once to present "understanding + directory + a 'shall I start writing?'" and
|
|
1371
|
-
wait \u2014 but keep this round **lightweight**, don't re-interrogate.
|
|
1408
|
+
## \u4E94\u3001\u4E3B\u9898
|
|
1372
1409
|
|
|
1373
|
-
|
|
1410
|
+
\u6837\u5F0F\u9ED8\u8BA4\u5957 **Cockpit \u4E3B\u9898**\uFF08\`index.html\` \u91CC\u5DF2\u7ECF link \u4E86 \`/html-lib/theme.css\`\uFF09\u3002**\u914D\u8272\u52A8\u624B\u524D\u5148\u62C9
|
|
1411
|
+
\u4E00\u6B21\u8FD9\u4EFD\u6587\u4EF6**\uFF0C\u5B83\u662F\u53D8\u91CF\u7684\u552F\u4E00\u771F\u6E90\uFF0C\u522B\u51ED\u8BB0\u5FC6\u731C\u53D8\u91CF\u540D\uFF1A
|
|
1374
1412
|
|
|
1375
|
-
|
|
1413
|
+
\`\`\`bash
|
|
1414
|
+
curl -fsS "{{BASE_URL}}/html-lib/theme.css"
|
|
1415
|
+
\`\`\`
|
|
1376
1416
|
|
|
1377
|
-
- \`
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1417
|
+
- \`:root\` \u6BB5\u662F\u4EAE\u8272\u53D6\u503C\u3001\`.dark\` \u6BB5\u662F\u6697\u8272\u3002\u539F\u59CB\u8272\u9636\uFF08\`--slate-*\` / \`--teal-*\` / \`--red-*\` \u2026\uFF09\u5B58\u7684\u662F
|
|
1418
|
+
HSL \u5206\u91CF\uFF0C\u6240\u4EE5\u534A\u900F\u660E\u76F4\u63A5\u5199 \`hsl(var(--green-9) / .12)\`\u3002
|
|
1419
|
+
- \u524D\u666F\u8272\u6210\u5BF9\u53D6\uFF08\`--card\`/\`--card-foreground\` \u2026\uFF09\uFF0C**\u53EA\u6709 \`--brand\` \u662F\u5751**\uFF1A\`--brand-foreground\` \u662F
|
|
1420
|
+
\u540C\u8272\u7CFB\u8272\u8C03\uFF0C\u538B\u5728 \`--brand\` \u586B\u5145\u4E0A\u4EC5 1.5:1\uFF1B**brand \u586B\u5145\u4E0A\u7684\u6587\u5B57\u6539\u7528 \`var(--background)\`**\u3002
|
|
1421
|
+
- \`cockpit-theme\` meta \u8BA9\u9884\u89C8\u5E26\u4E00\u4E2A\u6D6E\u52A8\u4EAE/\u6697\u5207\u6362\u6309\u94AE\uFF08\u9ED8\u8BA4\u53F3\u4E0A\u89D2\uFF0C\u7528\u6237\u53EF\u62D6\u52A8\u5438\u9644\u5230\u4EFB\u610F\u4E00\u89D2\uFF0C\u6240\u4EE5
|
|
1422
|
+
\u4E0D\u5FC5\u4E3A\u5B83\u5728\u53F3\u4E0A\u89D2\u7559\u7A7A\u4F4D\uFF09\uFF1B**\u9ED8\u8BA4\u8DDF\u968F Cockpit \u5BBF\u4E3B\u4E3B\u9898**\uFF08Cockpit \u5916\u6253\u5F00\u65F6\u6309 meta \u7684
|
|
1423
|
+
auto/light/dark\uFF09\uFF0C\u624B\u52A8\u5207\u6362\u540E**\u6309 app \u8BB0\u4F4F\u9009\u62E9\uFF08\u8DE8\u5237\u65B0\uFF0C\u4F18\u5148\u4E8E\u8DDF\u968F\uFF09**\uFF0C\u4E5F\u53EF\u8C03
|
|
1424
|
+
\`cockpit.toggleTheme()\`\u3002
|
|
1425
|
+
- **\u522B\u81EA\u62CD\u8C03\u8272\u677F\u3001\u522B\u4E0A Tailwind**\uFF1Bapp \u7279\u6709\u7EC6\u8282\u518D\u8865\u4E00\u5C0F\u6BB5 \`<style>\`\u3002`;
|
|
1426
|
+
var HTML_PROMPT_EN = `---
|
|
1427
|
+
name: html
|
|
1428
|
+
description: "Generate an interactive local React app with the built-in cockpit bash SDK to fetch/update data"
|
|
1429
|
+
argument-hint: "describe the app you want"
|
|
1430
|
+
---
|
|
1382
1431
|
|
|
1383
|
-
|
|
1432
|
+
# Build a Cockpit app (a local React page)
|
|
1384
1433
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
failure (bad path, dropped connection); no throw but **\`exitCode !== 0\` = the command ran
|
|
1389
|
-
and failed** \u2014 show \`stderr\` or the error body in stdout, don't just say "failed". Background:
|
|
1390
|
-
\`onError\` (infra) + a non-zero \`code\` in \`onExit\` (command).
|
|
1391
|
-
3. **\`command\` is a shell string**: validate or escape any dynamic/user input before
|
|
1392
|
-
interpolating it (shell metacharacters inject); prefer a fixed command.
|
|
1393
|
-
4. **Resource refs**: relative siblings (\`./app.jsx\`, \`./style.css\`, images) and absolute
|
|
1394
|
-
CDN URLs load; root-relative (\`/assets/x.css\`) 404s.
|
|
1434
|
+
A **local application**, not a one-off page: the preview injects a global \`window.cockpit\`
|
|
1435
|
+
SDK \u2014 essentially the Bash tool exposed to the page, so buttons can \`curl\`, read/write files
|
|
1436
|
+
and run scripts; data lands on disk inside the app directory and is there on reopen.
|
|
1395
1437
|
|
|
1396
|
-
|
|
1438
|
+
Zero build: React / Babel load from the same-origin \`/html-lib\` \u2014 no bundler, no deps, works offline.
|
|
1397
1439
|
|
|
1398
|
-
|
|
1399
|
-
<title>Page title</title>
|
|
1400
|
-
<meta name="cockpit-name" content="short-name"> <!-- unique short name for /name; letters/digits/-_ only -->
|
|
1401
|
-
<meta name="description" content="one line about the page">
|
|
1402
|
-
<meta name="cockpit-icon" content="\u{1F50D}"> <!-- emoji or icon url, optional -->
|
|
1403
|
-
<meta name="cockpit-theme" content="auto"> <!-- enable the draggable light/dark toggle; auto = first load follows the OS, or light/dark -->
|
|
1404
|
-
\`\`\`
|
|
1440
|
+
## 1. Flow: align first, write only after confirmation (important)
|
|
1405
1441
|
|
|
1406
|
-
|
|
1442
|
+
**Don't write files up front.** Three steps:
|
|
1407
1443
|
|
|
1408
|
-
|
|
1444
|
+
1. **Align on the requirement** \u2014 restate your understanding of the app (what it does, where
|
|
1445
|
+
data comes from, key features/interactions). Ask back only when the requirement is ambiguous
|
|
1446
|
+
or has several reasonable readings; if it's already clear, just give a short summary \u2014 don't
|
|
1447
|
+
interrogate.
|
|
1448
|
+
2. **Tell the storage directory** \u2014 resolve \`<dir>/<name>/\` and tell the user exactly where it
|
|
1449
|
+
goes and what it's called; they may reject or change it:
|
|
1450
|
+
- **You always pick \`<name>\`** from the request (lowercase kebab-case, matching
|
|
1451
|
+
\`cockpit-name\`) \u2014 the user only describes the app, they don't name it.
|
|
1452
|
+
- The user gave a directory \u2192 \`<the given directory>/<name>/\`; none given \u2192 \`<name>/\` under
|
|
1453
|
+
the **current chat working directory** (this session's cwd).
|
|
1454
|
+
- Every file of the app lives in that one directory, with data split into \`cache/\`
|
|
1455
|
+
\`state/\` \`out/\` per section 4. Don't scatter files elsewhere or invent a \`.cockpit-apps\`.
|
|
1456
|
+
3. **Write only after confirmation** \u2014 \`Write\` the files once the user clearly agrees (confirm /
|
|
1457
|
+
start / go / "write it" \u2014 any affirmative). Until then, only discuss.
|
|
1458
|
+
|
|
1459
|
+
Even if the user already spelled out requirement and directory in the \`/html\` call, still stop
|
|
1460
|
+
once to present "understanding + directory + a 'shall I start writing?'" and wait \u2014 but keep that
|
|
1461
|
+
round **lightweight**, don't re-interrogate.
|
|
1462
|
+
|
|
1463
|
+
## 2. Skeleton: three files, copy them
|
|
1464
|
+
|
|
1465
|
+
\`index.html\` \u2014 use this fixed shell, don't improvise:
|
|
1409
1466
|
|
|
1410
1467
|
\`\`\`html
|
|
1411
|
-
|
|
1412
|
-
<
|
|
1413
|
-
<
|
|
1414
|
-
<
|
|
1415
|
-
<
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1468
|
+
<!doctype html>
|
|
1469
|
+
<html>
|
|
1470
|
+
<head>
|
|
1471
|
+
<meta charset="utf-8">
|
|
1472
|
+
<title>Page title</title>
|
|
1473
|
+
<meta name="cockpit-name" content="short-name"> <!-- unique short name for /name; letters/digits/-_ -->
|
|
1474
|
+
<meta name="description" content="one line about the page">
|
|
1475
|
+
<meta name="cockpit-icon" content="\u{1F50D}"> <!-- emoji or icon url, optional -->
|
|
1476
|
+
<meta name="cockpit-theme" content="auto"> <!-- light/dark toggle, see section 5 -->
|
|
1477
|
+
<link rel="stylesheet" href="/html-lib/theme.css">
|
|
1478
|
+
</head>
|
|
1479
|
+
<body>
|
|
1480
|
+
<div id="root"></div>
|
|
1481
|
+
<script src="/html-lib/react.production.min.js"></script>
|
|
1482
|
+
<script src="/html-lib/react-dom.production.min.js"></script>
|
|
1483
|
+
<script src="/html-lib/babel.min.js"></script>
|
|
1484
|
+
<script>
|
|
1485
|
+
(async () => {
|
|
1486
|
+
const src = await (await fetch('./app.jsx')).text();
|
|
1487
|
+
// MUST use classic runtime: the default automatic runtime injects an import of
|
|
1488
|
+
// react/jsx-runtime, which \u2014 appended as a plain script \u2014 throws
|
|
1489
|
+
// "Cannot use import statement outside a module".
|
|
1490
|
+
const { code } = Babel.transform(src, { presets: [['react', { runtime: 'classic' }]] });
|
|
1491
|
+
const s = document.createElement('script'); s.textContent = code; document.body.appendChild(s);
|
|
1492
|
+
})();
|
|
1493
|
+
</script>
|
|
1494
|
+
</body>
|
|
1495
|
+
</html>
|
|
1425
1496
|
\`\`\`
|
|
1426
1497
|
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
\`ReactDOM.createRoot(document.getElementById('root')).render(<App/>)\`.
|
|
1430
|
-
- Style with the **Cockpit theme** by default: \`<link rel="stylesheet" href="/html-lib/theme.css">\`,
|
|
1431
|
-
then use the semantic vars (\`var(--background)\` / \`--foreground\` / \`--card\` / \`--brand\` /
|
|
1432
|
-
\`--muted-foreground\` / \`--border\` / \`--destructive\`; translucency via \`hsl(var(--green-9) / .12)\`).
|
|
1433
|
-
The \`cockpit-theme\` meta above gives the preview a floating light/dark toggle (top-right by
|
|
1434
|
-
default, draggable to any corner \u2014 so don't reserve top-right space for it); it **follows the
|
|
1435
|
-
Cockpit host theme by default** (outside Cockpit, the meta's auto/light/dark decides), and a manual
|
|
1436
|
-
toggle is **remembered per app across reloads and overrides the host**, or call \`cockpit.toggleTheme()\`
|
|
1437
|
-
from your own UI. Don't invent a palette or add Tailwind; add a small \`<style>\` for app-specific bits.
|
|
1498
|
+
All four meta lines matter: the HTML panel renders cards from them, and the console opens the
|
|
1499
|
+
app via \`cockpit-name\` (\`/name\`).
|
|
1438
1500
|
|
|
1439
|
-
|
|
1501
|
+
\`app.jsx\` \u2014 global \`React\`, render at the end. **Opening reads the cache; only Refresh goes out**:
|
|
1440
1502
|
|
|
1441
1503
|
\`\`\`jsx
|
|
1504
|
+
const { useState, useEffect, useCallback } = React;
|
|
1505
|
+
|
|
1442
1506
|
function App() {
|
|
1443
|
-
const [
|
|
1444
|
-
const
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1507
|
+
const [data, setData] = useState(null);
|
|
1508
|
+
const [busy, setBusy] = useState(false);
|
|
1509
|
+
const [err, setErr] = useState(null);
|
|
1510
|
+
|
|
1511
|
+
const load = useCallback(async (refresh) => {
|
|
1512
|
+
setBusy(true); setErr(null);
|
|
1513
|
+
try {
|
|
1514
|
+
const arg = btoa(JSON.stringify({ refresh: !!refresh })); // structured args as base64
|
|
1515
|
+
const { stdout, stderr, exitCode } = await cockpit.bash(\`node ./api.mjs \${arg}\`);
|
|
1516
|
+
if (exitCode !== 0) throw new Error(stderr || 'script failed'); // ran, but failed
|
|
1517
|
+
const d = JSON.parse(stdout);
|
|
1518
|
+
if (d.error && !d.stale) throw new Error(d.error);
|
|
1519
|
+
setData(d);
|
|
1520
|
+
if (d.stale) setErr(d.error); // old data is still shown
|
|
1521
|
+
} catch (e) {
|
|
1522
|
+
setErr(e.message); // a throw = spawn/infra failure. Keep existing data on screen
|
|
1523
|
+
} finally { setBusy(false); }
|
|
1524
|
+
}, []);
|
|
1525
|
+
|
|
1526
|
+
useEffect(() => { load(false); }, [load]); // on open: cache only, zero requests
|
|
1527
|
+
|
|
1528
|
+
if (!data) return <div>{err ?? 'Loading\u2026'}</div>;
|
|
1529
|
+
return (
|
|
1530
|
+
<div>
|
|
1531
|
+
<span>{data.cached ? 'cached' : 'updated'} \xB7 {new Date(data.fetchedAt).toLocaleString()}</span>
|
|
1532
|
+
<button disabled={busy} onClick={() => load(true)}>{busy ? 'Refreshing\u2026' : 'Refresh'}</button>
|
|
1533
|
+
{err && <em>Refresh failed, showing older data: {err}</em>}
|
|
1534
|
+
{/* render data */}
|
|
1535
|
+
</div>
|
|
1536
|
+
);
|
|
1449
1537
|
}
|
|
1450
|
-
\`\`\`
|
|
1451
1538
|
|
|
1452
|
-
|
|
1539
|
+
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
|
1540
|
+
\`\`\`
|
|
1453
1541
|
|
|
1454
|
-
|
|
1455
|
-
loops, a library, DB writes), write the backend as a **script file** next to the page and
|
|
1456
|
-
call it \u2014 the HTML/JSX is the frontend, the script is the backend handler.
|
|
1542
|
+
\`api.mjs\` \u2014 the backend handler: fetching, parsing and persistence live here; one JSON on stdout:
|
|
1457
1543
|
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
\`Buffer.from(arg,'base64')\` in the script) rather than string-building; use **parameterized**
|
|
1463
|
-
statements for DB writes.
|
|
1464
|
-
- The script returns JSON on stdout; the frontend does \`JSON.parse\`. \`Write\` the script first, then call it.
|
|
1544
|
+
\`\`\`js
|
|
1545
|
+
import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';
|
|
1546
|
+
import { join, dirname } from 'node:path';
|
|
1547
|
+
import { fileURLToPath } from 'node:url';
|
|
1465
1548
|
|
|
1466
|
-
|
|
1549
|
+
const DIR = dirname(fileURLToPath(import.meta.url));
|
|
1550
|
+
const CACHE = join(DIR, 'cache/data.json');
|
|
1551
|
+
const VERSION = 1; // bump whenever the payload shape changes, or old cache feeds a new UI
|
|
1467
1552
|
|
|
1468
|
-
|
|
1553
|
+
const args = JSON.parse(Buffer.from(process.argv[2] || '', 'base64').toString() || '{}');
|
|
1469
1554
|
|
|
1470
|
-
|
|
1555
|
+
function readCache() {
|
|
1556
|
+
if (!existsSync(CACHE)) return null;
|
|
1557
|
+
const c = JSON.parse(readFileSync(CACHE, 'utf8'));
|
|
1558
|
+
return c.v === VERSION ? c : null; // version mismatch = void it, act as if empty
|
|
1559
|
+
}
|
|
1560
|
+
function writeCache(payload) {
|
|
1561
|
+
mkdirSync(dirname(CACHE), { recursive: true });
|
|
1562
|
+
const tmp = \`\${CACHE}.\${process.pid}.tmp\`; // atomic: a kill mid-write leaves no half JSON
|
|
1563
|
+
writeFileSync(tmp, JSON.stringify({ v: VERSION, fetchedAt: new Date().toISOString(), payload }));
|
|
1564
|
+
renameSync(tmp, CACHE);
|
|
1565
|
+
}
|
|
1471
1566
|
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1567
|
+
try {
|
|
1568
|
+
const hit = args.refresh ? null : readCache();
|
|
1569
|
+
if (hit) {
|
|
1570
|
+
process.stdout.write(JSON.stringify({ ...hit.payload, cached: true, fetchedAt: hit.fetchedAt }));
|
|
1571
|
+
} else {
|
|
1572
|
+
const payload = await fetchFresh(args); // \u2190 your fetching logic (curl / DB / commands)
|
|
1573
|
+
writeCache(payload);
|
|
1574
|
+
process.stdout.write(JSON.stringify({ ...payload, cached: false, fetchedAt: new Date().toISOString() }));
|
|
1575
|
+
}
|
|
1576
|
+
} catch (e) {
|
|
1577
|
+
const hit = readCache(); // failed refresh: fall back to the old cache, labelled
|
|
1578
|
+
process.stdout.write(JSON.stringify(hit
|
|
1579
|
+
? { ...hit.payload, cached: true, stale: true, error: e.message, fetchedAt: hit.fetchedAt }
|
|
1580
|
+
: { error: e.message }));
|
|
1581
|
+
}
|
|
1486
1582
|
\`\`\`
|
|
1487
1583
|
|
|
1488
|
-
|
|
1584
|
+
- When one command is enough and the data isn't worth keeping (just viewing current state), skip
|
|
1585
|
+
\`api.mjs\` and call \`cockpit.bash("curl -s \u2026")\` directly. The moment data should survive a
|
|
1586
|
+
reopen, use this skeleton rather than inventing your own.
|
|
1587
|
+
- Same for other languages: \`python3 ./api.py\` / \`bash ./api.sh\` (explicit interpreter avoids
|
|
1588
|
+
\`chmod +x\`); probe \`node\` / \`python3\` with \`command -v\` if unsure and fall back to shell.
|
|
1589
|
+
- The cache key must include **every parameter that changes the result** (time range, fetch depth
|
|
1590
|
+
\u2026): use \`cache/<param>.json\` or bucket by key inside the file \u2014 otherwise a 7-day cache renders
|
|
1591
|
+
in the 30-day view.
|
|
1489
1592
|
|
|
1490
|
-
|
|
1491
|
-
<pre id="log"></pre>
|
|
1492
|
-
<script>
|
|
1493
|
-
const box = document.getElementById('log');
|
|
1494
|
-
const h = cockpit.bash("tail -f ./build.log", {
|
|
1495
|
-
background: true,
|
|
1496
|
-
onOutput: c => { box.textContent += c; },
|
|
1497
|
-
onExit: code => { box.textContent += '\\n[exit ' + code + ']'; },
|
|
1498
|
-
onError: msg => { box.textContent += '\\n[error ' + msg + ']'; },
|
|
1499
|
-
});
|
|
1500
|
-
// h.kill() when done
|
|
1501
|
-
</script>
|
|
1502
|
-
\`\`\`
|
|
1593
|
+
## 3. Contract: window.cockpit
|
|
1503
1594
|
|
|
1504
|
-
|
|
1595
|
+
The SDK is ready on load \u2014 no library to import:
|
|
1505
1596
|
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1597
|
+
- \`cockpit.cwd: string\` \u2014 directory of the current file; relative-path commands run here.
|
|
1598
|
+
- \`cockpit.bash(command, opts?)\` \u2014 run one bash command (\`command\` is a raw shell string):
|
|
1599
|
+
- **Foreground** (default, short/discrete) \u2192 \`Promise<{ stdout, stderr, exitCode }>\`; \`await\` for the full output at once.
|
|
1600
|
+
- **Background** (\`opts.background: true\`, long/live) \u2192 \`{ kill() }\`, streaming via callbacks.
|
|
1601
|
+
Large/continuous output **must** use background \u2014 foreground buffers it all in memory.
|
|
1602
|
+
|
|
1603
|
+
\`\`\`js
|
|
1604
|
+
const h = cockpit.bash("npm run build", {
|
|
1605
|
+
background: true,
|
|
1606
|
+
onOutput: c => append(c),
|
|
1607
|
+
onStderr: c => append(c),
|
|
1608
|
+
onExit: code => append('\\n[exit ' + code + ']'), // command finished; non-zero = failed
|
|
1609
|
+
onError: msg => append('\\n[error ' + msg + ']'), // spawn / infra failure
|
|
1610
|
+
});
|
|
1611
|
+
// h.kill() on unmount or when the user aborts
|
|
1612
|
+
\`\`\`
|
|
1510
1613
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1614
|
+
Rules every page must follow:
|
|
1615
|
+
|
|
1616
|
+
1. **Data only through \`cockpit.bash\`** (\`curl\` to read, \`curl -X POST\` to write, or read/write
|
|
1617
|
+
files) \u2014 **never \`fetch(externalURL)\`**: the same-origin sandbox blocks it via CORS.
|
|
1618
|
+
(\`fetch('./app.jsx')\` and other same-directory relative requests are fine.)
|
|
1619
|
+
2. **Handle both failure modes**: a **throw** caught by \`try/catch\` = spawn/infra failure (bad
|
|
1620
|
+
path, dropped connection); no throw but **\`exitCode !== 0\` = the command ran and failed** \u2014 show
|
|
1621
|
+
\`stderr\` or the error body in stdout, don't just say "failed". Background: \`onError\` (infra) +
|
|
1622
|
+
a non-zero \`code\` in \`onExit\` (command).
|
|
1623
|
+
3. **\`command\` is a shell string**: validate or escape any dynamic/user input before interpolating
|
|
1624
|
+
it (shell metacharacters inject); pass structured input as base64 (see the skeleton) instead of
|
|
1625
|
+
string-building; use **parameterized** statements for DB writes.
|
|
1626
|
+
4. **Resource refs**: relative siblings (\`./app.jsx\`, \`./style.css\`, images) and absolute CDN URLs
|
|
1627
|
+
load; root-relative (\`/assets/x.css\`) 404s \u2014 the server only hosts \`/html-lib/*\` and \`/apps/*\`.
|
|
1628
|
+
|
|
1629
|
+
## 4. Local data: three directories
|
|
1630
|
+
|
|
1631
|
+
Data must land on disk and be there on reopen \u2014 **don't refetch everything on every open**. All
|
|
1632
|
+
persistence goes through \`api.mjs\`; inside the app directory, split it by "can this be rebuilt?"
|
|
1633
|
+
into three directories instead of one flat pile:
|
|
1634
|
+
|
|
1635
|
+
- \`cache/\` \u2014 rebuildable (API responses, raw dumps, derived indexes). Deleting it only costs time. **Gitignore the whole directory.**
|
|
1636
|
+
- \`state/\` \u2014 NOT rebuildable (daily snapshots, hand-maintained lists). The source can't hand it to you twice; deleted means gone. **Commit it.**
|
|
1637
|
+
- \`out/\` \u2014 artifacts for humans (reports, exports), date-named, kept.
|
|
1638
|
+
|
|
1639
|
+
The split only pays off through the git policy: directories separated but \`cache/\` not ignored and
|
|
1640
|
+
\`state/\` not committed means nothing was separated.
|
|
1641
|
+
|
|
1642
|
+
What the skeleton already handles (versioning, atomic writes, the \`refresh\` flag, the fetch
|
|
1643
|
+
timestamp, stale fallback) isn't repeated here. What's left is on you:
|
|
1644
|
+
|
|
1645
|
+
1. **Any growing directory needs a cap enforced in the script** (keep N entries / prune past X MB).
|
|
1646
|
+
A cap that lives only in a comment is not a cap \u2014 unattended dump directories reach GB scale.
|
|
1647
|
+
2. **Never write absolute paths into a file on disk** (store paths relative to the app directory),
|
|
1648
|
+
or moving the directory voids the index; anything holding private data (emails \u2026) is gitignored.
|
|
1649
|
+
3. **Provide a clear/reset entry point** (a \`clear_cache\` mode in \`api.mjs\` + a button), or the
|
|
1650
|
+
user's only option is guessing which file is safe to delete in a file manager.
|
|
1651
|
+
4. To use another app's credentials / helper scripts, reference them as \`../<app>/\` and **probe
|
|
1652
|
+
that they exist first**; on failure give one actionable line ("log in again at xxx") rather than
|
|
1653
|
+
dumping the raw error.
|
|
1654
|
+
|
|
1655
|
+
## 5. Theme
|
|
1656
|
+
|
|
1657
|
+
Style with the **Cockpit theme** by default (\`index.html\` already links \`/html-lib/theme.css\`).
|
|
1658
|
+
**Fetch that file once before you pick any colour** \u2014 it is the single source of truth for the
|
|
1659
|
+
variables, so don't guess names from memory:
|
|
1660
|
+
|
|
1661
|
+
\`\`\`bash
|
|
1662
|
+
curl -fsS "{{BASE_URL}}/html-lib/theme.css"
|
|
1663
|
+
\`\`\`
|
|
1513
1664
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1665
|
+
- \`:root\` holds the light values, \`.dark\` the dark ones. The raw scales (\`--slate-*\` / \`--teal-*\` /
|
|
1666
|
+
\`--red-*\` \u2026) store HSL components, so translucency is just \`hsl(var(--green-9) / .12)\`.
|
|
1667
|
+
- Take foregrounds in pairs (\`--card\`/\`--card-foreground\` \u2026), **except \`--brand\`**:
|
|
1668
|
+
\`--brand-foreground\` is a tint of the same hue and hits only 1.5:1 on a \`--brand\` fill \u2014 **use
|
|
1669
|
+
\`var(--background)\` for text on a brand fill**.
|
|
1670
|
+
- The \`cockpit-theme\` meta gives the preview a floating light/dark toggle (top-right by default,
|
|
1671
|
+
draggable to any corner \u2014 so don't reserve top-right space for it); it **follows the Cockpit host
|
|
1672
|
+
theme by default** (outside Cockpit, the meta's auto/light/dark decides), and a manual toggle is
|
|
1673
|
+
**remembered per app across reloads and overrides the host**, or call \`cockpit.toggleTheme()\`.
|
|
1674
|
+
- **Don't invent a palette or add Tailwind**; add a small \`<style>\` for app-specific bits.`;
|
|
1516
1675
|
|
|
1517
1676
|
// packages/feature/agent/src/server/lib/newBranchPrompt.ts
|
|
1518
1677
|
var NEW_BRANCH_PROMPT_ZH = `---
|
|
@@ -2374,7 +2533,7 @@ function follow(ctx) {
|
|
|
2374
2533
|
else ctx.signal.addEventListener("abort", () => ac.abort(), { once: true });
|
|
2375
2534
|
return ac;
|
|
2376
2535
|
}
|
|
2377
|
-
async function runSdkLoop(ctx,
|
|
2536
|
+
async function runSdkLoop(ctx, buildOptions) {
|
|
2378
2537
|
const hasImages = !!(ctx.images && ctx.images.length > 0);
|
|
2379
2538
|
const content = [];
|
|
2380
2539
|
if (ctx.images) {
|
|
@@ -2416,7 +2575,7 @@ async function runSdkLoop(ctx, buildOptions2) {
|
|
|
2416
2575
|
})();
|
|
2417
2576
|
const attemptAbort = follow(ctx);
|
|
2418
2577
|
const resume = isRetry ? ctx.currentKey() : ctx.sessionId;
|
|
2419
|
-
const options = isRetry ? { ...
|
|
2578
|
+
const options = isRetry ? { ...buildOptions(attemptAbort, resume, true), resume } : buildOptions(attemptAbort, ctx.sessionId, false);
|
|
2420
2579
|
const response = query({ prompt: input, options });
|
|
2421
2580
|
let receivedResult = false;
|
|
2422
2581
|
for await (const message of response) {
|
|
@@ -2446,6 +2605,39 @@ async function runSdkLoop(ctx, buildOptions2) {
|
|
|
2446
2605
|
// packages/feature/agent/src/server/engines/claude.ts
|
|
2447
2606
|
var PLAN_EDIT_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
|
|
2448
2607
|
var isPlanFilePath = (p) => /(^|\/)\.claude\/plans\//.test(p);
|
|
2608
|
+
var CLAUDE_SDK_EFFORTS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
2609
|
+
var CLAUDE_ULTRACODE_MODELS = /* @__PURE__ */ new Set(["claude-fable-5", "claude-opus-5", "claude-opus-4-8"]);
|
|
2610
|
+
var CLAUDE_XHIGH_MODELS = /* @__PURE__ */ new Set(["claude-fable-5", "claude-opus-5", "claude-opus-4-8", "claude-sonnet-5"]);
|
|
2611
|
+
function resolveClaudeModel(ctx) {
|
|
2612
|
+
const model = typeof ctx.params.model === "string" && ctx.params.model.trim() ? ctx.params.model.trim() : void 0;
|
|
2613
|
+
if (!model) return void 0;
|
|
2614
|
+
return ctx.params.claudeContextWindow === "1m" ? `${model}[1m]` : model;
|
|
2615
|
+
}
|
|
2616
|
+
function resolveClaudeEffort(ctx) {
|
|
2617
|
+
const model = typeof ctx.params.model === "string" ? ctx.params.model.trim() : "";
|
|
2618
|
+
if (model === "claude-haiku-4-5") return void 0;
|
|
2619
|
+
const effort = typeof ctx.params.claudeEffort === "string" ? ctx.params.claudeEffort : void 0;
|
|
2620
|
+
if (!effort || effort === "ultrathink") return void 0;
|
|
2621
|
+
if (effort === "ultracode") return "xhigh";
|
|
2622
|
+
if (effort === "xhigh" && !CLAUDE_XHIGH_MODELS.has(model)) return "max";
|
|
2623
|
+
if (effort === "max" && model === "claude-sonnet-4-6") return "high";
|
|
2624
|
+
return CLAUDE_SDK_EFFORTS.has(effort) ? effort : void 0;
|
|
2625
|
+
}
|
|
2626
|
+
function resolveClaudeSettings(ctx) {
|
|
2627
|
+
const settings = {};
|
|
2628
|
+
const model = typeof ctx.params.model === "string" ? ctx.params.model.trim() : "";
|
|
2629
|
+
if (ctx.params.claudeFastMode === true) settings.fastMode = true;
|
|
2630
|
+
if (ctx.params.claudeThinking !== void 0) settings.alwaysThinkingEnabled = ctx.params.claudeThinking;
|
|
2631
|
+
if (ctx.params.claudeEffort === "ultracode" && CLAUDE_ULTRACODE_MODELS.has(model)) settings.ultracode = true;
|
|
2632
|
+
return Object.keys(settings).length > 0 ? settings : void 0;
|
|
2633
|
+
}
|
|
2634
|
+
function withClaudePromptEffort(ctx) {
|
|
2635
|
+
if (ctx.params.claudeEffort !== "ultrathink") return ctx;
|
|
2636
|
+
const prompt = ctx.prompt ?? "";
|
|
2637
|
+
if (/\bultrathink\b/i.test(prompt)) return ctx;
|
|
2638
|
+
return { ...ctx, prompt: `Ultrathink:
|
|
2639
|
+
${prompt}` };
|
|
2640
|
+
}
|
|
2449
2641
|
function planPermission(toolName, input, opts) {
|
|
2450
2642
|
if (toolName === "ExitPlanMode") {
|
|
2451
2643
|
return {
|
|
@@ -2472,6 +2664,9 @@ function planPermission(toolName, input, opts) {
|
|
|
2472
2664
|
function buildClaudeOptions(ctx, independent) {
|
|
2473
2665
|
const { permissionMode } = ctx.params;
|
|
2474
2666
|
const isPlan = permissionMode === "plan";
|
|
2667
|
+
const model = resolveClaudeModel(ctx);
|
|
2668
|
+
const effort = resolveClaudeEffort(ctx);
|
|
2669
|
+
const settings = resolveClaudeSettings(ctx);
|
|
2475
2670
|
return (abort, resume, isRetry) => ({
|
|
2476
2671
|
// Independent task: resume is what makes the CLI load the transcript, so the first
|
|
2477
2672
|
// attempt drops it and names the session explicitly instead — same file on disk, empty
|
|
@@ -2479,6 +2674,9 @@ function buildClaudeOptions(ctx, independent) {
|
|
|
2479
2674
|
// turn (the history is stashed away), which is exactly what the retry needs.
|
|
2480
2675
|
...independent && !isRetry ? { sessionId: resume } : resume && { resume },
|
|
2481
2676
|
...ctx.cwd && { cwd: ctx.cwd },
|
|
2677
|
+
...model ? { model } : {},
|
|
2678
|
+
...effort ? { effort } : {},
|
|
2679
|
+
...settings ? { settings } : {},
|
|
2482
2680
|
settingSources: ["user", "project", "local"],
|
|
2483
2681
|
// Permission mode: 'plan' (read-only) when requested, else skip all permission checks.
|
|
2484
2682
|
permissionMode: isPlan ? "plan" : "bypassPermissions",
|
|
@@ -2500,15 +2698,16 @@ var claudeSpec = {
|
|
|
2500
2698
|
name: "claude",
|
|
2501
2699
|
runner: {
|
|
2502
2700
|
async run(ctx) {
|
|
2503
|
-
const
|
|
2701
|
+
const runCtx = withClaudePromptEffort(ctx);
|
|
2702
|
+
const independent = runCtx.params.noHistory === true && !!runCtx.sessionId;
|
|
2504
2703
|
if (!independent) {
|
|
2505
|
-
await runSdkLoop(
|
|
2704
|
+
await runSdkLoop(runCtx, buildClaudeOptions(runCtx, false));
|
|
2506
2705
|
return;
|
|
2507
2706
|
}
|
|
2508
|
-
const sessionPath = getClaudeSessionPath(
|
|
2707
|
+
const sessionPath = getClaudeSessionPath(runCtx.cwd, runCtx.sessionId);
|
|
2509
2708
|
const stashed = stashTranscript(sessionPath);
|
|
2510
2709
|
try {
|
|
2511
|
-
await runSdkLoop(
|
|
2710
|
+
await runSdkLoop(runCtx, buildClaudeOptions(runCtx, true));
|
|
2512
2711
|
} finally {
|
|
2513
2712
|
if (stashed) mergeStashedTranscript(sessionPath);
|
|
2514
2713
|
}
|
|
@@ -16841,6 +17040,16 @@ ${summary}`;
|
|
|
16841
17040
|
}
|
|
16842
17041
|
|
|
16843
17042
|
// packages/feature/agent/src/server/engines/builtinAgent/stream.ts
|
|
17043
|
+
function emitUsageUpdate(outputTokens, safeEnqueue) {
|
|
17044
|
+
safeEnqueue(
|
|
17045
|
+
`data: ${JSON.stringify({
|
|
17046
|
+
type: "usage_update",
|
|
17047
|
+
output_tokens: Math.max(0, Math.round(outputTokens))
|
|
17048
|
+
})}
|
|
17049
|
+
|
|
17050
|
+
`
|
|
17051
|
+
);
|
|
17052
|
+
}
|
|
16844
17053
|
function formatToolError(error48) {
|
|
16845
17054
|
if (error48 instanceof Error) return error48.message || String(error48);
|
|
16846
17055
|
if (typeof error48 === "string") return error48;
|
|
@@ -16852,12 +17061,15 @@ function formatToolError(error48) {
|
|
|
16852
17061
|
}
|
|
16853
17062
|
async function consumeStream(fullStream, safeEnqueue, _sessionId, opts) {
|
|
16854
17063
|
let text = "";
|
|
17064
|
+
let progressOutputTokens = 0;
|
|
16855
17065
|
const toolCalls = [];
|
|
16856
17066
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
16857
17067
|
for await (const part of fullStream) {
|
|
16858
17068
|
switch (part.type) {
|
|
16859
17069
|
case "text-delta": {
|
|
16860
17070
|
text += part.text;
|
|
17071
|
+
progressOutputTokens += estimateOutputUnits(part.text);
|
|
17072
|
+
emitUsageUpdate(progressOutputTokens, safeEnqueue);
|
|
16861
17073
|
safeEnqueue(
|
|
16862
17074
|
`data: ${JSON.stringify({
|
|
16863
17075
|
type: "stream_event",
|
|
@@ -16871,12 +17083,16 @@ async function consumeStream(fullStream, safeEnqueue, _sessionId, opts) {
|
|
|
16871
17083
|
);
|
|
16872
17084
|
break;
|
|
16873
17085
|
}
|
|
17086
|
+
case "finish-step":
|
|
17087
|
+
break;
|
|
16874
17088
|
case "tool-call": {
|
|
16875
17089
|
if (text) {
|
|
16876
17090
|
opts?.onTextFlush?.(text);
|
|
16877
17091
|
text = "";
|
|
16878
17092
|
}
|
|
16879
17093
|
const input = part.input || {};
|
|
17094
|
+
progressOutputTokens += estimateOutputUnits(`${part.toolName} ${JSON.stringify(input)}`);
|
|
17095
|
+
emitUsageUpdate(progressOutputTokens, safeEnqueue);
|
|
16880
17096
|
opts?.onToolCall?.(part.toolCallId, part.toolName, input);
|
|
16881
17097
|
safeEnqueue(
|
|
16882
17098
|
`data: ${JSON.stringify({
|
|
@@ -16897,6 +17113,8 @@ async function consumeStream(fullStream, safeEnqueue, _sessionId, opts) {
|
|
|
16897
17113
|
}
|
|
16898
17114
|
case "tool-result": {
|
|
16899
17115
|
const content = String(part.output);
|
|
17116
|
+
progressOutputTokens += estimateOutputUnits(content);
|
|
17117
|
+
emitUsageUpdate(progressOutputTokens, safeEnqueue);
|
|
16900
17118
|
opts?.onToolResult?.(part.toolCallId, content, false);
|
|
16901
17119
|
safeEnqueue(
|
|
16902
17120
|
`data: ${JSON.stringify({
|
|
@@ -16918,6 +17136,8 @@ async function consumeStream(fullStream, safeEnqueue, _sessionId, opts) {
|
|
|
16918
17136
|
// unusable session. Every tool call must close with a result line, success or not.
|
|
16919
17137
|
case "tool-error": {
|
|
16920
17138
|
const content = formatToolError(part.error);
|
|
17139
|
+
progressOutputTokens += estimateOutputUnits(content);
|
|
17140
|
+
emitUsageUpdate(progressOutputTokens, safeEnqueue);
|
|
16921
17141
|
opts?.onToolResult?.(part.toolCallId, content, true);
|
|
16922
17142
|
safeEnqueue(
|
|
16923
17143
|
`data: ${JSON.stringify({
|
|
@@ -16941,8 +17161,6 @@ async function consumeStream(fullStream, safeEnqueue, _sessionId, opts) {
|
|
|
16941
17161
|
// the normal failure path (orchestrator → run `error` event → UI).
|
|
16942
17162
|
case "error":
|
|
16943
17163
|
throw new Error(formatProviderError(part.error), { cause: part.error });
|
|
16944
|
-
case "finish":
|
|
16945
|
-
break;
|
|
16946
17164
|
default:
|
|
16947
17165
|
break;
|
|
16948
17166
|
}
|
|
@@ -17129,12 +17347,12 @@ function resolveEndpoints(p, saved, language) {
|
|
|
17129
17347
|
const id = saved.region || p.regions.defaultFor(language);
|
|
17130
17348
|
return p.regions.byId[id] ?? p.endpoints;
|
|
17131
17349
|
}
|
|
17132
|
-
function
|
|
17350
|
+
function resolveModel(requested, saved, fallback) {
|
|
17133
17351
|
if (typeof requested === "string" && requested.trim()) return requested.trim();
|
|
17134
17352
|
if (saved && saved.trim()) return saved.trim();
|
|
17135
17353
|
return fallback;
|
|
17136
17354
|
}
|
|
17137
|
-
function
|
|
17355
|
+
function buildConfig(p, apiKey, endpoints) {
|
|
17138
17356
|
return {
|
|
17139
17357
|
sessionsRoot: getBuiltinSessionsRoot(p.name),
|
|
17140
17358
|
defaultModel: p.defaultModel,
|
|
@@ -17142,37 +17360,6 @@ function buildBuiltinConfig(p, apiKey, endpoints) {
|
|
|
17142
17360
|
...p.builtinTemperature !== void 0 && { temperature: p.builtinTemperature }
|
|
17143
17361
|
};
|
|
17144
17362
|
}
|
|
17145
|
-
function buildEnv(opts) {
|
|
17146
|
-
const { provider: p, apiKey, model, settings, endpoints } = opts;
|
|
17147
|
-
const wire = p.sdkWireModel ? p.sdkWireModel(model) : model;
|
|
17148
|
-
return sanitizedSpawnEnv({
|
|
17149
|
-
ANTHROPIC_AUTH_TOKEN: void 0,
|
|
17150
|
-
ANTHROPIC_BASE_URL: endpoints.anthropicBaseUrl,
|
|
17151
|
-
ANTHROPIC_API_KEY: apiKey,
|
|
17152
|
-
// both providers accept this as x-api-key
|
|
17153
|
-
ANTHROPIC_MODEL: wire,
|
|
17154
|
-
ANTHROPIC_SMALL_FAST_MODEL: p.smallFastModel ?? wire,
|
|
17155
|
-
CLAUDE_CONFIG_DIR: p.configDir,
|
|
17156
|
-
...p.disablePromptCaching && { DISABLE_PROMPT_CACHING: "1" },
|
|
17157
|
-
CLAUDE_CODE_USE_BEDROCK: "0",
|
|
17158
|
-
CLAUDE_CODE_USE_VERTEX: "0",
|
|
17159
|
-
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
|
|
17160
|
-
...p.sdkEnv?.(model, settings)
|
|
17161
|
-
});
|
|
17162
|
-
}
|
|
17163
|
-
function buildOptions(ctx, env) {
|
|
17164
|
-
return (abort, resume) => ({
|
|
17165
|
-
...resume && { resume },
|
|
17166
|
-
...ctx.cwd && { cwd: ctx.cwd },
|
|
17167
|
-
settingSources: ["user", "project", "local"],
|
|
17168
|
-
permissionMode: "bypassPermissions",
|
|
17169
|
-
allowDangerouslySkipPermissions: true,
|
|
17170
|
-
includePartialMessages: true,
|
|
17171
|
-
abortController: abort,
|
|
17172
|
-
env
|
|
17173
|
-
});
|
|
17174
|
-
}
|
|
17175
|
-
var isBuiltinMode = (params) => params.mode === "builtin";
|
|
17176
17363
|
function makeAnthropicCompatSpec(p) {
|
|
17177
17364
|
return {
|
|
17178
17365
|
name: p.name,
|
|
@@ -17188,14 +17375,13 @@ function makeAnthropicCompatSpec(p) {
|
|
|
17188
17375
|
error: `${p.label} API key is not configured. Open the ${p.label} picker in the chat header to set one.`
|
|
17189
17376
|
};
|
|
17190
17377
|
}
|
|
17191
|
-
const
|
|
17192
|
-
if (
|
|
17193
|
-
|
|
17194
|
-
|
|
17195
|
-
|
|
17196
|
-
|
|
17197
|
-
|
|
17198
|
-
params.model = p.resolveSdkModel(requested, saved.model);
|
|
17378
|
+
const textCheck = requireTextPrompt(params);
|
|
17379
|
+
if (!textCheck.ok) return textCheck;
|
|
17380
|
+
params.model = resolveModel(
|
|
17381
|
+
typeof params.model === "string" ? params.model : void 0,
|
|
17382
|
+
saved.builtinModel,
|
|
17383
|
+
p.defaultModel
|
|
17384
|
+
);
|
|
17199
17385
|
return { ok: true };
|
|
17200
17386
|
},
|
|
17201
17387
|
runner: {
|
|
@@ -17204,13 +17390,7 @@ function makeAnthropicCompatSpec(p) {
|
|
|
17204
17390
|
const settings = await readSettings();
|
|
17205
17391
|
const saved = settings.engines?.[p.name] ?? {};
|
|
17206
17392
|
const endpoints = resolveEndpoints(p, saved, settings.language);
|
|
17207
|
-
|
|
17208
|
-
await runBuiltinAgent(ctx, buildBuiltinConfig(p, apiKey, endpoints));
|
|
17209
|
-
return;
|
|
17210
|
-
}
|
|
17211
|
-
const model = typeof ctx.params.model === "string" ? ctx.params.model : p.defaultModel;
|
|
17212
|
-
const env = buildEnv({ provider: p, apiKey, model, settings: saved, endpoints });
|
|
17213
|
-
await runSdkLoop(ctx, buildOptions(ctx, env));
|
|
17393
|
+
await runBuiltinAgent(ctx, buildConfig(p, apiKey, endpoints));
|
|
17214
17394
|
},
|
|
17215
17395
|
resolveTitle: (cwd, sessionId) => getSessionTitle(cwd, sessionId)
|
|
17216
17396
|
}
|
|
@@ -17218,28 +17398,14 @@ function makeAnthropicCompatSpec(p) {
|
|
|
17218
17398
|
}
|
|
17219
17399
|
|
|
17220
17400
|
// packages/feature/agent/src/server/engines/deepseek.ts
|
|
17221
|
-
var DEEPSEEK_BASE_URL = "https://api.deepseek.com/anthropic";
|
|
17222
|
-
var DEFAULT_MODEL = "deepseek-v4-pro";
|
|
17223
|
-
var SMALL_FAST_MODEL = "deepseek-v4-flash";
|
|
17224
|
-
var ALLOWED_MODELS = /* @__PURE__ */ new Set(["deepseek-v4-flash", "deepseek-v4-pro"]);
|
|
17225
17401
|
var DEEPSEEK_OPENAI_BASE_URL = "https://api.deepseek.com/v1";
|
|
17402
|
+
var DEFAULT_MODEL = "deepseek-v4-pro";
|
|
17226
17403
|
var deepseekProvider = {
|
|
17227
17404
|
name: "deepseek",
|
|
17228
17405
|
label: "DeepSeek",
|
|
17229
|
-
endpoints: {
|
|
17230
|
-
configDir: DEEPSEEK_DIR,
|
|
17406
|
+
endpoints: { openAiBaseUrl: DEEPSEEK_OPENAI_BASE_URL },
|
|
17231
17407
|
defaultModel: DEFAULT_MODEL,
|
|
17232
|
-
apiKey: deepseekApiKey
|
|
17233
|
-
smallFastModel: SMALL_FAST_MODEL,
|
|
17234
|
-
disablePromptCaching: true,
|
|
17235
|
-
// DeepSeek runs its own server-side prefix KV cache
|
|
17236
|
-
// SDK mode is whitelist-gated: with no listing API to validate against, an unknown id
|
|
17237
|
-
// would only fail at the first chat turn.
|
|
17238
|
-
resolveSdkModel(requested, saved) {
|
|
17239
|
-
if (typeof requested === "string" && ALLOWED_MODELS.has(requested)) return requested;
|
|
17240
|
-
if (saved && ALLOWED_MODELS.has(saved)) return saved;
|
|
17241
|
-
return DEFAULT_MODEL;
|
|
17242
|
-
}
|
|
17408
|
+
apiKey: deepseekApiKey
|
|
17243
17409
|
};
|
|
17244
17410
|
var deepseekSpec = makeAnthropicCompatSpec(deepseekProvider);
|
|
17245
17411
|
|
|
@@ -17262,72 +17428,25 @@ var ollamaSpec = {
|
|
|
17262
17428
|
};
|
|
17263
17429
|
|
|
17264
17430
|
// packages/feature/agent/src/server/engines/kimi.ts
|
|
17265
|
-
var KIMI_BASE_URL = "https://api.kimi.com/coding/";
|
|
17266
17431
|
var KIMI_OPENAI_BASE_URL = "https://api.kimi.com/coding/v1";
|
|
17267
17432
|
var DEFAULT_MODEL3 = "kimi-for-coding";
|
|
17268
|
-
var CONTEXT_TOKENS_FALLBACK = {
|
|
17269
|
-
k3: 1048576,
|
|
17270
|
-
"k3-256k": 262144,
|
|
17271
|
-
"kimi-for-coding": 262144,
|
|
17272
|
-
"kimi-for-coding-highspeed": 262144
|
|
17273
|
-
};
|
|
17274
|
-
var DEFAULT_CONTEXT_TOKENS = 262144;
|
|
17275
|
-
function kimiWireModel(model) {
|
|
17276
|
-
return model === "k3" ? "k3[1m]" : model;
|
|
17277
|
-
}
|
|
17278
17433
|
var kimiProvider = {
|
|
17279
17434
|
name: "kimi",
|
|
17280
17435
|
label: "Kimi",
|
|
17281
|
-
endpoints: {
|
|
17282
|
-
configDir: KIMI_DIR,
|
|
17436
|
+
endpoints: { openAiBaseUrl: KIMI_OPENAI_BASE_URL },
|
|
17283
17437
|
defaultModel: DEFAULT_MODEL3,
|
|
17284
17438
|
apiKey: kimiApiKey,
|
|
17285
|
-
sdkWireModel: kimiWireModel,
|
|
17286
17439
|
// Kimi's coding models are thinking-only and reject any temperature but 1 with
|
|
17287
17440
|
// `invalid temperature: only 1 is allowed for this model`, which fails the turn before a
|
|
17288
17441
|
// token streams. Omit the field and take the provider default.
|
|
17289
|
-
builtinTemperature: null
|
|
17290
|
-
// Kimi Code publishes no small/fast tier — the official Claude Code config points every
|
|
17291
|
-
// model slot at the selected model, so ANTHROPIC_SMALL_FAST_MODEL falls through to it.
|
|
17292
|
-
// Both protocols list live models (GET /coding/v1/models), so unlike DeepSeek there is
|
|
17293
|
-
// nothing to whitelist against: pass the id through and let the API reject unknowns.
|
|
17294
|
-
resolveSdkModel(requested, saved) {
|
|
17295
|
-
if (typeof requested === "string" && requested.trim()) return requested.trim();
|
|
17296
|
-
if (saved && saved.trim()) return saved.trim();
|
|
17297
|
-
return DEFAULT_MODEL3;
|
|
17298
|
-
},
|
|
17299
|
-
sdkEnv(model, settings) {
|
|
17300
|
-
const wire = kimiWireModel(model);
|
|
17301
|
-
const contextTokens = settings.modelContextTokens ?? CONTEXT_TOKENS_FALLBACK[model] ?? DEFAULT_CONTEXT_TOKENS;
|
|
17302
|
-
return {
|
|
17303
|
-
// Kimi serves one model per key tier, so every alias the SDK may route to
|
|
17304
|
-
// (subagents, haiku-class helpers) has to resolve to the same id — otherwise the
|
|
17305
|
-
// SDK asks for `claude-3-5-haiku` and gets a 404.
|
|
17306
|
-
ANTHROPIC_DEFAULT_FABLE_MODEL: wire,
|
|
17307
|
-
ANTHROPIC_DEFAULT_OPUS_MODEL: wire,
|
|
17308
|
-
ANTHROPIC_DEFAULT_SONNET_MODEL: wire,
|
|
17309
|
-
ANTHROPIC_DEFAULT_HAIKU_MODEL: wire,
|
|
17310
|
-
CLAUDE_CODE_SUBAGENT_MODEL: wire,
|
|
17311
|
-
// Without these the SDK compacts against Claude's default window and throws away
|
|
17312
|
-
// most of the 256K/1M context this plan pays for.
|
|
17313
|
-
CLAUDE_CODE_MAX_CONTEXT_TOKENS: String(contextTokens),
|
|
17314
|
-
CLAUDE_CODE_AUTO_COMPACT_WINDOW: String(contextTokens),
|
|
17315
|
-
...settings.modelEffort ? { CLAUDE_CODE_EFFORT_LEVEL: settings.modelEffort } : {}
|
|
17316
|
-
};
|
|
17317
|
-
}
|
|
17442
|
+
builtinTemperature: null
|
|
17318
17443
|
};
|
|
17319
17444
|
var kimiSpec = makeAnthropicCompatSpec(kimiProvider);
|
|
17320
17445
|
|
|
17321
17446
|
// packages/feature/agent/src/server/engines/glm.ts
|
|
17322
17447
|
var GLM_REGIONS = {
|
|
17323
|
-
cn: {
|
|
17324
|
-
|
|
17325
|
-
openAiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4"
|
|
17326
|
-
},
|
|
17327
|
-
global: {
|
|
17328
|
-
anthropicBaseUrl: "https://api.z.ai/api/anthropic",
|
|
17329
|
-
openAiBaseUrl: "https://api.z.ai/api/coding/paas/v4"
|
|
17330
|
-
}
|
|
17448
|
+
cn: { openAiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4" },
|
|
17449
|
+
global: { openAiBaseUrl: "https://api.z.ai/api/coding/paas/v4" }
|
|
17331
17450
|
};
|
|
17332
17451
|
var GLM_REGION_IDS = Object.keys(GLM_REGIONS);
|
|
17333
17452
|
var DEFAULT_MODEL4 = "glm-5.2";
|
|
@@ -17340,34 +17459,10 @@ var glmProvider = {
|
|
|
17340
17459
|
// Shared with the picker so the UI cannot claim one host while runs go to another.
|
|
17341
17460
|
defaultFor: defaultRegionForLanguage
|
|
17342
17461
|
},
|
|
17343
|
-
configDir: GLM_DIR,
|
|
17344
17462
|
defaultModel: DEFAULT_MODEL4,
|
|
17345
|
-
apiKey: glmApiKey
|
|
17346
|
-
//
|
|
17347
|
-
//
|
|
17348
|
-
// Pass the id through and let the API reject unknowns.
|
|
17349
|
-
resolveSdkModel(requested, saved) {
|
|
17350
|
-
if (typeof requested === "string" && requested.trim()) return requested.trim();
|
|
17351
|
-
if (saved && saved.trim()) return saved.trim();
|
|
17352
|
-
return DEFAULT_MODEL4;
|
|
17353
|
-
},
|
|
17354
|
-
sdkEnv(model) {
|
|
17355
|
-
return {
|
|
17356
|
-
// GLM serves one model per request, so every alias the SDK may route to has to
|
|
17357
|
-
// resolve to the same id — otherwise it asks for `claude-3-5-haiku` and gets a 404.
|
|
17358
|
-
ANTHROPIC_DEFAULT_FABLE_MODEL: model,
|
|
17359
|
-
ANTHROPIC_DEFAULT_OPUS_MODEL: model,
|
|
17360
|
-
ANTHROPIC_DEFAULT_SONNET_MODEL: model,
|
|
17361
|
-
ANTHROPIC_DEFAULT_HAIKU_MODEL: model,
|
|
17362
|
-
CLAUDE_CODE_SUBAGENT_MODEL: model
|
|
17363
|
-
};
|
|
17364
|
-
}
|
|
17365
|
-
// No CLAUDE_CODE_MAX_CONTEXT_TOKENS: GLM publishes no per-model context metadata, and a
|
|
17366
|
-
// hardcoded table would silently rot as models ship. The SDK default is close enough to
|
|
17367
|
-
// GLM's 200K that guessing is not worth the staleness.
|
|
17368
|
-
//
|
|
17369
|
-
// No builtinTemperature override either: temperature 0 with tools is accepted here
|
|
17370
|
-
// (verified), unlike Kimi's thinking-only models.
|
|
17463
|
+
apiKey: glmApiKey
|
|
17464
|
+
// No builtinTemperature override: temperature 0 with tools is accepted here (verified),
|
|
17465
|
+
// unlike Kimi's thinking-only models.
|
|
17371
17466
|
};
|
|
17372
17467
|
var glmSpec = makeAnthropicCompatSpec(glmProvider);
|
|
17373
17468
|
|
|
@@ -17393,6 +17488,8 @@ var CODEX_TOOL_NAMES = {
|
|
|
17393
17488
|
var CODEX_MCP_NAMESPACE_PREFIX = "mcp__";
|
|
17394
17489
|
var codexMcpToolName = (server, tool2) => `${CODEX_MCP_NAMESPACE_PREFIX}${server}__${tool2}`;
|
|
17395
17490
|
var CODEX_SPAWN_FN_NAME = "spawn_agent";
|
|
17491
|
+
var CODEX_PATCH_FN_NAME = "apply_patch";
|
|
17492
|
+
var CODEX_EXEC_SCRIPT_FN_NAME = "exec";
|
|
17396
17493
|
function codexTodoInput(items) {
|
|
17397
17494
|
return {
|
|
17398
17495
|
todos: (items ?? []).flatMap(
|
|
@@ -17423,6 +17520,80 @@ function codexMcpResultText(item) {
|
|
|
17423
17520
|
if (item.result === null || item.result === void 0) return "";
|
|
17424
17521
|
return typeof item.result === "string" ? item.result : JSON.stringify(item.result, null, 2);
|
|
17425
17522
|
}
|
|
17523
|
+
var JS_STRING_LITERAL_RE = /"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`/g;
|
|
17524
|
+
var JS_SIMPLE_ESCAPES = {
|
|
17525
|
+
n: "\n",
|
|
17526
|
+
r: "\r",
|
|
17527
|
+
t: " ",
|
|
17528
|
+
b: "\b",
|
|
17529
|
+
f: "\f",
|
|
17530
|
+
v: "\v",
|
|
17531
|
+
"0": "\0"
|
|
17532
|
+
};
|
|
17533
|
+
function decodeJsStringLiteral(literal2) {
|
|
17534
|
+
if (literal2.startsWith('"')) {
|
|
17535
|
+
try {
|
|
17536
|
+
return JSON.parse(literal2);
|
|
17537
|
+
} catch {
|
|
17538
|
+
}
|
|
17539
|
+
}
|
|
17540
|
+
return literal2.slice(1, -1).replace(
|
|
17541
|
+
/\\(u\{[0-9a-fA-F]+\}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{2}|[\s\S])/g,
|
|
17542
|
+
(_full, esc2) => {
|
|
17543
|
+
if (esc2.startsWith("u{")) return String.fromCodePoint(parseInt(esc2.slice(2, -1), 16));
|
|
17544
|
+
if (esc2.startsWith("u") || esc2.startsWith("x")) return String.fromCharCode(parseInt(esc2.slice(1), 16));
|
|
17545
|
+
return JS_SIMPLE_ESCAPES[esc2] ?? esc2;
|
|
17546
|
+
}
|
|
17547
|
+
);
|
|
17548
|
+
}
|
|
17549
|
+
function extractPatchLiteral(script) {
|
|
17550
|
+
JS_STRING_LITERAL_RE.lastIndex = 0;
|
|
17551
|
+
let m;
|
|
17552
|
+
while (m = JS_STRING_LITERAL_RE.exec(script)) {
|
|
17553
|
+
const value = decodeJsStringLiteral(m[0]);
|
|
17554
|
+
if (value.includes("*** Begin Patch")) return value;
|
|
17555
|
+
}
|
|
17556
|
+
return null;
|
|
17557
|
+
}
|
|
17558
|
+
function extractExecArgs(script) {
|
|
17559
|
+
const call = script.indexOf("tools.exec_command(");
|
|
17560
|
+
if (call === -1) return null;
|
|
17561
|
+
const open = script.indexOf("{", call);
|
|
17562
|
+
if (open === -1) return null;
|
|
17563
|
+
let depth = 0;
|
|
17564
|
+
let quote = "";
|
|
17565
|
+
for (let i = open; i < script.length; i++) {
|
|
17566
|
+
const ch = script[i];
|
|
17567
|
+
if (quote) {
|
|
17568
|
+
if (ch === "\\") i++;
|
|
17569
|
+
else if (ch === quote) quote = "";
|
|
17570
|
+
continue;
|
|
17571
|
+
}
|
|
17572
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
17573
|
+
quote = ch;
|
|
17574
|
+
continue;
|
|
17575
|
+
}
|
|
17576
|
+
if (ch === "{") depth++;
|
|
17577
|
+
else if (ch === "}" && --depth === 0) {
|
|
17578
|
+
try {
|
|
17579
|
+
const parsed = JSON.parse(script.slice(open, i + 1));
|
|
17580
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
17581
|
+
} catch {
|
|
17582
|
+
return null;
|
|
17583
|
+
}
|
|
17584
|
+
}
|
|
17585
|
+
}
|
|
17586
|
+
return null;
|
|
17587
|
+
}
|
|
17588
|
+
function parseCodexExecScript(script) {
|
|
17589
|
+
const patch = extractPatchLiteral(script);
|
|
17590
|
+
if (patch) return { kind: "patch", patch };
|
|
17591
|
+
const args = extractExecArgs(script);
|
|
17592
|
+
if (args) {
|
|
17593
|
+
return { kind: "exec", command: typeof args.cmd === "string" ? args.cmd : "", args };
|
|
17594
|
+
}
|
|
17595
|
+
return { kind: "unknown" };
|
|
17596
|
+
}
|
|
17426
17597
|
var CODEX_AGENT_DONE = /* @__PURE__ */ new Set(["completed", "errored", "interrupted", "not_found", "failed"]);
|
|
17427
17598
|
function parseCodexSpawnInput(args, extra) {
|
|
17428
17599
|
const prompt = typeof args.message === "string" ? args.message : "";
|
|
@@ -17515,8 +17686,12 @@ function parseCallLines(text, exec2, patch, spawns, mcps) {
|
|
|
17515
17686
|
} catch {
|
|
17516
17687
|
}
|
|
17517
17688
|
exec2.push({ callId: p.call_id, cmd });
|
|
17518
|
-
} else if (p.type === "custom_tool_call" && p.name ===
|
|
17689
|
+
} else if (p.type === "custom_tool_call" && p.name === CODEX_PATCH_FN_NAME) {
|
|
17519
17690
|
patch.push({ callId: p.call_id, files: parsePatchFiles(p.input || "") });
|
|
17691
|
+
} else if (p.type === "custom_tool_call" && p.name === CODEX_EXEC_SCRIPT_FN_NAME) {
|
|
17692
|
+
const script = parseCodexExecScript(p.input || "");
|
|
17693
|
+
if (script.kind === "exec") exec2.push({ callId: p.call_id, cmd: script.command });
|
|
17694
|
+
else if (script.kind === "patch") patch.push({ callId: p.call_id, files: parsePatchFiles(script.patch) });
|
|
17520
17695
|
} else if (p.type === "function_call" && p.name && p.namespace?.startsWith(CODEX_MCP_NAMESPACE_PREFIX)) {
|
|
17521
17696
|
mcps.push({
|
|
17522
17697
|
callId: p.call_id,
|
|
@@ -17643,7 +17818,9 @@ function createCodexEventAdapter(ctx) {
|
|
|
17643
17818
|
const { sessionId } = ctx;
|
|
17644
17819
|
let terminated = false;
|
|
17645
17820
|
let failure = null;
|
|
17821
|
+
let progressOutputTokens = 0;
|
|
17646
17822
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
17823
|
+
const progressItems = /* @__PURE__ */ new Set();
|
|
17647
17824
|
let rolloutPath = null;
|
|
17648
17825
|
let execBase = 0;
|
|
17649
17826
|
let patchBase = 0;
|
|
@@ -17814,6 +17991,14 @@ function createCodexEventAdapter(ctx) {
|
|
|
17814
17991
|
}
|
|
17815
17992
|
emitAgentReports(item);
|
|
17816
17993
|
};
|
|
17994
|
+
const emitOutputProgress = (text, key) => {
|
|
17995
|
+
if (key) {
|
|
17996
|
+
if (progressItems.has(key)) return;
|
|
17997
|
+
progressItems.add(key);
|
|
17998
|
+
}
|
|
17999
|
+
progressOutputTokens += estimateOutputUnits(text);
|
|
18000
|
+
ctx.emit({ type: "usage_update", output_tokens: progressOutputTokens });
|
|
18001
|
+
};
|
|
17817
18002
|
const handle = (event) => {
|
|
17818
18003
|
if (terminated) return;
|
|
17819
18004
|
switch (event.type) {
|
|
@@ -17829,12 +18014,15 @@ function createCodexEventAdapter(ctx) {
|
|
|
17829
18014
|
const item = event.item;
|
|
17830
18015
|
if (!item) break;
|
|
17831
18016
|
if (item.type === "agent_message" && item.text) {
|
|
18017
|
+
emitOutputProgress(item.text);
|
|
17832
18018
|
ctx.emit({ type: "assistant", message: { content: [{ type: "text", text: item.text }] } });
|
|
17833
18019
|
}
|
|
17834
18020
|
if (item.type === "error" && (item.message || item.text)) {
|
|
18021
|
+
emitOutputProgress(item.message || item.text || "");
|
|
17835
18022
|
ctx.emit({ type: "error", error: item.message || item.text });
|
|
17836
18023
|
}
|
|
17837
18024
|
if (item.type === "reasoning" && item.text) {
|
|
18025
|
+
emitOutputProgress(item.text);
|
|
17838
18026
|
ctx.emit({
|
|
17839
18027
|
type: "assistant",
|
|
17840
18028
|
message: { content: [{ type: "text", text: `<details><summary>Reasoning</summary>
|
|
@@ -17847,11 +18035,13 @@ ${item.text}
|
|
|
17847
18035
|
if (item.type === "command_execution") {
|
|
17848
18036
|
const toolUseId = resolveExecToolUseId(item);
|
|
17849
18037
|
if (!pendingToolCalls.has(toolUseId)) {
|
|
18038
|
+
emitOutputProgress(item.command || CODEX_TOOL_NAMES.bash, `command-start:${toolUseId}`);
|
|
17850
18039
|
ctx.emit({
|
|
17851
18040
|
type: "assistant",
|
|
17852
18041
|
message: { content: [{ type: "tool_use", id: toolUseId, name: CODEX_TOOL_NAMES.bash, input: { command: item.command || "" } }] }
|
|
17853
18042
|
});
|
|
17854
18043
|
}
|
|
18044
|
+
emitOutputProgress(item.aggregated_output || `(exit code: ${item.exit_code ?? "unknown"})`, `command-result:${toolUseId}`);
|
|
17855
18045
|
ctx.emit({
|
|
17856
18046
|
type: "user",
|
|
17857
18047
|
message: { content: [{ tool_use_id: toolUseId, content: item.aggregated_output || `(exit code: ${item.exit_code ?? "unknown"})` }] }
|
|
@@ -17859,17 +18049,28 @@ ${item.text}
|
|
|
17859
18049
|
pendingToolCalls.delete(toolUseId);
|
|
17860
18050
|
}
|
|
17861
18051
|
if (item.type === "collab_tool_call") handleCollabItem(item);
|
|
17862
|
-
if (item.type === "mcp_tool_call")
|
|
17863
|
-
|
|
17864
|
-
|
|
18052
|
+
if (item.type === "mcp_tool_call") {
|
|
18053
|
+
emitOutputProgress(`${item.server || "mcp"} ${item.tool || ""} ${JSON.stringify(item.result ?? item.error ?? "")}`);
|
|
18054
|
+
emitMcpCall(item, true);
|
|
18055
|
+
}
|
|
18056
|
+
if (item.type === "web_search") {
|
|
18057
|
+
emitOutputProgress(JSON.stringify(item.action || item.query || "web_search"));
|
|
18058
|
+
emitWebSearch(item);
|
|
18059
|
+
}
|
|
18060
|
+
if (item.type === "todo_list") {
|
|
18061
|
+
emitOutputProgress(JSON.stringify(item.items || []));
|
|
18062
|
+
emitTodoList(item);
|
|
18063
|
+
}
|
|
17865
18064
|
if (item.type === "file_change") {
|
|
17866
18065
|
const toolUseId = resolvePatchToolUseId(item);
|
|
17867
18066
|
if (!pendingToolCalls.has(toolUseId)) {
|
|
18067
|
+
emitOutputProgress(patchResultText(item), `patch-start:${toolUseId}`);
|
|
17868
18068
|
ctx.emit({
|
|
17869
18069
|
type: "assistant",
|
|
17870
18070
|
message: { content: [{ type: "tool_use", id: toolUseId, name: CODEX_TOOL_NAMES.applyPatch, input: patchInput(item) }] }
|
|
17871
18071
|
});
|
|
17872
18072
|
}
|
|
18073
|
+
emitOutputProgress(patchResultText(item), `patch-result:${toolUseId}`);
|
|
17873
18074
|
ctx.emit({
|
|
17874
18075
|
type: "user",
|
|
17875
18076
|
message: { content: [{ tool_use_id: toolUseId, content: patchResultText(item) }] }
|
|
@@ -17884,6 +18085,7 @@ ${item.text}
|
|
|
17884
18085
|
if (item?.type === "command_execution" && item.command) {
|
|
17885
18086
|
const toolUseId = resolveExecToolUseId(item);
|
|
17886
18087
|
pendingToolCalls.set(toolUseId, toolUseId);
|
|
18088
|
+
emitOutputProgress(item.command, `command-start:${toolUseId}`);
|
|
17887
18089
|
ctx.emit({
|
|
17888
18090
|
type: "assistant",
|
|
17889
18091
|
message: { content: [{ type: "tool_use", id: toolUseId, name: CODEX_TOOL_NAMES.bash, input: { command: item.command } }] }
|
|
@@ -17892,6 +18094,7 @@ ${item.text}
|
|
|
17892
18094
|
if (item?.type === "file_change" && (item.changes?.length ?? 0) > 0) {
|
|
17893
18095
|
const toolUseId = resolvePatchToolUseId(item);
|
|
17894
18096
|
pendingToolCalls.set(toolUseId, toolUseId);
|
|
18097
|
+
emitOutputProgress(patchResultText(item), `patch-start:${toolUseId}`);
|
|
17895
18098
|
ctx.emit({
|
|
17896
18099
|
type: "assistant",
|
|
17897
18100
|
message: { content: [{ type: "tool_use", id: toolUseId, name: CODEX_TOOL_NAMES.applyPatch, input: patchInput(item) }] }
|
|
@@ -17949,14 +18152,30 @@ function codexSdkInput(prompt, imageFiles) {
|
|
|
17949
18152
|
];
|
|
17950
18153
|
}
|
|
17951
18154
|
function codexThreadOptions(ctx) {
|
|
18155
|
+
const effort = resolveCodexReasoningEffort(ctx.params.codexReasoningEffort);
|
|
17952
18156
|
return {
|
|
17953
18157
|
...ctx.params.model ? { model: ctx.params.model } : {},
|
|
18158
|
+
...effort ? { modelReasoningEffort: effort } : {},
|
|
17954
18159
|
...ctx.cwd ? { workingDirectory: ctx.cwd } : {},
|
|
17955
18160
|
sandboxMode: "danger-full-access",
|
|
17956
18161
|
approvalPolicy: "never",
|
|
17957
18162
|
skipGitRepoCheck: true
|
|
17958
18163
|
};
|
|
17959
18164
|
}
|
|
18165
|
+
function resolveCodexReasoningEffort(value) {
|
|
18166
|
+
switch (value) {
|
|
18167
|
+
case "minimal":
|
|
18168
|
+
case "low":
|
|
18169
|
+
case "medium":
|
|
18170
|
+
case "high":
|
|
18171
|
+
case "xhigh":
|
|
18172
|
+
case "max":
|
|
18173
|
+
case "ultra":
|
|
18174
|
+
return value;
|
|
18175
|
+
default:
|
|
18176
|
+
return void 0;
|
|
18177
|
+
}
|
|
18178
|
+
}
|
|
17960
18179
|
async function runCodexSdk(ctx) {
|
|
17961
18180
|
const imageFiles = ctx.images && ctx.images.length > 0 ? writeImagesToTemp(ctx.images) : [];
|
|
17962
18181
|
const adapter = createCodexEventAdapter(ctx);
|
|
@@ -18023,6 +18242,7 @@ var en_default2 = {
|
|
|
18023
18242
|
cancel: "Cancel",
|
|
18024
18243
|
delete: "Delete",
|
|
18025
18244
|
close: "Close",
|
|
18245
|
+
minimize: "Minimize",
|
|
18026
18246
|
copy: "Copy",
|
|
18027
18247
|
edit: "Edit",
|
|
18028
18248
|
file: "File",
|
|
@@ -18091,8 +18311,9 @@ var en_default2 = {
|
|
|
18091
18311
|
searchPlaceholder: "Search HTML apps...",
|
|
18092
18312
|
pathLabel: "Absolute path to the .html file",
|
|
18093
18313
|
invalid: "Invalid",
|
|
18314
|
+
builtin: "Built-in",
|
|
18094
18315
|
noDescription: "No description",
|
|
18095
|
-
emptyNoApps: "No HTML apps yet.",
|
|
18316
|
+
emptyNoApps: "No custom HTML apps yet.",
|
|
18096
18317
|
emptyCreatePrefix: "Use",
|
|
18097
18318
|
emptyCreateSuffix: "in chat to create a new app.",
|
|
18098
18319
|
addExisting: "add an existing HTML file",
|
|
@@ -18139,7 +18360,6 @@ var en_default2 = {
|
|
|
18139
18360
|
unstagedNFiles: "Unstaged {{count}} files",
|
|
18140
18361
|
discardFailed: "Discard failed",
|
|
18141
18362
|
discardedNFiles: "Discarded changes in {{count}} files",
|
|
18142
|
-
stagedAllFiles: "All files staged",
|
|
18143
18363
|
deletedFile: "File deleted",
|
|
18144
18364
|
discardedChanges: "Changes discarded",
|
|
18145
18365
|
pastedFile: "Pasted: {{name}}",
|
|
@@ -18481,10 +18701,11 @@ var en_default2 = {
|
|
|
18481
18701
|
startConversation: "Start a conversation",
|
|
18482
18702
|
loadMoreHistory: "Load more...",
|
|
18483
18703
|
scrollUpForMore: "\u2191 Scroll up to load more history",
|
|
18484
|
-
claudeThinking: "Claude is thinking...",
|
|
18485
18704
|
jumpToStart: "Jump to start",
|
|
18486
18705
|
jumpToLatest: "Jump to latest",
|
|
18487
18706
|
copyMessage: "Copy message",
|
|
18707
|
+
copyImage: "Copy image",
|
|
18708
|
+
copiedImage: "Image copied",
|
|
18488
18709
|
forkSession: "Fork session from here",
|
|
18489
18710
|
excerptTurn: "Excerpt this turn to a new session",
|
|
18490
18711
|
imageN: "Image {{index}}",
|
|
@@ -18509,7 +18730,6 @@ var en_default2 = {
|
|
|
18509
18730
|
placeholder: "Type a message, Enter to send (Shift+Enter for new line, paste images; start a line with / (main session) or @ (subagent) for commands, multiple allowed)",
|
|
18510
18731
|
placeholderDisabled: "Generating... you can keep typing (ESC to cancel; if stuck, cancel and resend 'continue')",
|
|
18511
18732
|
imageSizeLimit: "Image size exceeds limit (max 5MB), current: {{size}}MB",
|
|
18512
|
-
stageAll: "Stage all files (git add -A)",
|
|
18513
18733
|
viewGitChanges: "View Git changes",
|
|
18514
18734
|
viewAllComments: "View all comments",
|
|
18515
18735
|
userMessages: "User messages",
|
|
@@ -19225,6 +19445,7 @@ var zh_default = {
|
|
|
19225
19445
|
cancel: "\u53D6\u6D88",
|
|
19226
19446
|
delete: "\u5220\u9664",
|
|
19227
19447
|
close: "\u5173\u95ED",
|
|
19448
|
+
minimize: "\u6700\u5C0F\u5316",
|
|
19228
19449
|
copy: "\u590D\u5236",
|
|
19229
19450
|
edit: "\u7F16\u8F91",
|
|
19230
19451
|
file: "\u6587\u4EF6",
|
|
@@ -19293,8 +19514,9 @@ var zh_default = {
|
|
|
19293
19514
|
searchPlaceholder: "\u641C\u7D22 HTML \u5E94\u7528...",
|
|
19294
19515
|
pathLabel: ".html \u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84",
|
|
19295
19516
|
invalid: "\u65E0\u6548",
|
|
19517
|
+
builtin: "\u5185\u7F6E",
|
|
19296
19518
|
noDescription: "\u65E0\u63CF\u8FF0",
|
|
19297
|
-
emptyNoApps: "\u8FD8\u6CA1\u6709 HTML \u5E94\u7528\u3002",
|
|
19519
|
+
emptyNoApps: "\u8FD8\u6CA1\u6709\u81EA\u5B9A\u4E49 HTML \u5E94\u7528\u3002",
|
|
19298
19520
|
emptyCreatePrefix: "\u5728\u804A\u5929\u91CC\u4F7F\u7528",
|
|
19299
19521
|
emptyCreateSuffix: "\u521B\u5EFA\u65B0\u5E94\u7528\u3002",
|
|
19300
19522
|
addExisting: "\u6DFB\u52A0\u5DF2\u6709 HTML \u6587\u4EF6",
|
|
@@ -19341,7 +19563,6 @@ var zh_default = {
|
|
|
19341
19563
|
unstagedNFiles: "\u5DF2\u53D6\u6D88\u6682\u5B58 {{count}} \u4E2A\u6587\u4EF6",
|
|
19342
19564
|
discardFailed: "\u653E\u5F03\u53D8\u66F4\u5931\u8D25",
|
|
19343
19565
|
discardedNFiles: "\u5DF2\u653E\u5F03 {{count}} \u4E2A\u6587\u4EF6\u7684\u53D8\u66F4",
|
|
19344
|
-
stagedAllFiles: "\u5DF2\u6682\u5B58\u6240\u6709\u6587\u4EF6",
|
|
19345
19566
|
deletedFile: "\u5DF2\u5220\u9664\u6587\u4EF6",
|
|
19346
19567
|
discardedChanges: "\u5DF2\u653E\u5F03\u53D8\u66F4",
|
|
19347
19568
|
pastedFile: "\u5DF2\u7C98\u8D34: {{name}}",
|
|
@@ -19683,10 +19904,11 @@ var zh_default = {
|
|
|
19683
19904
|
startConversation: "\u5F00\u59CB\u5BF9\u8BDD\u5427",
|
|
19684
19905
|
loadMoreHistory: "\u52A0\u8F7D\u66F4\u591A...",
|
|
19685
19906
|
scrollUpForMore: "\u2191 \u5411\u4E0A\u6EDA\u52A8\u52A0\u8F7D\u66F4\u591A\u5386\u53F2",
|
|
19686
|
-
claudeThinking: "Claude \u6B63\u5728\u601D\u8003...",
|
|
19687
19907
|
jumpToStart: "\u8DF3\u8F6C\u5230\u5F00\u59CB",
|
|
19688
19908
|
jumpToLatest: "\u8DF3\u8F6C\u5230\u6700\u65B0",
|
|
19689
19909
|
copyMessage: "\u590D\u5236\u6D88\u606F",
|
|
19910
|
+
copyImage: "\u590D\u5236\u56FE\u7247",
|
|
19911
|
+
copiedImage: "\u5DF2\u590D\u5236\u56FE\u7247",
|
|
19690
19912
|
forkSession: "\u4ECE\u6B64\u5904\u5206\u53C9\u4F1A\u8BDD",
|
|
19691
19913
|
excerptTurn: "\u6458\u5F55\u672C\u8F6E\u5230\u65B0\u4F1A\u8BDD",
|
|
19692
19914
|
imageN: "\u56FE\u7247 {{index}}",
|
|
@@ -19711,7 +19933,6 @@ var zh_default = {
|
|
|
19711
19933
|
placeholder: "\u8F93\u5165\u6D88\u606F\uFF0CEnter \u53D1\u9001\uFF08Shift+Enter \u6362\u884C\uFF0C\u53EF\u7C98\u8D34\u56FE\u7247\uFF1B\u884C\u9996 /\uFF08\u4E3B\u4F1A\u8BDD\uFF09\u6216 @\uFF08\u5B50\u4EE3\u7406\uFF09\u5524\u8D77\u547D\u4EE4\uFF0C\u652F\u6301\u591A\u884C\u591A\u547D\u4EE4\uFF09",
|
|
19712
19934
|
placeholderDisabled: "\u751F\u6210\u4E2D... \u53EF\u7EE7\u7EED\u8F93\u5165 (ESC \u53D6\u6D88;\u5361\u4F4F\u592A\u4E45\u53EF\u53D6\u6D88\u540E\u91CD\u53D1\u300C\u7EE7\u7EED\u300D)",
|
|
19713
19935
|
imageSizeLimit: "\u56FE\u7247\u5927\u5C0F\u8D85\u8FC7\u9650\u5236\uFF08\u6700\u5927 5MB\uFF09\uFF0C\u5F53\u524D\u5927\u5C0F: {{size}}MB",
|
|
19714
|
-
stageAll: "\u6682\u5B58\u6240\u6709\u6587\u4EF6 (git add -A)",
|
|
19715
19936
|
viewGitChanges: "\u67E5\u770B Git \u53D8\u66F4",
|
|
19716
19937
|
viewAllComments: "\u67E5\u770B\u6240\u6709\u8BC4\u8BBA",
|
|
19717
19938
|
userMessages: "\u7528\u6237\u6D88\u606F\u5217\u8868",
|
|
@@ -20497,8 +20718,7 @@ var dispatchEngineMessageEff = (task, engine, startFresh) => Effect_exports.tryP
|
|
|
20497
20718
|
if (!spec) {
|
|
20498
20719
|
throw new Error(`no engine spec for ${engine}`);
|
|
20499
20720
|
}
|
|
20500
|
-
const
|
|
20501
|
-
const noHistory = await readSessionNoHistory(task, engine, builtinLoop);
|
|
20721
|
+
const noHistory = await readSessionNoHistory(task, engine);
|
|
20502
20722
|
const outcome = await dispatchChat(spec, {
|
|
20503
20723
|
prompt: buildTaskPrompt(task),
|
|
20504
20724
|
// Omit sessionId to start a brand-new session when the resume target is gone;
|
|
@@ -20507,7 +20727,6 @@ var dispatchEngineMessageEff = (task, engine, startFresh) => Effect_exports.tryP
|
|
|
20507
20727
|
cwd: task.cwd,
|
|
20508
20728
|
engine,
|
|
20509
20729
|
...task.model && { model: task.model },
|
|
20510
|
-
...builtinLoop && { mode: "builtin" },
|
|
20511
20730
|
...noHistory && { noHistory: true }
|
|
20512
20731
|
});
|
|
20513
20732
|
if (!outcome.ok) {
|
|
@@ -20542,29 +20761,19 @@ var dispatchEngineMessageEff = (task, engine, startFresh) => Effect_exports.tryP
|
|
|
20542
20761
|
})
|
|
20543
20762
|
});
|
|
20544
20763
|
var BUILTIN_LOOP_PATHS = {
|
|
20764
|
+
ollama: getOllamaSessionPath,
|
|
20545
20765
|
deepseek: getDeepseekBuiltinSessionPath,
|
|
20546
20766
|
kimi: getKimiBuiltinSessionPath,
|
|
20547
20767
|
glm: getGlmBuiltinSessionPath
|
|
20548
20768
|
};
|
|
20549
|
-
|
|
20550
|
-
|
|
20551
|
-
kimi: getKimiSessionPath,
|
|
20552
|
-
glm: getGlmSessionPath
|
|
20553
|
-
};
|
|
20554
|
-
function isBuiltinLoopSession(engine, task) {
|
|
20555
|
-
const resolve = BUILTIN_LOOP_PATHS[engine];
|
|
20556
|
-
return !!resolve && existsSync3(resolve(task.cwd, task.sessionId));
|
|
20557
|
-
}
|
|
20558
|
-
async function readSessionNoHistory(task, engine, builtinLoop) {
|
|
20559
|
-
if (engine !== "ollama" && !builtinLoop && engine !== "claude" && engine !== "codex") return false;
|
|
20769
|
+
async function readSessionNoHistory(task, engine) {
|
|
20770
|
+
if (!BUILTIN_LOOP_PATHS[engine] && engine !== "claude" && engine !== "codex") return false;
|
|
20560
20771
|
const state = await readJsonFile(getSessionFilePath(task.cwd), {});
|
|
20561
20772
|
return state.noHistories?.[task.sessionId] === true;
|
|
20562
20773
|
}
|
|
20563
20774
|
function sessionPathFor(engine, task) {
|
|
20564
|
-
|
|
20565
|
-
if (
|
|
20566
|
-
const sdkStorePath = SDK_STORE_PATHS[engine];
|
|
20567
|
-
if (sdkStorePath) return sdkStorePath(task.cwd, task.sessionId);
|
|
20775
|
+
const builtinPath = BUILTIN_LOOP_PATHS[engine];
|
|
20776
|
+
if (builtinPath) return builtinPath(task.cwd, task.sessionId);
|
|
20568
20777
|
if (engine === "codex") return findCodexSessionPath(task.sessionId);
|
|
20569
20778
|
return getClaudeSessionPath(task.cwd, task.sessionId);
|
|
20570
20779
|
}
|