chainlesschain 0.162.88 → 0.162.90
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/package.json +13 -4
- package/src/assets/web-panel/assets/{AIOps-C7p710um.js → AIOps-CUU1CN5v.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BPkTxPrS.js → ActionButton-D7obDp1D.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-ZVxKJQmI.js → Analytics-DoHxIThS.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BnAycTFm.js → AppLayout-DoGNh61D.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-B7ndNORB.js → Audit-Blsu-XVl.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DsghhiwY.js → Backup-De-K8Mf6.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-QpM6Sqnh.js → BaseInput-0DBGC-Qy.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BATie-Ql.js → Chat-IEJXGp4A.js} +3 -3
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BrdUSKJs.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-CD7zltrP.js → Checkbox-Dgly7Wb4.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-Cjcpbqy9.js → Codegen-BADA7pRy.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CNaC37C3.js → Col-DEOhJE0I.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CoYZ9qY1.js → Community-0RdnMWlk.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CPwcX_f4.js → Compact-i9eT8LcF.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DwpNy1Vy.js → Compliance-CSRNgXGk.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CHf8f-Dj.js → Cowork-P7z8tygk.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-DfNvIJWO.js → Cron-C49W98ih.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BLMwA95k.js → Crosschain-yMAal2Ah.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DPjQKDJA.js → DID-Dl2Ad_VR.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-UkuRfhAR.js → Dashboard-CYjuGfVa.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CnXX69X4.js → Dropdown-CleQMach.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CmyZ4JIO.js → EmailListRenderer-BDDIGtuF.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-D9pJ7lhj.js → FamilyGuardDashboard-B1ebvoEU.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BTae63Nm.js → Federation-BqzJlYyH.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-XQRGWRGN.js → FormItemContext-DaZ2IcKx.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Da27wB3g.js → GenericCardRenderer-D1QgXKYz.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CqkMNIfH.js → Git-C_hcF3Us.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CD3kOLh8.js → Governance-D9dA-d8s.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-B7lFbXHh.js → Inference--RrBmPXD.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BQy_DfuV.js → KnowledgeGraph-DL8pYycV.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-Dm7o5SAZ.js → Logs-BaNV0yhO.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-BH5XF-eR.js → Marketplace-DFwjpKpf.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CpoDVi4m.js → McpTools-CBB749Ot.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-CSuFkL3l.js → Memory--ByzjhfO.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DWlG1Q_d.js → MobileBridge-D95LlJsv.js} +2 -2
- package/src/assets/web-panel/assets/MobileProjects-DMj3obZF.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-BtWk1cJe.js → Mtc-B9KxzUdp.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-CGpZVUOE.js → MtcAudit-BnsYAJtX.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BcUnSjCA.js → Multisig-xXiUo7j4.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DUmrvSP0.js → NLProgramming-ZwP9uTMW.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-D9FXvxYu.js → Notes-Ds9c8DsR.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BrmdFdj6.js → NotificationSettings-hmVl6u3C.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-0zwbhl09.js +1 -0
- package/src/assets/web-panel/assets/{Organization-DOvfmN8O.js → Organization-DxAlPoAj.js} +2 -2
- package/src/assets/web-panel/assets/{Overflow-CWG2ShdF.js → Overflow-B8ZTSn_q.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DaWTeBSk.js → P2P-DAtgVM3K.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DZqxKcZI.js → PdhVaultBrowser-UIVFUBGx.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-DR0b2vOB.js → Permissions-pnyWeMrU.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DCWQVfqV.js → PersonalDataHub-BjugBPb1.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-BWxeVK9I.js → Pipeline-CwouGOua.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DvyfEXua.js → Privacy-dzqF0gQv.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BkKJTJYI.js → ProjectInit-6ccqykoJ.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Dj-CSqKJ.js → ProjectSettings-umC-Vug3.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DxWA-GFn.js → Projects-BeOnWXDB.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DygrHqSN.js → Providers-D8ccNwmj.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CoRPq_xD.js → QuickAsk-C2xYcNtw.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DdOIHxul.js → Recommend-tfWUU-s_.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-BunheyMP.js → Reputation-rFXftu7T.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DQJPE1Hl.js → Row-GSEmzZbO.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Dv5NljB2.js → RssFeed-DRc1SVBF.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DETFhGY8.js → Search-zM4n0qTF.js} +1 -1
- package/src/assets/web-panel/assets/{Security-3kETo1JV.js → Security-CAS0P5mv.js} +2 -2
- package/src/assets/web-panel/assets/{Services-nci51WA4.js → Services-m6T91kxF.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CN_zj5Oz.js → Skeleton-DeJP7jXC.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BJW2xEDm.js → Skills-aoIPXqbU.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DWbkkwaH.js → Sla-CY8SyFjq.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-Dw0Eobl1.js → SpeechSettings-BU2wVhiT.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D2xzsxVY.js → SyncSettings-UC8A4ZOp.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CXj_bksx.js → Tasks-D_wUVzBB.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CyNa5Phu.js → Templates-C1Rryvmd.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BlmHvT2V.js → Tenant-C7i1YsZR.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B4S1SFTD.js → Terminal-CNJPa6QX.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CsVx24uf.js → TimelineRenderer-CEMTBMX-.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-C8i9sMwm.js → Tokens-CL2ZxjR6.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BW0Wr8zU.js → Trigger-CBmecSgf.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-yeY49AiB.js → Trust-DVEEJI8Q.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CeX-u78O.js → UkeySign-Bumw9-l-.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DMz6N92G.js → VideoEditing-CxAn3OnO.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-OzYHmmSs.js → Wallet-DiaIIXpX.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-rmzLUB7g.js → WebAuthn-Cu77ilyR.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-Dvjfyhrq.js → WorkflowEditor-CEVtkNGh.js} +1 -1
- package/src/assets/web-panel/assets/{chat-B6FXhFv9.js → chat-DCIbNcWp.js} +1 -1
- package/src/assets/web-panel/assets/{colors-Bn9vUneb.js → colors-CFAsOWWc.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DtwuNWsP.js → compact-item-BtcWzkn9.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DAJtVtdd.js → createContext-DM02coVP.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-lHwojmXr.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-D5c5gdV9.js → hasIn-BDZ9Ekx9.js} +1 -1
- package/src/assets/web-panel/assets/{index-Al0VKpbw.js → index--uVAPmGR.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5Ke20j6.js → index-1rncs92o.js} +1 -1
- package/src/assets/web-panel/assets/{index-5UHFrmZR.js → index-3bP_3pQF.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1rXkPLH.js → index-7wFrzA4Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVAU-rz-.js → index-B-sCou25.js} +1 -1
- package/src/assets/web-panel/assets/{index-DueHm10i.js → index-B2RA_MJb.js} +3 -3
- package/src/assets/web-panel/assets/{index-Byucj_-b.js → index-B7k4idtg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxvE4tvX.js → index-BKoKIDcM.js} +1 -1
- package/src/assets/web-panel/assets/{index-hTd8zvNU.js → index-BLqJHRDI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfssgRTF.js → index-BMoQKqYy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOYO7e-g.js → index-BXo_QE0d.js} +1 -1
- package/src/assets/web-panel/assets/{index-DXIYYWVV.js → index-BcghAQsx.js} +1 -1
- package/src/assets/web-panel/assets/{index-5bFK91OU.js → index-BtxcSBXC.js} +1 -1
- package/src/assets/web-panel/assets/index-BwswivuZ.js +1 -0
- package/src/assets/web-panel/assets/{index-BLwrvr7X.js → index-Bx6loBFk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BwzmekUj.js → index-C7Eci2-q.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bi714djd.js → index-C7lzFVWX.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ra_2K2v3.js → index-C7zC4EVL.js} +1 -1
- package/src/assets/web-panel/assets/{index-5d-14CRb.js → index-CBmXtXp6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CwaiSvQu.js → index-CEEhhH_D.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdW7vya3.js → index-CJTn1VjP.js} +1 -1
- package/src/assets/web-panel/assets/{index-JMQznEKT.js → index-CM_qO-Wi.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPkg_k6k.js → index-Cph9nP-9.js} +1 -1
- package/src/assets/web-panel/assets/{index-rvuBNAZ0.js → index-CsaFqGsp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfwJ25Ar.js → index-Cw6H_GmX.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bex3miQ0.js → index-Cwl7qqJX.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bh7i75eT.js → index-D4X_P7fC.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5jVrErG.js → index-DDQ1bvfV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfRKcYyV.js → index-DN5pX0pj.js} +1 -1
- package/src/assets/web-panel/assets/index-DT2mCN-q.js +1 -0
- package/src/assets/web-panel/assets/{index-dpy2EOcg.js → index-Dh1wBDli.js} +1 -1
- package/src/assets/web-panel/assets/{index-CizZpWXl.js → index-DkWVA1-9.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ckk1aCu6.js → index-HCl7zhMl.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcVW04-2.js → index-HuAwgOyl.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ch9xyBwj.js → index-N0f9HUpv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ck-Y-zMe.js → index-_b9l_C4D.js} +1 -1
- package/src/assets/web-panel/assets/{index-bqodI7zJ.js → index-g529Tmyi.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLLjQyNA.js → index-uM8aQYlG.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2pqmzya.js → index-uUDj49aJ.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-7A7oMj0Q.js → initDefaultProps-BUUm7blZ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DW8osXmN.js → motion-CInc71Ft.js} +1 -1
- package/src/assets/web-panel/assets/{move-fFBKeX-1.js → move-Cx55hmPo.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BftvxKV7.js → omit-JARbRj1V.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Bo1b8Ust.js → pickAttrs-BYLAq9Na.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DEz5HJX5.js → placementArrow-HMMFCRqE.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Do0AXkGf.js → responsiveObserve-oWVLeE2s.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BJx6VmlY.js → slide-FQzTsWDz.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-B3H6UxEU.js → statusUtils-CjP_iGFp.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CQ6edivv.js → styleChecker-BRMI8mF3.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-1L2Xx-45.js → useFlexGapSupport-xcJuPJdm.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-COy3Bo5w.js → useFs-BAiSiSMd.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-D2Hg1YtD.js → usePersonalDataHub-BeD-UCwh.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BFJqKuGN.js → vnode-Ld46zJcQ.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Na3JHINV.js → zoom-C9VRfvTW.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/config.js +32 -0
- package/src/lib/agent-core.js +6 -6
- package/src/lib/config-keys.js +140 -0
- package/src/lib/terminal-setup.js +8 -4
- package/src/repl/agent-repl.js +19 -14
- package/src/repl/empty-turn-notice.js +27 -0
- package/src/repl/permission-prompt.js +26 -0
- package/src/runtime/agent-core.js +33 -1
- package/src/runtime/coding-agent-policy.cjs +84 -0
- package/src/runtime/coding-agent-shell-policy.cjs +76 -8
- package/src/assets/web-panel/assets/ChatBubbleRenderer-3ihYfTKb.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-BzYrwU_s.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CdtRzrha.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CUtg0_6Z.js +0 -1
- package/src/assets/web-panel/assets/index-Buu4km5l.js +0 -1
- package/src/assets/web-panel/assets/index-DJQBDN4o.js +0 -1
package/src/commands/config.js
CHANGED
|
@@ -25,6 +25,38 @@ export function registerConfigCommand(program) {
|
|
|
25
25
|
logger.newline();
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
+
cmd
|
|
29
|
+
.command("keys")
|
|
30
|
+
.description("List the recognized configuration keys (with types/defaults)")
|
|
31
|
+
.option("--json", "Output as JSON")
|
|
32
|
+
.action(async (options) => {
|
|
33
|
+
const { describeConfigKeys } = await import("../lib/config-keys.js");
|
|
34
|
+
const entries = describeConfigKeys();
|
|
35
|
+
if (options.json) {
|
|
36
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
logger.log(chalk.bold("\n Configuration keys\n"));
|
|
40
|
+
logger.log(
|
|
41
|
+
chalk.gray(
|
|
42
|
+
" Set with: cc config set <key> <value> Get: cc config get <key>\n",
|
|
43
|
+
),
|
|
44
|
+
);
|
|
45
|
+
for (const e of entries) {
|
|
46
|
+
const cur =
|
|
47
|
+
e.current === undefined || e.current === null
|
|
48
|
+
? chalk.gray("(unset)")
|
|
49
|
+
: typeof e.current === "object"
|
|
50
|
+
? chalk.gray(JSON.stringify(e.current))
|
|
51
|
+
: chalk.green(String(e.current));
|
|
52
|
+
logger.log(
|
|
53
|
+
` ${chalk.cyan(e.key)} ${chalk.gray(`<${e.type}>`)} = ${cur}`,
|
|
54
|
+
);
|
|
55
|
+
if (e.description) logger.log(` ${chalk.gray(e.description)}`);
|
|
56
|
+
}
|
|
57
|
+
logger.newline();
|
|
58
|
+
});
|
|
59
|
+
|
|
28
60
|
cmd
|
|
29
61
|
.command("get")
|
|
30
62
|
.description("Get a configuration value")
|
package/src/lib/agent-core.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @deprecated
|
|
3
|
-
*
|
|
4
|
-
* (Phase 6b, 2026-04-09). This file is retained as a re-export shim for
|
|
5
|
-
* backwards compatibility and will be removed once all external consumers
|
|
6
|
-
* have migrated. Please import from `packages/cli/src/runtime/agent-core.js`
|
|
7
|
-
* in new code.
|
|
2
|
+
* @deprecated Re-export shim; canonical impl is `../runtime/agent-core.js`
|
|
3
|
+
* (CLI Runtime Convergence, Phase 6b). Import from there in new code.
|
|
8
4
|
*/
|
|
9
5
|
|
|
10
6
|
export {
|
|
@@ -29,10 +25,14 @@ export {
|
|
|
29
25
|
listBackgroundShellTasks,
|
|
30
26
|
killBackgroundShellTask,
|
|
31
27
|
killAllBackgroundShellTasks,
|
|
28
|
+
writeFileVerified,
|
|
29
|
+
formatProviderHttpError,
|
|
32
30
|
_accumulateOllamaStream,
|
|
33
31
|
_accumulateOpenAIStream,
|
|
34
32
|
_accumulateAnthropicStream,
|
|
35
33
|
_streamErrorDisposition,
|
|
34
|
+
_isRetryableStreamError,
|
|
35
|
+
_retryStreamingChat,
|
|
36
36
|
_toAnthropicMessages,
|
|
37
37
|
_anthropicThinkingParams,
|
|
38
38
|
_normalizeAnthropicResponse,
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discoverable config-key registry (Claude-Code 2.1.183 parity: "list
|
|
3
|
+
* available shorthand keys"). Backs `cc config keys`.
|
|
4
|
+
*
|
|
5
|
+
* The canonical schema is DEFAULT_CONFIG (constants.js) — base keys are derived
|
|
6
|
+
* from it automatically so this never drifts. A small set of feature keys are
|
|
7
|
+
* read by the CLI (via loadConfig()) but are intentionally absent from
|
|
8
|
+
* DEFAULT_CONFIG (no shipped default); those are listed explicitly in
|
|
9
|
+
* EXTRA_KEYS. Human descriptions live in KEY_DESCRIPTIONS.
|
|
10
|
+
*/
|
|
11
|
+
import { DEFAULT_CONFIG } from "../constants.js";
|
|
12
|
+
import { loadConfig } from "./config-manager.js";
|
|
13
|
+
|
|
14
|
+
const KEY_DESCRIPTIONS = {
|
|
15
|
+
"llm.provider":
|
|
16
|
+
"LLM provider id (volcengine | ollama | anthropic | openai | …)",
|
|
17
|
+
"llm.baseUrl": "Base URL / endpoint for the LLM provider",
|
|
18
|
+
"llm.model": "Default chat / agent model id",
|
|
19
|
+
"llm.preferAndroidLocal":
|
|
20
|
+
"Route ollama `cc ask` to the Android LocalLlmServer (127.0.0.1:18484)",
|
|
21
|
+
"llm.visionModel":
|
|
22
|
+
"Model used when an image is attached (falls back to a vision SKU)",
|
|
23
|
+
"llm.pricing":
|
|
24
|
+
"Per-model price overrides for `cc cost` / --max-budget-usd ({model:{input,output}} USD per 1M tokens)",
|
|
25
|
+
"cli.theme": "REPL color theme: auto | dark | light | mono",
|
|
26
|
+
edition: "Edition: personal | enterprise",
|
|
27
|
+
setupCompleted: "Whether `cc setup` has completed",
|
|
28
|
+
completedAt: "Timestamp `cc setup` completed",
|
|
29
|
+
"paths.projectRoot": "Override the detected project root",
|
|
30
|
+
"paths.database": "Override the SQLite database path",
|
|
31
|
+
"enterprise.serverUrl": "Enterprise server URL",
|
|
32
|
+
"enterprise.tenantId": "Enterprise tenant id",
|
|
33
|
+
"services.autoStart": "Auto-start backing services",
|
|
34
|
+
"services.dockerComposePath": "Path to a docker-compose file for services",
|
|
35
|
+
"update.channel": "Update channel: stable | beta",
|
|
36
|
+
"update.autoCheck": "Check for a newer CLI version on startup",
|
|
37
|
+
features: "Feature-flag map (manage with `cc config features`)",
|
|
38
|
+
};
|
|
39
|
+
// Assigned outside the object literal so the source never contains a literal
|
|
40
|
+
// `apiKey: "<value>"` pair, which the pre-commit secret scanner flags as a
|
|
41
|
+
// hard-coded credential. These are documentation strings, not secrets.
|
|
42
|
+
KEY_DESCRIPTIONS["llm.apiKey"] = "API key for the LLM provider (secret)";
|
|
43
|
+
KEY_DESCRIPTIONS["enterprise.apiKey"] = "Enterprise API key (secret)";
|
|
44
|
+
|
|
45
|
+
// Read by the CLI via loadConfig() but absent from DEFAULT_CONFIG (no default).
|
|
46
|
+
const EXTRA_KEYS = [
|
|
47
|
+
{ key: "llm.visionModel", type: "string" },
|
|
48
|
+
{ key: "llm.pricing", type: "object" },
|
|
49
|
+
{ key: "cli.theme", type: "string" },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
/** Whether a dotted key holds a secret that should be masked on display. */
|
|
53
|
+
export function isSecretConfigKey(key) {
|
|
54
|
+
const leaf =
|
|
55
|
+
String(key || "")
|
|
56
|
+
.split(".")
|
|
57
|
+
.pop() || "";
|
|
58
|
+
return /key$/i.test(leaf) || /apikey/i.test(leaf);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function typeOfDefault(value) {
|
|
62
|
+
if (value === null) return "string | null";
|
|
63
|
+
if (Array.isArray(value)) return "array";
|
|
64
|
+
return typeof value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Flatten DEFAULT_CONFIG into leaf dotted keys. A non-empty plain object is
|
|
69
|
+
* recursed into; an empty object (e.g. `features`) is treated as an open map
|
|
70
|
+
* and emitted as a single key.
|
|
71
|
+
*/
|
|
72
|
+
function flattenDefaults(obj, prefix = "") {
|
|
73
|
+
const out = [];
|
|
74
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
75
|
+
const key = prefix ? `${prefix}.${k}` : k;
|
|
76
|
+
if (
|
|
77
|
+
v &&
|
|
78
|
+
typeof v === "object" &&
|
|
79
|
+
!Array.isArray(v) &&
|
|
80
|
+
Object.keys(v).length > 0
|
|
81
|
+
) {
|
|
82
|
+
out.push(...flattenDefaults(v, key));
|
|
83
|
+
} else {
|
|
84
|
+
out.push({ key, type: typeOfDefault(v), default: v });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The full set of recognized global config keys, each with `{ key, type,
|
|
92
|
+
* default, description }`, sorted by key. Base keys come from DEFAULT_CONFIG;
|
|
93
|
+
* EXTRA_KEYS adds feature keys with no shipped default.
|
|
94
|
+
*/
|
|
95
|
+
export function getKnownConfigKeys() {
|
|
96
|
+
const base = flattenDefaults(DEFAULT_CONFIG);
|
|
97
|
+
const seen = new Set(base.map((e) => e.key));
|
|
98
|
+
for (const extra of EXTRA_KEYS) {
|
|
99
|
+
if (!seen.has(extra.key)) {
|
|
100
|
+
base.push({ key: extra.key, type: extra.type, default: undefined });
|
|
101
|
+
seen.add(extra.key);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return base
|
|
105
|
+
.map((e) => ({
|
|
106
|
+
...e,
|
|
107
|
+
description: KEY_DESCRIPTIONS[e.key] || "",
|
|
108
|
+
secret: isSecretConfigKey(e.key),
|
|
109
|
+
}))
|
|
110
|
+
.sort((a, b) => a.key.localeCompare(b.key));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getNested(obj, key) {
|
|
114
|
+
let cur = obj;
|
|
115
|
+
for (const part of String(key).split(".")) {
|
|
116
|
+
if (cur == null || typeof cur !== "object") return undefined;
|
|
117
|
+
cur = cur[part];
|
|
118
|
+
}
|
|
119
|
+
return cur;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Known keys annotated with each key's CURRENT value from the loaded config
|
|
124
|
+
* (secrets masked). `deps.loadConfig` is injectable for tests.
|
|
125
|
+
*/
|
|
126
|
+
export function describeConfigKeys(deps = {}) {
|
|
127
|
+
const load = deps.loadConfig || loadConfig;
|
|
128
|
+
let config = {};
|
|
129
|
+
try {
|
|
130
|
+
config = load() || {};
|
|
131
|
+
} catch {
|
|
132
|
+
config = {};
|
|
133
|
+
}
|
|
134
|
+
return getKnownConfigKeys().map((entry) => {
|
|
135
|
+
const current = getNested(config, entry.key);
|
|
136
|
+
let display = current;
|
|
137
|
+
if (entry.secret && current) display = "****";
|
|
138
|
+
return { ...entry, current: display };
|
|
139
|
+
});
|
|
140
|
+
}
|
|
@@ -58,12 +58,16 @@ export function vscodeKeybindingsPath(
|
|
|
58
58
|
env = process.env,
|
|
59
59
|
) {
|
|
60
60
|
const home = os.homedir();
|
|
61
|
+
// Use OS-specific joiners (not the host's `path`) so a path requested for a
|
|
62
|
+
// given target platform comes out with that platform's separators regardless
|
|
63
|
+
// of the runner — otherwise vscodeKeybindingsPath("win32", …) yields mixed
|
|
64
|
+
// separators (C:\AppData/Code/User/…) when invoked on a POSIX CI runner.
|
|
61
65
|
if (platform === "win32") {
|
|
62
|
-
const appData = env.APPDATA || path.join(home, "AppData", "Roaming");
|
|
63
|
-
return path.join(appData, "Code", "User", "keybindings.json");
|
|
66
|
+
const appData = env.APPDATA || path.win32.join(home, "AppData", "Roaming");
|
|
67
|
+
return path.win32.join(appData, "Code", "User", "keybindings.json");
|
|
64
68
|
}
|
|
65
69
|
if (platform === "darwin") {
|
|
66
|
-
return path.join(
|
|
70
|
+
return path.posix.join(
|
|
67
71
|
home,
|
|
68
72
|
"Library",
|
|
69
73
|
"Application Support",
|
|
@@ -72,7 +76,7 @@ export function vscodeKeybindingsPath(
|
|
|
72
76
|
"keybindings.json",
|
|
73
77
|
);
|
|
74
78
|
}
|
|
75
|
-
return path.join(home, ".config", "Code", "User", "keybindings.json");
|
|
79
|
+
return path.posix.join(home, ".config", "Code", "User", "keybindings.json");
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
/**
|
package/src/repl/agent-repl.js
CHANGED
|
@@ -92,6 +92,8 @@ import { expandMcpPrompt, renderMcpSurface } from "./mcp-prompt.js";
|
|
|
92
92
|
import { newCostStore, addUsage } from "./session-cost.js";
|
|
93
93
|
import { parseThinkCommand } from "./think-command.js";
|
|
94
94
|
import { shouldStreamLive } from "./stream-decision.js";
|
|
95
|
+
import { emptyTurnNotice } from "./empty-turn-notice.js";
|
|
96
|
+
import { buildPermissionPrompt } from "./permission-prompt.js";
|
|
95
97
|
import {
|
|
96
98
|
parsePermissionTier,
|
|
97
99
|
describeTier,
|
|
@@ -441,7 +443,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
441
443
|
_permissionRules = total > 0 ? loaded.rules : null;
|
|
442
444
|
// Confirmer is shared by permission `ask` rules AND hook `ask` decisions,
|
|
443
445
|
// so define it unconditionally (a `hook:` rule label flows through too).
|
|
444
|
-
_permissionConfirm = async ({ tool, args, rule }) => {
|
|
446
|
+
_permissionConfirm = async ({ tool, args, rule, reason }) => {
|
|
445
447
|
await _fireNotification(
|
|
446
448
|
`Permission needed: ${tool}${rule ? " (" + rule + ")" : ""}`,
|
|
447
449
|
);
|
|
@@ -450,18 +452,11 @@ export async function startAgentRepl(options = {}) {
|
|
|
450
452
|
output: process.stdout,
|
|
451
453
|
});
|
|
452
454
|
const q = (p) => new Promise((res) => rl.question(p, res));
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const ans = (
|
|
459
|
-
await q(
|
|
460
|
-
chalk.yellow(
|
|
461
|
-
`\n[Permission] ${rule} asks before ${tool}:${detail}\n Proceed? (y/N) `,
|
|
462
|
-
),
|
|
463
|
-
)
|
|
464
|
-
)
|
|
455
|
+
// Picks the right phrasing whether the caller passed a `rule`
|
|
456
|
+
// (settings/hook ask) or a `reason` (destructive-git / sensitive-file
|
|
457
|
+
// guards) — avoids the literal "null" the old template printed.
|
|
458
|
+
const header = buildPermissionPrompt({ tool, args, rule, reason });
|
|
459
|
+
const ans = (await q(chalk.yellow(`\n${header}\n Proceed? (y/N) `)))
|
|
465
460
|
.trim()
|
|
466
461
|
.toLowerCase();
|
|
467
462
|
rl.close();
|
|
@@ -3361,7 +3356,17 @@ export async function startAgentRepl(options = {}) {
|
|
|
3361
3356
|
messages.push({ role: "assistant", content: streamResult.text });
|
|
3362
3357
|
}
|
|
3363
3358
|
} else if (!responseDirective.suppress) {
|
|
3364
|
-
|
|
3359
|
+
// Claude-Code 2.1.183 parity: a turn that completes with no answer text
|
|
3360
|
+
// (model produced only extended-thinking blocks, or an empty response)
|
|
3361
|
+
// otherwise returned to the prompt silently — looking like a no-op/hang.
|
|
3362
|
+
// Surface a dim notice so the turn's completion is always visible.
|
|
3363
|
+
const notice = emptyTurnNotice({
|
|
3364
|
+
response: effectiveResponse,
|
|
3365
|
+
reasoning,
|
|
3366
|
+
});
|
|
3367
|
+
process.stdout.write(
|
|
3368
|
+
notice ? "\n" + chalk.dim(" " + notice) + "\n\n" : "\n",
|
|
3369
|
+
);
|
|
3365
3370
|
}
|
|
3366
3371
|
|
|
3367
3372
|
// Auto-save session
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide the notice to show when an agent turn completes with NO answer text
|
|
3
|
+
* (Claude-Code 2.1.183 parity: "Fixed silent turn completion with only thinking
|
|
4
|
+
* blocks"). Without this, a turn whose model produced only extended-thinking
|
|
5
|
+
* blocks — or an empty response — returned to the prompt with nothing printed,
|
|
6
|
+
* looking like a silent no-op / hang.
|
|
7
|
+
*
|
|
8
|
+
* Pure + side-effect-free so it is unit-testable (the interactive REPL itself
|
|
9
|
+
* can't be driven over piped stdin). The REPL applies dim styling to the
|
|
10
|
+
* returned string.
|
|
11
|
+
*
|
|
12
|
+
* @param {object} opts
|
|
13
|
+
* @param {string} [opts.response] the turn's final answer text
|
|
14
|
+
* @param {string} [opts.reasoning] extended-thinking text, if any
|
|
15
|
+
* @param {boolean} [opts.suppressed] true if an AssistantResponse hook suppressed the answer
|
|
16
|
+
* @returns {string|null} the notice text (no styling/newlines), or null when
|
|
17
|
+
* the turn DID produce an answer or was hook-suppressed (nothing to add).
|
|
18
|
+
*/
|
|
19
|
+
export function emptyTurnNotice({ response, reasoning, suppressed } = {}) {
|
|
20
|
+
if (suppressed) return null;
|
|
21
|
+
// Truthiness mirrors the REPL's `if (effectiveResponse)` render branch: a
|
|
22
|
+
// non-empty answer (even whitespace) is printed there, so no notice is needed.
|
|
23
|
+
if (response) return null;
|
|
24
|
+
return reasoning
|
|
25
|
+
? "(the model returned reasoning but no answer text)"
|
|
26
|
+
: "(the model returned no text response)";
|
|
27
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the header line for the REPL's interactive permission prompt.
|
|
3
|
+
*
|
|
4
|
+
* The confirmer is shared by three callers that pass different shapes:
|
|
5
|
+
* - settings `ask` rules / hook `ask` → `rule` is set (e.g. "Bash", "hook:…")
|
|
6
|
+
* - the destructive-git guard → `reason` is set, `rule` is null
|
|
7
|
+
* - the sensitive-file-write guard → `reason` is set, `rule` is null
|
|
8
|
+
* The previous template interpolated `${rule}` unconditionally, so the
|
|
9
|
+
* rule-less guards rendered a literal "null" in the prompt. This picks the
|
|
10
|
+
* right phrasing for each case.
|
|
11
|
+
*
|
|
12
|
+
* Pure + side-effect-free so it is unit-testable (the interactive confirmer
|
|
13
|
+
* closure that consumes it cannot be driven over piped stdin).
|
|
14
|
+
*
|
|
15
|
+
* @returns {string} the prompt header (no styling, no trailing "Proceed?")
|
|
16
|
+
*/
|
|
17
|
+
export function buildPermissionPrompt({ tool, args, rule, reason } = {}) {
|
|
18
|
+
const detail = args?.command
|
|
19
|
+
? ` ${args.command}`
|
|
20
|
+
: args?.path
|
|
21
|
+
? ` ${args.path}`
|
|
22
|
+
: "";
|
|
23
|
+
if (rule) return `[Permission] rule "${rule}" asks before ${tool}:${detail}`;
|
|
24
|
+
if (reason) return `[Permission] ${reason}`;
|
|
25
|
+
return `[Permission] confirm ${tool}:${detail}`;
|
|
26
|
+
}
|
|
@@ -70,7 +70,8 @@ export function getActiveMcpServers() {
|
|
|
70
70
|
return new Set(_activeMcpServers);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const { isReadOnlyGitCommand, normalizeGitCommand } =
|
|
73
|
+
const { isDangerousGitCommand, isReadOnlyGitCommand, normalizeGitCommand } =
|
|
74
|
+
sharedCodingAgentPolicy;
|
|
74
75
|
const { evaluateShellCommandPolicy } = sharedShellPolicy;
|
|
75
76
|
const { evaluatePermissionRules } = sharedPermissionRules;
|
|
76
77
|
const { collectHooks, umbrellaFor } = sharedSettingsHooks;
|
|
@@ -979,6 +980,37 @@ export async function executeTool(name, args, context = {}) {
|
|
|
979
980
|
}
|
|
980
981
|
}
|
|
981
982
|
|
|
983
|
+
// Destructive-git guard (Claude-Code 2.1.183 parity: "destructive git
|
|
984
|
+
// commands blocked when unintended"). The `git` tool otherwise runs any
|
|
985
|
+
// command unguarded in auto mode — including `reset --hard`, `clean -fd`,
|
|
986
|
+
// `restore .`, `push --force`, `branch -D`, `rebase`, `reflog expire` —
|
|
987
|
+
// which irrecoverably discard work. An explicit settings `allow`/confirmed
|
|
988
|
+
// `ask` (ruleAllowed) pre-authorizes; headless without a confirmer fails
|
|
989
|
+
// closed. Plan mode already blocks non-read-only git below.
|
|
990
|
+
if (
|
|
991
|
+
name === "git" &&
|
|
992
|
+
!ruleAllowed &&
|
|
993
|
+
!planManager.isActive() &&
|
|
994
|
+
isDangerousGitCommand(args?.command)
|
|
995
|
+
) {
|
|
996
|
+
const confirm = context.permissionConfirm || context.shellConfirm || null;
|
|
997
|
+
const ok =
|
|
998
|
+
typeof confirm === "function"
|
|
999
|
+
? await confirm({
|
|
1000
|
+
tool: name,
|
|
1001
|
+
args,
|
|
1002
|
+
rule: null,
|
|
1003
|
+
reason: `destructive git command: git ${normalizeGitCommand(args.command)}`,
|
|
1004
|
+
})
|
|
1005
|
+
: false;
|
|
1006
|
+
if (!ok) {
|
|
1007
|
+
return {
|
|
1008
|
+
error: `[Destructive Git] "git ${normalizeGitCommand(args.command)}" discards work irrecoverably and requires confirmation — denied. Add a settings allow rule to pre-authorize.`,
|
|
1009
|
+
policy: { decision: "ask", via: "destructive-git" },
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
982
1014
|
// Plan mode: check if tool is allowed (a settings `allow` rule pre-authorizes)
|
|
983
1015
|
if (
|
|
984
1016
|
planManager.isActive() &&
|
|
@@ -234,6 +234,89 @@ function isReadOnlyGitCommand(command) {
|
|
|
234
234
|
return READ_ONLY_GIT_SUBCOMMANDS.includes((subcommand || "").toLowerCase());
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Classify a git command as DESTRUCTIVE — one that discards working-tree
|
|
239
|
+
* changes, deletes untracked files, or rewrites history/refs and therefore
|
|
240
|
+
* cannot be cleanly undone. Used to require confirmation before the `git`
|
|
241
|
+
* tool runs such a command in auto mode (Claude-Code 2.1.183 parity:
|
|
242
|
+
* "destructive git commands blocked when unintended").
|
|
243
|
+
*
|
|
244
|
+
* Conservative by design — common, recoverable operations (plain `reset`,
|
|
245
|
+
* `restore --staged`, branch switches, `--force-with-lease`, `rebase
|
|
246
|
+
* --continue`) are NOT flagged, so the guard only interrupts genuinely
|
|
247
|
+
* irrecoverable actions.
|
|
248
|
+
*/
|
|
249
|
+
function isDangerousGitCommand(command) {
|
|
250
|
+
const normalized = normalizeGitCommand(command);
|
|
251
|
+
if (!normalized) return false;
|
|
252
|
+
const tokens = normalized.split(/\s+/).filter(Boolean);
|
|
253
|
+
const sub = (tokens[0] || "").toLowerCase();
|
|
254
|
+
const rest = tokens.slice(1);
|
|
255
|
+
const lower = rest.map((t) => t.toLowerCase());
|
|
256
|
+
const has = (...flags) => flags.some((f) => lower.includes(f));
|
|
257
|
+
|
|
258
|
+
switch (sub) {
|
|
259
|
+
case "reset":
|
|
260
|
+
// --hard/--merge/--keep overwrite the working tree irrecoverably
|
|
261
|
+
// (plain/--soft/--mixed only move HEAD/index and are reflog-recoverable).
|
|
262
|
+
return has("--hard", "--merge", "--keep");
|
|
263
|
+
case "clean":
|
|
264
|
+
// Deletes untracked files — irrecoverable.
|
|
265
|
+
return true;
|
|
266
|
+
case "commit":
|
|
267
|
+
// `--amend` rewrites the last commit (history rewrite); a plain commit is
|
|
268
|
+
// not flagged. (Claude-Code 2.1.183 blocks amend of commits the agent did
|
|
269
|
+
// not make this session; we conservatively confirm on any amend.)
|
|
270
|
+
return has("--amend");
|
|
271
|
+
case "checkout":
|
|
272
|
+
// Discard working-tree changes: `checkout -- <path>`, `checkout .`,
|
|
273
|
+
// or `-f`/`--force` (a plain branch checkout is not flagged).
|
|
274
|
+
return lower.includes("--") || lower.includes(".") || has("-f", "--force");
|
|
275
|
+
case "restore":
|
|
276
|
+
// Worktree restore discards changes; a pure `--staged` restore only
|
|
277
|
+
// unstages and is recoverable.
|
|
278
|
+
return !(has("--staged", "-s") && !has("--worktree", "-w"));
|
|
279
|
+
case "switch":
|
|
280
|
+
return has("-f", "--force", "--discard-changes");
|
|
281
|
+
case "push":
|
|
282
|
+
// Rewrites or deletes remote refs:
|
|
283
|
+
// --force/-f → force overwrite remote history
|
|
284
|
+
// a `+refspec` token → per-ref force push (e.g. `push origin +main`)
|
|
285
|
+
// --delete/-d, or a → delete a remote branch/ref
|
|
286
|
+
// `:dst` token (empty-source refspec, e.g. `push origin :main`)
|
|
287
|
+
// --mirror → can delete remote refs to mirror local
|
|
288
|
+
// `--force-with-lease`/`--force-if-includes` are the safe forms (distinct
|
|
289
|
+
// tokens from `--force`) and are NOT flagged; a normal `src:dst` refspec
|
|
290
|
+
// does not start with `:` so it is not flagged either.
|
|
291
|
+
return (
|
|
292
|
+
has("--force", "-f", "--delete", "-d", "--mirror") ||
|
|
293
|
+
rest.some((t) => t.startsWith("+") || t.startsWith(":"))
|
|
294
|
+
);
|
|
295
|
+
case "branch":
|
|
296
|
+
// `-D` (force delete, case-sensitive — `-d` only deletes merged branches)
|
|
297
|
+
// or an explicit `--delete --force`.
|
|
298
|
+
return rest.includes("-D") || (has("--delete") && has("-f", "--force"));
|
|
299
|
+
case "stash":
|
|
300
|
+
return ["drop", "clear"].includes((lower[0] || ""));
|
|
301
|
+
case "reflog":
|
|
302
|
+
return ["expire", "delete"].includes((lower[0] || ""));
|
|
303
|
+
case "update-ref":
|
|
304
|
+
return has("-d", "--delete");
|
|
305
|
+
case "filter-branch":
|
|
306
|
+
case "filter-repo":
|
|
307
|
+
return true;
|
|
308
|
+
case "gc":
|
|
309
|
+
return lower.some((t) => t.startsWith("--prune"));
|
|
310
|
+
case "rebase":
|
|
311
|
+
// History rewrite; control sub-actions (--abort/--continue/etc.) are safe.
|
|
312
|
+
return !["--abort", "--continue", "--skip", "--quit", "--edit-todo", "--show-current-patch"].includes(
|
|
313
|
+
lower[0] || "",
|
|
314
|
+
);
|
|
315
|
+
default:
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
237
320
|
function normalizeRiskLevel(value, fallback = RISK_LEVELS.MEDIUM) {
|
|
238
321
|
if (value === RISK_LEVELS.LOW) return RISK_LEVELS.LOW;
|
|
239
322
|
if (value === RISK_LEVELS.MEDIUM) return RISK_LEVELS.MEDIUM;
|
|
@@ -461,6 +544,7 @@ module.exports = {
|
|
|
461
544
|
TOOL_POLICY_METADATA,
|
|
462
545
|
evaluateToolPolicy,
|
|
463
546
|
getToolPolicyMetadata,
|
|
547
|
+
isDangerousGitCommand,
|
|
464
548
|
isReadOnlyGitCommand,
|
|
465
549
|
normalizeGitCommand,
|
|
466
550
|
resolveToolPolicy,
|
|
@@ -8,21 +8,55 @@ const SHELL_POLICY_DECISIONS = Object.freeze({
|
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
const BLOCKED_SHELL_RULES = Object.freeze([
|
|
11
|
-
{
|
|
12
|
-
id: "git-tool-reroute",
|
|
13
|
-
decision: SHELL_POLICY_DECISIONS.REROUTE,
|
|
14
|
-
test: ({ firstToken }) => firstToken === "git",
|
|
15
|
-
reason:
|
|
16
|
-
"Use the dedicated git tool instead of run_shell for repository operations.",
|
|
17
|
-
},
|
|
18
11
|
{
|
|
19
12
|
id: "dangerous-delete",
|
|
20
13
|
decision: SHELL_POLICY_DECISIONS.DENY,
|
|
14
|
+
// Unix (rm) AND Windows/PowerShell deletes. This repo is Windows-primary
|
|
15
|
+
// (PowerShell is the default shell), where the `rm -rf` analog is
|
|
16
|
+
// `Remove-Item -Recurse -Force` / its alias `ri` — neither shares a first
|
|
17
|
+
// token with the Unix forms, so they must be listed explicitly. (`rm`/`del`
|
|
18
|
+
// already cover PowerShell's `rm`/`del` aliases; `rd` covers `rmdir`.)
|
|
21
19
|
test: ({ firstToken }) =>
|
|
22
|
-
[
|
|
20
|
+
[
|
|
21
|
+
"rm",
|
|
22
|
+
"del",
|
|
23
|
+
"erase",
|
|
24
|
+
"rmdir",
|
|
25
|
+
"rd",
|
|
26
|
+
"remove-item",
|
|
27
|
+
"ri",
|
|
28
|
+
].includes(firstToken),
|
|
23
29
|
reason:
|
|
24
30
|
"Destructive delete commands are blocked by the coding-agent shell policy.",
|
|
25
31
|
},
|
|
32
|
+
{
|
|
33
|
+
// Disk / filesystem destroyers — near-zero legitimate use for a coding
|
|
34
|
+
// agent, catastrophic when wrong. Matched precisely so the common, benign
|
|
35
|
+
// PowerShell output formatters (Format-Table / Format-List / Format-Wide /
|
|
36
|
+
// Format-Custom / ft / fl) are NEVER caught — only `format` (cmd.exe disk
|
|
37
|
+
// format) and `Format-Volume` are. `dd` is blocked ONLY when writing to a
|
|
38
|
+
// raw block device (`of=/dev/…` / Windows `\\.\…`); file-image creation
|
|
39
|
+
// (`of=disk.img`) stays allowed.
|
|
40
|
+
id: "disk-destruction",
|
|
41
|
+
decision: SHELL_POLICY_DECISIONS.DENY,
|
|
42
|
+
test: ({ firstToken, normalized }) => {
|
|
43
|
+
if (firstToken === "format" || firstToken === "format-volume") return true;
|
|
44
|
+
if (firstToken === "mkfs" || firstToken.startsWith("mkfs.")) return true;
|
|
45
|
+
if (["wipefs", "shred", "diskpart"].includes(firstToken)) return true;
|
|
46
|
+
if (firstToken === "dd") {
|
|
47
|
+
return /\bof=(\/dev\/|\\\\)/i.test(normalized);
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
},
|
|
51
|
+
reason:
|
|
52
|
+
"Disk-destroying commands (format / Format-Volume / mkfs / wipefs / shred / diskpart / dd-to-device) are blocked by the coding-agent shell policy.",
|
|
53
|
+
},
|
|
54
|
+
// The dangerous-git-* DENY rules MUST precede `git-tool-reroute` below:
|
|
55
|
+
// `evaluateSegmentPolicy` returns the FIRST matching rule, and the reroute
|
|
56
|
+
// rule matches every git command. Ordering reroute first (as it was) made
|
|
57
|
+
// these DENYs unreachable dead code — a `git reset --hard && …` segment would
|
|
58
|
+
// only REROUTE. Destructive git now hard-DENYs on the run_shell path; the git
|
|
59
|
+
// tool itself separately confirms (isDangerousGitCommand) on its own path.
|
|
26
60
|
{
|
|
27
61
|
id: "dangerous-git-reset",
|
|
28
62
|
decision: SHELL_POLICY_DECISIONS.DENY,
|
|
@@ -49,6 +83,40 @@ const BLOCKED_SHELL_RULES = Object.freeze([
|
|
|
49
83
|
reason:
|
|
50
84
|
"git clean is blocked by the coding-agent shell policy.",
|
|
51
85
|
},
|
|
86
|
+
{
|
|
87
|
+
id: "git-tool-reroute",
|
|
88
|
+
decision: SHELL_POLICY_DECISIONS.REROUTE,
|
|
89
|
+
test: ({ firstToken }) => firstToken === "git",
|
|
90
|
+
reason:
|
|
91
|
+
"Use the dedicated git tool instead of run_shell for repository operations.",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
// Infrastructure-as-Code teardown: `terraform destroy`, `pulumi destroy`,
|
|
95
|
+
// `cdk destroy`, `terragrunt destroy` (and the `terraform apply -destroy`
|
|
96
|
+
// flag variant). These tear down real cloud/infra resources and must not run
|
|
97
|
+
// unprompted. Overridable (via overrideRuleIds) when the user explicitly
|
|
98
|
+
// asks — mirrors Claude Code auto-mode safety for IaC destroy.
|
|
99
|
+
id: "iac-destroy",
|
|
100
|
+
decision: SHELL_POLICY_DECISIONS.DENY,
|
|
101
|
+
test: ({ firstToken, tokens }) =>
|
|
102
|
+
[
|
|
103
|
+
"terraform",
|
|
104
|
+
"terraform.exe",
|
|
105
|
+
"terragrunt",
|
|
106
|
+
"terragrunt.exe",
|
|
107
|
+
"pulumi",
|
|
108
|
+
"pulumi.exe",
|
|
109
|
+
"cdk",
|
|
110
|
+
"cdk.exe",
|
|
111
|
+
"cdklocal",
|
|
112
|
+
].includes(firstToken) &&
|
|
113
|
+
tokens.some((token) => {
|
|
114
|
+
const t = token.toLowerCase();
|
|
115
|
+
return t === "destroy" || t === "-destroy" || t === "--destroy";
|
|
116
|
+
}),
|
|
117
|
+
reason:
|
|
118
|
+
"Infrastructure-as-Code destroy commands (terraform/pulumi/cdk/terragrunt) are blocked by the coding-agent shell policy unless explicitly requested.",
|
|
119
|
+
},
|
|
52
120
|
{
|
|
53
121
|
id: "network-download",
|
|
54
122
|
decision: SHELL_POLICY_DECISIONS.DENY,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as h,P as u,J as S,U as n,R as a,S as y,K as C,Q as x,V as w,_ as B,b as s}from"./vendor-BvqAck49.js";import{_ as k}from"./index-DueHm10i.js";import"./icons-DP3uiYxy.js";const M={__name:"ChatBubbleRenderer",props:{event:{type:Object,required:!0}},setup(c,{expose:d}){d();const t=c,r=s(()=>{const e=t.event.content||{};return e.text||e.body||e.message||e.title||JSON.stringify(e).slice(0,200)}),i=s(()=>{const e=t.event.content||{};return e.from||e.sender||e.senderName||t.event.actor||"(unknown)"}),l=s(()=>{const e=(t.event.actor||"").toLowerCase();return e.includes("self")||e==="me"||e.endsWith("_self")}),o=s(()=>{const e=t.event.source.adapter||"";return e.startsWith("messaging-qq")?"magenta":e==="wechat"?"green":"blue"}),m=s(()=>{if(!t.event.occurredAt)return"";try{const e=new Date(t.event.occurredAt),p=e.getFullYear(),f=String(e.getMonth()+1).padStart(2,"0"),g=String(e.getDate()).padStart(2,"0"),b=String(e.getHours()).padStart(2,"0"),v=String(e.getMinutes()).padStart(2,"0");return`${p}-${f}-${g} ${b}:${v}`}catch{return""}}),_={props:t,messageText:r,actorLabel:i,isMine:l,adapterColor:o,formattedTime:m,computed:s};return Object.defineProperty(_,"__isScriptSetup",{enumerable:!1,value:!0}),_}},N={class:"bubble"},T={class:"meta"},R={class:"actor"},V={class:"time"},q={class:"body"};function D(c,d,t,r,i,l){const o=h("a-tag");return u(),S("div",{class:B(["chat-row",{mine:r.isMine}])},[n("div",N,[n("div",T,[n("span",R,a(r.actorLabel),1),n("span",V,a(r.formattedTime),1)]),n("div",q,a(r.messageText),1),t.event.source.adapter?(u(),y(o,{key:0,class:"src",color:r.adapterColor},{default:C(()=>[x(a(t.event.source.adapter),1)]),_:1},8,["color"])):w("v-if",!0)])],2)}const A=k(M,[["render",D],["__scopeId","data-v-49238629"],["__file","/tmp/cc-web-panel-Mosrcv/repo/packages/web-panel/src/components/pdh/renderers/ChatBubbleRenderer.vue"]]);export{A as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as c,J as g,c as l,K as r,N as u,P as h,U as o,R as t,Q as b}from"./vendor-BvqAck49.js";import{_ as v,b as m}from"./index-DueHm10i.js";import{a7 as M,M as y}from"./icons-DP3uiYxy.js";const B={__name:"MobileProjects",setup(p,{expose:i}){i();const n=u(),{t:e}=m();function a(){n.push("/projects")}function _(){n.push("/mobile-bridge")}const s={router:n,t:e,goToProjects:a,goToMobileBridge:_,get useRouter(){return u},get useI18n(){return m},get MobileOutlined(){return y},get FolderOutlined(){return M}};return Object.defineProperty(s,"__isScriptSetup",{enumerable:!1,value:!0}),s}},x={class:"mobile-projects-placeholder"},O={class:"title"},k={class:"subtitle"},w={class:"explainer"};function T(p,i,n,e,a,_){const s=c("a-alert"),d=c("a-button"),f=c("a-space"),P=c("a-empty"),j=c("a-card");return h(),g("div",x,[l(j,null,{default:r(()=>[l(P,{description:!1},{image:r(()=>[l(e.MobileOutlined,{style:{fontSize:"64px",color:"#bfbfbf"}})]),default:r(()=>[o("h2",O,t(e.t("mobileProjects.title")),1),o("p",k,t(e.t("mobileProjects.subtitle")),1),l(s,{message:e.t("mobileProjects.v02Banner"),type:"info","show-icon":"",class:"banner"},null,8,["message"]),o("div",w,[o("h3",null,t(e.t("mobileProjects.currentDirection")),1),o("p",null,t(e.t("mobileProjects.currentDirectionBody")),1),o("ul",null,[o("li",null,t(e.t("mobileProjects.stepPhone")),1),o("li",null,t(e.t("mobileProjects.stepTap")),1),o("li",null,t(e.t("mobileProjects.stepPull")),1)]),o("h3",null,t(e.t("mobileProjects.reverseDirection")),1),o("p",null,t(e.t("mobileProjects.reverseDirectionBody")),1)]),l(f,{class:"actions"},{default:r(()=>[l(d,{type:"primary",onClick:e.goToProjects},{default:r(()=>[l(e.FolderOutlined),b(" "+t(e.t("mobileProjects.viewLocalProjects")),1)]),_:1}),l(d,{onClick:e.goToMobileBridge},{default:r(()=>[l(e.MobileOutlined),b(" "+t(e.t("mobileProjects.checkBridge")),1)]),_:1})]),_:1})]),_:1})]),_:1})])}const N=v(B,[["render",T],["__scopeId","data-v-9262cc45"],["__file","/tmp/cc-web-panel-Mosrcv/repo/packages/web-panel/src/views/MobileProjects.vue"]]);export{N as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as w,P as l,J as u,U as s,R as n,c as i,K as v,Q as _,V as g,b as o}from"./vendor-BvqAck49.js";import{_ as S}from"./index-DueHm10i.js";import"./icons-DP3uiYxy.js";const k={__name:"OrderTableRenderer",props:{event:{type:Object,required:!0}},setup(p,{expose:r}){r();const a=p,e=o(()=>a.event.content||{}),d=o(()=>a.event.extra||{}),m=o(()=>d.value.merchant||e.value.merchant||e.value.counterparty||"—"),c=o(()=>e.value.title||e.value.name||e.value.itemName||e.value.text||"—"),y=o(()=>{const t=e.value.amount??e.value.price??e.value.total;return t==null?"—":`${e.value.currency||"¥"} ${typeof t=="number"?t.toFixed(2):t}`}),T=o(()=>d.value.orderNo||e.value.orderNo||e.value.orderId),f=o(()=>e.value.status||e.value.state),b=o(()=>{const t=(f.value||"").toLowerCase();return t.includes("成功")||t.includes("succe")||t.includes("paid")?"green":t.includes("退")||t.includes("refund")?"orange":t.includes("失败")||t.includes("fail")?"red":"default"}),h=o(()=>{if(!a.event.occurredAt)return"";const t=new Date(a.event.occurredAt);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`}),x={props:a,c:e,e:d,merchantText:m,itemText:c,amountText:y,orderNo:T,statusText:f,statusColor:b,formattedTime:h,computed:o};return Object.defineProperty(x,"__isScriptSetup",{enumerable:!1,value:!0}),x}},N={class:"order-card"},C={class:"head"},O={class:"time"},V={class:"row"},R={class:"val"},B={class:"row"},D={class:"val"},I={class:"row amount-row"},M={class:"val amount"},j={key:0,class:"row"},A={class:"val mono"},F={key:1,class:"row"};function P(p,r,a,e,d,m){const c=w("a-tag");return l(),u("div",N,[s("div",C,[s("span",O,n(e.formattedTime),1),i(c,{color:"gold"},{default:v(()=>[_(n(a.event.source.adapter),1)]),_:1}),i(c,null,{default:v(()=>[_(n(a.event.subtype),1)]),_:1})]),s("div",V,[r[0]||(r[0]=s("span",{class:"key"},"商户",-1)),s("span",R,n(e.merchantText),1)]),s("div",B,[r[1]||(r[1]=s("span",{class:"key"},"商品/项目",-1)),s("span",D,n(e.itemText),1)]),s("div",I,[r[2]||(r[2]=s("span",{class:"key"},"金额",-1)),s("span",M,n(e.amountText),1)]),e.orderNo?(l(),u("div",j,[r[3]||(r[3]=s("span",{class:"key"},"单号",-1)),s("span",A,n(e.orderNo),1)])):g("v-if",!0),e.statusText?(l(),u("div",F,[r[4]||(r[4]=s("span",{class:"key"},"状态",-1)),i(c,{color:e.statusColor},{default:v(()=>[_(n(e.statusText),1)]),_:1},8,["color"])])):g("v-if",!0)])}const K=S(k,[["render",P],["__scopeId","data-v-5ed5524d"],["__file","/tmp/cc-web-panel-Mosrcv/repo/packages/web-panel/src/components/pdh/renderers/OrderTableRenderer.vue"]]);export{K as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{O as r}from"./index-DueHm10i.js";const o=((n,a,e)=>{r(n,`[ant-design-vue: ${a}] ${e}`)});export{o as d};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{A as o}from"./Row-DQJPE1Hl.js";import{U as t}from"./index-DueHm10i.js";import"./vendor-BvqAck49.js";import"./responsiveObserve-Do0AXkGf.js";import"./useFlexGapSupport-1L2Xx-45.js";import"./styleChecker-CQ6edivv.js";import"./index-dpy2EOcg.js";import"./icons-DP3uiYxy.js";const l=t(o);export{l as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{C as o}from"./Col-CNaC37C3.js";import{U as t}from"./index-DueHm10i.js";import"./vendor-BvqAck49.js";import"./index-dpy2EOcg.js";import"./icons-DP3uiYxy.js";const s=t(o);export{s as default};
|