chainlesschain 0.162.45 → 0.162.46
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 +2 -2
- package/src/assets/web-panel/assets/{AIOps-D-W6s4eA.js → AIOps-QzHleqCS.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-nPOyfwP_.js → ActionButton-CZ3F-KVY.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DoX0H6wa.js → Analytics-CHml-drq.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BYNnmmUE.js → AppLayout-a-fczvRH.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-AjglOteL.js → Audit-CtMWsXwm.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DT38xAKk.js → Backup-ErWTcS0A.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DT1pD4sT.js → BaseInput-B4w2b2dA.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Z77BT13i.js → Chat-CFPKVfmV.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-C1MKMzjB.js → ChatBubbleRenderer-WGpUTY92.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CVOMmTDm.js → Checkbox-C4vasfJc.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-Bqy11vCf.js → Codegen-DHmDAbSg.js} +1 -1
- package/src/assets/web-panel/assets/{Col-C3A1d5Wt.js → Col-LhgveyhM.js} +1 -1
- package/src/assets/web-panel/assets/{Community-D2q6UNaX.js → Community-CB8gtudt.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-C2701nAm.js → Compact-CTndsUSy.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-SJnaA5oN.js → Compliance-CCpGUHPm.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BvTYuhJy.js → Cowork-CQmJiOM_.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-sJ-9lLUV.js → Cron-TrTmpnSB.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CStGE_K9.js → Crosschain-DvaR1Xq4.js} +1 -1
- package/src/assets/web-panel/assets/{DID-JY0Kx-7a.js → DID-CdDdiRs3.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BiZEZPRN.js → Dashboard-S6gCBQDc.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DK2BszlZ.js → Dropdown-CWO-Kwzl.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DtjOYS0Q.js → EmailListRenderer-Crnrr1-p.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-D2EI2B5E.js → FamilyGuardDashboard-OpG9ZtfR.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CebAtHrY.js → Federation-B0sybW7G.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Bd8WNQLm.js → FormItemContext-DjHMvxwJ.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-tH3zcpN9.js → GenericCardRenderer-DtU5cWwE.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DARoXdm7.js → Git-BhOjZX2a.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-gcKIyn7g.js → Governance-D3_hjjA-.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Rh4pllPC.js → Inference-DwxLXmgA.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cvh73WwA.js → KnowledgeGraph-C8-FVs02.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BBZSagwy.js → Logs-CrHjnzfr.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-e5C7xHES.js → Marketplace-KW1Am18o.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BzfXvajQ.js → McpTools-ezYH1J8m.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-C3ZgSBrL.js → Memory-Dcv8Gume.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BYVobYJJ.js → MobileBridge-v18J0LN4.js} +2 -2
- package/src/assets/web-panel/assets/MobileProjects-kk6hAwkK.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-BxNytLEb.js → Mtc-Cm35-vh_.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BUadScwJ.js → MtcAudit-DyeKAlI_.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BWZmAn6M.js → Multisig-Dy-imleB.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-B4eau0Yo.js → NLProgramming-DMVmWNAH.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-eHe1Jd1n.js → Notes-Dq6oHKbl.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DFMSI_xu.js → NotificationSettings-CCaSupDi.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-DOwptdNb.js +1 -0
- package/src/assets/web-panel/assets/{Organization-M_NYVPks.js → Organization-8TF96XtV.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Bi-TzxkX.js → Overflow-BnYnh84b.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-vtorIyuM.js → P2P-CZk3Qkec.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-D9I3WUUZ.js → PdhVaultBrowser-D1HrzBx2.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-YFbIKLZw.js → Permissions-CwJ6hcaI.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-JM8yN0w0.js → PersonalDataHub-BGnPTj1G.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-Cyvwlqpr.js → Pipeline-BzXhXLBz.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CIKHwg7m.js → Privacy-BaVQ3GwL.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Bj6-CmbC.js → ProjectInit-Bik3h4R7.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CBnTXhIt.js → ProjectSettings-CIuC6Hpr.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-B3CwPTNd.js → Projects-DLjnWEe6.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-dtUZaXuY.js → Providers-mhxZrHiK.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BWzt81Dm.js → QuickAsk-Dk9mayL0.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CToDb_n9.js → Recommend-BHC2sBZY.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-B6MCFsXH.js → Reputation-DV26B11J.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CipchQUs.js → Row-bJlVEnFC.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CR3mpmuE.js → RssFeed-D_XucmWY.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DRvRD7_k.js → Search-uKXcm_Kf.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bv1bEvfO.js → Security-DEzqsQLn.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BLAljjtu.js → Services-I8qKgcom.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BRFNIS9s.js → Skeleton-BmrglBMu.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-B6dEwkkO.js → Skills-CHN4ZDyP.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-A97O6hQJ.js → Sla-Ca6FsUS2.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DNBeqO5l.js → SpeechSettings-CP8IikWb.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-C61PTeaN.js → SyncSettings-Bqp-kPAA.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-C8q-8JXF.js → Tasks-C7FvJjze.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-R-4ZBAPY.js → Templates-Bq51Irli.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-dRpKvdmN.js → Tenant-OTducEVg.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-ywwPryM5.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-BbXUD3AZ.js → TimelineRenderer-kCmFL2wc.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CuYIAO1f.js → Tokens-BUfD650M.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-bt_5ffBh.js → Trigger-CqpM6xE7.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-rk4LCtlg.js → Trust--4gS7a0j.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Bgf8zhuS.js → UkeySign-B7wfki-b.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B_D_kcq5.js → VideoEditing-FwDEX2S-.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BuhroOcU.js → Wallet-DS_c2rMk.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DNVp_EWD.js → WebAuthn-CdMhP-GP.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-C94Nr2Yq.js → WorkflowEditor-DVvpoOmg.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CqvcO82L.js → chat-BQyTT2BS.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CiHv7zLX.js → colors-DvylfUTx.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BbL_7DRy.js → compact-item-Budvh2nW.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BcnUkPGe.js → createContext-DkNa84zG.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CG_75MBJ.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-bKZM79uJ.js → hasIn-Dc1juUIf.js} +1 -1
- package/src/assets/web-panel/assets/{index-Drdlb5iB.js → index-B0u5DW4Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bfcq_Svy.js → index-BC08UMEf.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cc48TfA8.js → index-BCD2L2SI.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0Dew6UJ.js → index-BCv_FUY5.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAayTu3W.js → index-BEviVulf.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dpb_Zh3w.js → index-BQJA__yH.js} +1 -1
- package/src/assets/web-panel/assets/{index-AJLQ9KfF.js → index-BTnZ44a_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-jqR16_.js → index-BTzrvuB0.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1b8TGYq.js → index-BWcGURYh.js} +1 -1
- package/src/assets/web-panel/assets/{index-SeP48n4C.js → index-BYuozZuh.js} +1 -1
- package/src/assets/web-panel/assets/index-Ba6DLEa1.js +1 -0
- package/src/assets/web-panel/assets/{index-Kl_dtIkL.js → index-BgYKjtNP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWPLkrPr.js → index-BgxdM7b_.js} +1 -1
- package/src/assets/web-panel/assets/{index-2nrwvHPn.js → index-Bk_Uhu49.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bd0u4iTg.js → index-By8Bs_eN.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPJ4NWCv.js → index-C1X5QpSy.js} +1 -1
- package/src/assets/web-panel/assets/{index-ByEejvcB.js → index-C8he-Xo0.js} +1 -1
- package/src/assets/web-panel/assets/{index-Csgtimo8.js → index-CFMS9PKh.js} +1 -1
- package/src/assets/web-panel/assets/{index-BySbK7vA.js → index-CGHLsKyb.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5X8cqw-.js → index-CKoWcis9.js} +1 -1
- package/src/assets/web-panel/assets/index-CLycnSs-.js +1 -0
- package/src/assets/web-panel/assets/{index-B1StZUmz.js → index-CbCIBifY.js} +1 -1
- package/src/assets/web-panel/assets/{index-kHUuRAFp.js → index-Cz41iB_M.js} +1 -1
- package/src/assets/web-panel/assets/{index-ABAWaz7Y.js → index-D2dcbmzD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlIYy94Y.js → index-D74oTZL7.js} +1 -1
- package/src/assets/web-panel/assets/{index-B76qMGe0.js → index-D7FYxxFT.js} +3 -3
- package/src/assets/web-panel/assets/{index-Zxnsweba.js → index-DMCqz0wy.js} +1 -1
- package/src/assets/web-panel/assets/{index-UrCGcZXp.js → index-DO9Lvv1w.js} +1 -1
- package/src/assets/web-panel/assets/{index-DYH3St42.js → index-DRNJnXik.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQonLmOf.js → index-DTcZkWJ7.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bh9TSxy7.js → index-D_0nyIY9.js} +1 -1
- package/src/assets/web-panel/assets/{index-sdSZZzwh.js → index-DecFF9je.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNBdA_cQ.js → index-DwJ0d_2Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-BSzQlPjx.js → index-DxCPTrC-.js} +1 -1
- package/src/assets/web-panel/assets/{index-JteeOvFE.js → index-TsPv8m1Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-vzVBm4mg.js → index-YVdWdhK_.js} +1 -1
- package/src/assets/web-panel/assets/{index-Kt6y6Hh5.js → index-eD_MN29w.js} +1 -1
- package/src/assets/web-panel/assets/{index-J8Xba8Zt.js → index-jTGSFnNF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPHskdVZ.js → index-yx5Y6xb7.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Cg2ZQCW2.js → initDefaultProps-gLjyJG0Q.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CVup4XZf.js → motion-BtXv1r2p.js} +1 -1
- package/src/assets/web-panel/assets/{move-0we4XeEJ.js → move-BgFqW2gA.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DNvkWjsk.js → omit-CeIwl-eJ.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DNGWkffo.js → pickAttrs-VlY4igbs.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DdMP7Lk2.js → placementArrow-B9Wjn3oR.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CgCiGZ6t.js → responsiveObserve-BBgHTvYX.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CQxOWXtr.js → slide-DWw81KAi.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BVYop9K6.js → statusUtils-Bsho38w-.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BTkUPq_y.js → styleChecker-BNvVEdm1.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CKncli3p.js → useFlexGapSupport-DG9bkwrT.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-B7k6cPxd.js → useFs-DU7qrIrQ.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CiVRczRO.js → usePersonalDataHub-DX-Bf7VJ.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CPueAEhr.js → vnode-B1UcnNoK.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BhzuJF_P.js → zoom-BFgJuvLv.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +44 -30
- package/src/commands/hub.js +87 -0
- package/src/harness/mcp-client.js +86 -1
- package/src/lib/auto-checkpoint-default.js +49 -0
- package/src/lib/personal-data-hub-wiring.js +44 -2
- package/src/lib/repl-completer.js +6 -3
- package/src/lib/skill-loader.js +43 -7
- package/src/repl/agent-repl.js +27 -1
- package/src/runtime/mcp-config.js +19 -1
- package/src/assets/web-panel/assets/MobileProjects-Bl8xCXs4.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-Ctejeurt.js +0 -1
- package/src/assets/web-panel/assets/Terminal-egMhD-wn.js +0 -3
- package/src/assets/web-panel/assets/devWarning-BSbU0obw.js +0 -1
- package/src/assets/web-panel/assets/index-DHgbmZ-Y.js +0 -1
- package/src/assets/web-panel/assets/index-pr3q7Jlo.js +0 -1
package/src/commands/agent.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
2
|
* Agentic AI assistant - Claude Code style
|
|
3
3
|
* chainlesschain agent [--model] [--provider]
|
|
4
4
|
*
|
|
@@ -44,7 +44,7 @@ export function resolveAddDirs(rawDirs) {
|
|
|
44
44
|
* thinking `budget_tokens`), or undefined when unset/invalid. Pure; exported for
|
|
45
45
|
* tests. The companion `thinking` toggle comes from --think/--ultrathink; a
|
|
46
46
|
* budget without that toggle is a no-op (chatWithTools only reads it when
|
|
47
|
-
* thinking is on, and only for legacy models
|
|
47
|
+
* thinking is on, and only for legacy models 鈥?adaptive models use effort).
|
|
48
48
|
*
|
|
49
49
|
* @param {string|number} [raw]
|
|
50
50
|
* @returns {number|undefined}
|
|
@@ -85,7 +85,7 @@ export function registerAgentCommand(program) {
|
|
|
85
85
|
)
|
|
86
86
|
.argument(
|
|
87
87
|
"[task...]",
|
|
88
|
-
"Headless task
|
|
88
|
+
"Headless task 鈥?when given (or with -p / piped stdin), runs non-interactively",
|
|
89
89
|
)
|
|
90
90
|
.option("--model <model>", "Model name")
|
|
91
91
|
.option(
|
|
@@ -118,11 +118,11 @@ export function registerAgentCommand(program) {
|
|
|
118
118
|
.option("--session <id>", "Resume a previous agent session")
|
|
119
119
|
.option(
|
|
120
120
|
"-c, --continue",
|
|
121
|
-
"Resume the most recent session (no id needed
|
|
121
|
+
"Resume the most recent session (no id needed 鈥?claude --continue parity)",
|
|
122
122
|
)
|
|
123
123
|
.option(
|
|
124
124
|
"--resume [id]",
|
|
125
|
-
"Resume a session by id (no id
|
|
125
|
+
"Resume a session by id (no id 鈫?most recent 鈥?claude --resume parity)",
|
|
126
126
|
)
|
|
127
127
|
.option(
|
|
128
128
|
"--fork-session",
|
|
@@ -135,7 +135,11 @@ export function registerAgentCommand(program) {
|
|
|
135
135
|
)
|
|
136
136
|
.option(
|
|
137
137
|
"--checkpoint",
|
|
138
|
-
"Auto-snapshot the work tree before each mutating tool (git repo; cc checkpoint restore to roll back)",
|
|
138
|
+
"Auto-snapshot the work tree before each mutating tool (DEFAULT inside a git repo; cc checkpoint restore to roll back)",
|
|
139
|
+
)
|
|
140
|
+
.option(
|
|
141
|
+
"--no-checkpoint",
|
|
142
|
+
"Disable auto-checkpointing (it is on by default inside git repos)",
|
|
139
143
|
)
|
|
140
144
|
.option("--agent-id <id>", "Agent id for scoped memory recall")
|
|
141
145
|
.option("--recall-limit <n>", "Top-K memories to inject into system prompt")
|
|
@@ -150,7 +154,7 @@ export function registerAgentCommand(program) {
|
|
|
150
154
|
"--bundle <path>",
|
|
151
155
|
"Agent bundle directory (chainless-agent.toml + AGENTS.md + skills/ + mcp.json + USER.md)",
|
|
152
156
|
)
|
|
153
|
-
//
|
|
157
|
+
// 鈹€鈹€ Headless / print mode (Claude-Code `claude -p` parity) 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
154
158
|
.option(
|
|
155
159
|
"-p, --print [prompt]",
|
|
156
160
|
"Headless: run one non-interactive turn and exit",
|
|
@@ -239,7 +243,18 @@ export function registerAgentCommand(program) {
|
|
|
239
243
|
"--settings <file>",
|
|
240
244
|
"Merge an extra .claude/settings.json-shaped file for this run: permission rules (allow/ask/deny) + native config overrides (model, env)",
|
|
241
245
|
)
|
|
242
|
-
.action(async (task, options) => {
|
|
246
|
+
.action(async (task, options, command) => {
|
|
247
|
+
// Claude-Code parity: auto-checkpoint defaults ON inside a git repo
|
|
248
|
+
// (shadow-commit engine, zero working-tree touch); explicit
|
|
249
|
+
// --checkpoint / --no-checkpoint always wins.
|
|
250
|
+
const { resolveAutoCheckpoint } = await import(
|
|
251
|
+
"../lib/auto-checkpoint-default.js"
|
|
252
|
+
);
|
|
253
|
+
const autoCheckpoint = resolveAutoCheckpoint({
|
|
254
|
+
flagValue: options.checkpoint,
|
|
255
|
+
flagSource: command?.getOptionValueSource?.("checkpoint"),
|
|
256
|
+
cwd: process.cwd(),
|
|
257
|
+
});
|
|
243
258
|
// `--continue` / `--resume` resolve a session id so the user need not
|
|
244
259
|
// copy it. Explicit `--session <id>` always wins. `--resume <id>` targets
|
|
245
260
|
// a specific session; `--continue` or a bare `--resume` pick the most
|
|
@@ -260,7 +275,7 @@ export function registerAgentCommand(program) {
|
|
|
260
275
|
!process.stdin.isTTY;
|
|
261
276
|
if (headlessIntent) {
|
|
262
277
|
// Headless replays the JSONL store only, so resolve "most recent"
|
|
263
|
-
// from there
|
|
278
|
+
// from there 鈥?a DB-only id would resume into an empty transcript.
|
|
264
279
|
const { getLastSessionId } =
|
|
265
280
|
await import("../harness/jsonl-session-store.js");
|
|
266
281
|
options.session = getLastSessionId();
|
|
@@ -283,7 +298,7 @@ export function registerAgentCommand(program) {
|
|
|
283
298
|
|
|
284
299
|
// --fork-session: branch the resolved session into a NEW id so the
|
|
285
300
|
// ORIGINAL transcript is preserved (Claude-Code parity). One chokepoint
|
|
286
|
-
// before headless / stream / interactive dispatch
|
|
301
|
+
// before headless / stream / interactive dispatch 鈥?all read
|
|
287
302
|
// `options.session`. The copy carries the full history, so the branch
|
|
288
303
|
// replays end-to-end on a later --resume. Only meaningful when resuming;
|
|
289
304
|
// a no-op (silent) for a fresh run.
|
|
@@ -297,12 +312,12 @@ export function registerAgentCommand(program) {
|
|
|
297
312
|
);
|
|
298
313
|
if (fork.missing) {
|
|
299
314
|
process.stderr.write(
|
|
300
|
-
`--fork-session: no headless transcript for "${options.session}"
|
|
315
|
+
`--fork-session: no headless transcript for "${options.session}" 鈥?` +
|
|
301
316
|
"nothing to fork; continuing on it.\n",
|
|
302
317
|
);
|
|
303
318
|
} else if (fork.forkedFrom) {
|
|
304
319
|
process.stderr.write(
|
|
305
|
-
`Forked session ${fork.forkedFrom}
|
|
320
|
+
`Forked session ${fork.forkedFrom} 鈫?${fork.sessionId} (original preserved)\n`,
|
|
306
321
|
);
|
|
307
322
|
options.session = fork.sessionId;
|
|
308
323
|
}
|
|
@@ -332,7 +347,7 @@ export function registerAgentCommand(program) {
|
|
|
332
347
|
}
|
|
333
348
|
|
|
334
349
|
// The explicit `--model` the user typed, captured BEFORE the --settings
|
|
335
|
-
// block below may default options.model
|
|
350
|
+
// block below may default options.model 鈥?so vision input can tell an
|
|
336
351
|
// explicit model from a settings default.
|
|
337
352
|
const explicitCliModel = options.model;
|
|
338
353
|
|
|
@@ -354,10 +369,10 @@ export function registerAgentCommand(program) {
|
|
|
354
369
|
}
|
|
355
370
|
if (!options.model && sc.model) options.model = sc.model;
|
|
356
371
|
} catch {
|
|
357
|
-
// settings overrides are best-effort
|
|
372
|
+
// settings overrides are best-effort 鈥?never block the run
|
|
358
373
|
}
|
|
359
374
|
|
|
360
|
-
// Extra workspace roots (--add-dir)
|
|
375
|
+
// Extra workspace roots (--add-dir) 鈥?shared by headless + interactive.
|
|
361
376
|
const additionalDirectories = resolveAddDirs(options.addDir);
|
|
362
377
|
|
|
363
378
|
// --image <path> (repeatable): read into {mediaType, base64} for the
|
|
@@ -376,7 +391,7 @@ export function registerAgentCommand(program) {
|
|
|
376
391
|
// When an image is attached, default the run to the configured vision LLM
|
|
377
392
|
// (config.llm provider/baseUrl/apiKey + llm.visionModel | --vision-model |
|
|
378
393
|
// doubao default) so `cc agent --image foo.png` works without extra flags.
|
|
379
|
-
// Explicit --provider/--model/etc. always win; no image
|
|
394
|
+
// Explicit --provider/--model/etc. always win; no image 鈫?unchanged.
|
|
380
395
|
const visionLlm = resolveVisionLlm({
|
|
381
396
|
hasImage: images.length > 0,
|
|
382
397
|
flags: {
|
|
@@ -391,7 +406,7 @@ export function registerAgentCommand(program) {
|
|
|
391
406
|
|
|
392
407
|
// Config-default LLM (parity with cc ask/chat): a bare `cc agent` honors
|
|
393
408
|
// config.json `llm` (provider/model/baseUrl/apiKey) instead of silently
|
|
394
|
-
// assuming local ollama
|
|
409
|
+
// assuming local ollama 鈥?this is what makes the editor chat panel work
|
|
395
410
|
// against a cloud-configured setup. Explicit --provider wins outright;
|
|
396
411
|
// the vision resolution above still overrides for --image runs. Applied
|
|
397
412
|
// AFTER resolveVisionLlm so config defaults don't masquerade as explicit
|
|
@@ -405,16 +420,15 @@ export function registerAgentCommand(program) {
|
|
|
405
420
|
});
|
|
406
421
|
}
|
|
407
422
|
|
|
408
|
-
// --think / --ultrathink
|
|
409
|
-
// extended thinking; ignored by other providers). --think with no value
|
|
410
|
-
// true; --think <level> → that level; --ultrathink wins as "ultra".
|
|
423
|
+
// --think / --ultrathink 鈫?options.thinking for the agent loop (Anthropic
|
|
424
|
+
// extended thinking; ignored by other providers). --think with no value 鈫? // true; --think <level> 鈫?that level; --ultrathink wins as "ultra".
|
|
411
425
|
const thinking = options.ultrathink
|
|
412
426
|
? "ultra"
|
|
413
427
|
: options.think === true
|
|
414
428
|
? true
|
|
415
429
|
: options.think || undefined;
|
|
416
430
|
// --thinking-budget <n>: legacy-model thinking budget (no-op without
|
|
417
|
-
// --think/--ultrathink and on adaptive models). undefined
|
|
431
|
+
// --think/--ultrathink and on adaptive models). undefined 鈫?engine default.
|
|
418
432
|
const thinkingBudget = resolveThinkingBudget(options.thinkingBudget);
|
|
419
433
|
|
|
420
434
|
// --fallback-model: a chatFn that retries once on the backup model when
|
|
@@ -431,7 +445,7 @@ export function registerAgentCommand(program) {
|
|
|
431
445
|
})
|
|
432
446
|
: undefined;
|
|
433
447
|
|
|
434
|
-
//
|
|
448
|
+
// 鈹€鈹€ Streaming-input mode (--input-format stream-json) 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
|
435
449
|
// A persistent multi-turn conversation driven by NDJSON user events on
|
|
436
450
|
// stdin; output is always NDJSON. Routed before single-prompt handling
|
|
437
451
|
// so stdin is consumed as events, not as one prompt.
|
|
@@ -489,7 +503,7 @@ export function registerAgentCommand(program) {
|
|
|
489
503
|
}
|
|
490
504
|
|
|
491
505
|
// Resolve the headless prompt from: --print value, positional task, or
|
|
492
|
-
// piped stdin (in that precedence). Any of them
|
|
506
|
+
// piped stdin (in that precedence). Any of them 鈫?headless mode.
|
|
493
507
|
const positional =
|
|
494
508
|
Array.isArray(task) && task.length > 0 ? task.join(" ") : "";
|
|
495
509
|
let prompt = "";
|
|
@@ -511,7 +525,7 @@ export function registerAgentCommand(program) {
|
|
|
511
525
|
|
|
512
526
|
if (prompt) {
|
|
513
527
|
// Resume requested onto a session with no headless (JSONL) transcript?
|
|
514
|
-
// Warn instead of silently starting empty
|
|
528
|
+
// Warn instead of silently starting empty 鈥?headless resume rebuilds
|
|
515
529
|
// from the JSONL store only (DB-only sessions are not replayable here).
|
|
516
530
|
const resumeRequested =
|
|
517
531
|
Boolean(options.continue) || options.resume !== undefined;
|
|
@@ -520,7 +534,7 @@ export function registerAgentCommand(program) {
|
|
|
520
534
|
await import("../harness/jsonl-session-store.js");
|
|
521
535
|
if (!sessionExists(options.session)) {
|
|
522
536
|
process.stderr.write(
|
|
523
|
-
`Note: no headless transcript for session "${options.session}"
|
|
537
|
+
`Note: no headless transcript for session "${options.session}" 鈥?` +
|
|
524
538
|
"starting fresh (headless resume reads JSONL sessions only).\n",
|
|
525
539
|
);
|
|
526
540
|
}
|
|
@@ -549,9 +563,9 @@ export function registerAgentCommand(program) {
|
|
|
549
563
|
allowedTools: parseToolList(options.allowedTools),
|
|
550
564
|
disallowedTools: parseToolList(options.disallowedTools),
|
|
551
565
|
additionalDirectories,
|
|
552
|
-
autoCheckpoint
|
|
566
|
+
autoCheckpoint,
|
|
553
567
|
maxTurns,
|
|
554
|
-
// commander maps --no-file-refs
|
|
568
|
+
// commander maps --no-file-refs 鈫?options.fileRefs === false
|
|
555
569
|
expandFileRefs: options.fileRefs !== false,
|
|
556
570
|
// --system-prompt / --append-system-prompt (literal or @file)
|
|
557
571
|
systemPrompt: resolvePromptText(options.systemPrompt, {
|
|
@@ -582,7 +596,7 @@ export function registerAgentCommand(program) {
|
|
|
582
596
|
chatFn: fallbackChatFn,
|
|
583
597
|
};
|
|
584
598
|
|
|
585
|
-
// --json-schema: structured output
|
|
599
|
+
// --json-schema: structured output 鈥?wrap the runner with capture +
|
|
586
600
|
// validate + retry (json-schema-output.js); prints only the
|
|
587
601
|
// validated JSON. Incompatible with stream-json (event stream and a
|
|
588
602
|
// single JSON contract don't mix).
|
|
@@ -621,7 +635,7 @@ export function registerAgentCommand(program) {
|
|
|
621
635
|
}
|
|
622
636
|
|
|
623
637
|
// Reached only for an interactive session, where --image has no turn to
|
|
624
|
-
// attach to
|
|
638
|
+
// attach to 鈥?warn instead of silently dropping the attachment.
|
|
625
639
|
if (images.length) {
|
|
626
640
|
process.stderr.write(
|
|
627
641
|
"--image is only used in headless mode (-p / a task / piped stdin); ignoring for the interactive session.\n",
|
|
@@ -644,7 +658,7 @@ export function registerAgentCommand(program) {
|
|
|
644
658
|
parkOnExit: options.parkOnExit, // false when --no-park-on-exit
|
|
645
659
|
bundlePath: options.bundle || null,
|
|
646
660
|
additionalDirectories,
|
|
647
|
-
autoCheckpoint
|
|
661
|
+
autoCheckpoint,
|
|
648
662
|
// --system-prompt / --append-system-prompt (literal or @file) also
|
|
649
663
|
// apply to interactive sessions, composed in startAgentRepl.
|
|
650
664
|
systemPrompt: resolvePromptText(options.systemPrompt, {
|
package/src/commands/hub.js
CHANGED
|
@@ -1732,6 +1732,65 @@ async function cmdDouyinAdbSync(options) {
|
|
|
1732
1732
|
}
|
|
1733
1733
|
}
|
|
1734
1734
|
|
|
1735
|
+
async function cmdDouyinWatchSync(options) {
|
|
1736
|
+
try {
|
|
1737
|
+
const hub = await (options._getHub || getHub)();
|
|
1738
|
+
const result = await hub.douyinWatchSync({
|
|
1739
|
+
uid: options.uid,
|
|
1740
|
+
stagingDir: options.stagingDir,
|
|
1741
|
+
displayName: options.displayName,
|
|
1742
|
+
limit: parsePositiveInt(options.limit),
|
|
1743
|
+
resolveTitles: !!options.resolveTitles,
|
|
1744
|
+
titleLimit: parsePositiveInt(options.titleLimit),
|
|
1745
|
+
});
|
|
1746
|
+
if (options.json) {
|
|
1747
|
+
printJson(result);
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
if (!result.ok) {
|
|
1751
|
+
logger.log(chalk.red(`✗ douyin-watch-sync failed: ${result.reason}`));
|
|
1752
|
+
logger.log(chalk.gray(` ${result.message || ""}`));
|
|
1753
|
+
if (result.reason === "DOUYIN_NO_ROOT") {
|
|
1754
|
+
logger.log(
|
|
1755
|
+
chalk.gray(
|
|
1756
|
+
" Phone needs Magisk root — Douyin release APK isn't debuggable",
|
|
1757
|
+
),
|
|
1758
|
+
);
|
|
1759
|
+
} else if (result.reason === "DOUYIN_VIDEO_RECORD_MISSING") {
|
|
1760
|
+
logger.log(
|
|
1761
|
+
chalk.gray(
|
|
1762
|
+
" Open Douyin + watch a few videos to populate video_record.db, then retry",
|
|
1763
|
+
),
|
|
1764
|
+
);
|
|
1765
|
+
} else if (result.reason === "BRIDGE_UNAVAILABLE") {
|
|
1766
|
+
logger.log(
|
|
1767
|
+
chalk.gray(
|
|
1768
|
+
" Install Android Platform Tools or set ADB_PATH=/path/to/adb",
|
|
1769
|
+
),
|
|
1770
|
+
);
|
|
1771
|
+
}
|
|
1772
|
+
process.exitCode = 1;
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
const report = result.report || {};
|
|
1776
|
+
const dy = report.douyin || {};
|
|
1777
|
+
const counts = dy.eventCounts || {};
|
|
1778
|
+
logger.log(chalk.green(`✓ douyin-watch-sync succeeded`));
|
|
1779
|
+
logger.log(` uid: ${chalk.cyan(dy.uid || "?")}`);
|
|
1780
|
+
logger.log(` watched: ${counts.history || 0}`);
|
|
1781
|
+
if (options.resolveTitles) {
|
|
1782
|
+
logger.log(` titles: ${dy.titlesResolved || 0} resolved`);
|
|
1783
|
+
}
|
|
1784
|
+
logger.log(` status: ${report.status || "?"}`);
|
|
1785
|
+
logger.log(` rawCount: ${report.rawCount || 0}`);
|
|
1786
|
+
if (dy.cleanupFailed) {
|
|
1787
|
+
logger.log(chalk.gray(` (note: staging cleanup failed — non-fatal)`));
|
|
1788
|
+
}
|
|
1789
|
+
} catch (err) {
|
|
1790
|
+
fail(null, err, options.json);
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1735
1794
|
/**
|
|
1736
1795
|
* Phase 1e — `cc hub bilibili-adb-doctor`
|
|
1737
1796
|
*
|
|
@@ -2309,6 +2368,33 @@ export function registerHubCommand(program) {
|
|
|
2309
2368
|
.option("--json", "Output JSON")
|
|
2310
2369
|
.action(cmdDouyinAdbSync);
|
|
2311
2370
|
|
|
2371
|
+
// Douyin watch-history (video_record.db, plaintext — no X-Bogus / no SQLCipher)
|
|
2372
|
+
hub
|
|
2373
|
+
.command("douyin-watch-sync")
|
|
2374
|
+
.description(
|
|
2375
|
+
"Douyin 观看历史 C 路径: pull video_record.db via ADB from the user's Android Douyin App (com.ss.android.ugc.aweme), read record_<uid> (aid + view_time_timestamp + enter_from), ingest as `history` (BROWSE) events. Plaintext — no X-Bogus signing, no SQLCipher. Needs rooted Android + Douyin App with watch history. Add --resolve-titles to enrich each event with the video's desc/author/duration via the web detail endpoint (also no signing).",
|
|
2376
|
+
)
|
|
2377
|
+
.option(
|
|
2378
|
+
"--uid <id>",
|
|
2379
|
+
"Douyin uid to disambiguate multiple accounts (default: largest record_<uid> table)",
|
|
2380
|
+
)
|
|
2381
|
+
.option("--limit <n>", "Cap watch records (default 2000)")
|
|
2382
|
+
.option(
|
|
2383
|
+
"--resolve-titles",
|
|
2384
|
+
"Resolve aweme ids → desc/author/duration via the web detail endpoint (no signing) so events show WHAT was watched",
|
|
2385
|
+
)
|
|
2386
|
+
.option(
|
|
2387
|
+
"--title-limit <n>",
|
|
2388
|
+
"Cap unique videos to title-resolve (default 60; dedup'd, ~200ms/call)",
|
|
2389
|
+
)
|
|
2390
|
+
.option("--display-name <s>", "Account displayName for the snapshot")
|
|
2391
|
+
.option(
|
|
2392
|
+
"--staging-dir <path>",
|
|
2393
|
+
"Custom dir for the temp snapshot JSON (default os.tmpdir())",
|
|
2394
|
+
)
|
|
2395
|
+
.option("--json", "Output JSON")
|
|
2396
|
+
.action(cmdDouyinWatchSync);
|
|
2397
|
+
|
|
2312
2398
|
hub
|
|
2313
2399
|
.command("rederive")
|
|
2314
2400
|
.description(
|
|
@@ -2555,6 +2641,7 @@ export const _internal = {
|
|
|
2555
2641
|
cmdBilibiliAdbSync,
|
|
2556
2642
|
cmdBilibiliAdbDoctor,
|
|
2557
2643
|
cmdDouyinAdbSync,
|
|
2644
|
+
cmdDouyinWatchSync,
|
|
2558
2645
|
cmdWeiboAdbSync,
|
|
2559
2646
|
cmdXhsAdbSync,
|
|
2560
2647
|
cmdToutiaoAdbSync,
|
|
@@ -70,6 +70,23 @@ export function isHttpTransport(transportKind) {
|
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Heuristic: does this error look like the server went away (vs. the tool
|
|
75
|
+
* itself failing)? Used to gate reconnect-and-retry for servers that have a
|
|
76
|
+
* registered reconnector (e.g. the IDE bridge after a window reload, which
|
|
77
|
+
* comes back on a NEW port with a NEW token).
|
|
78
|
+
*
|
|
79
|
+
* Covers: fetch-level network failures, auth rejection after a token
|
|
80
|
+
* rotation (401/403), an unknown session on a restarted server (404), and
|
|
81
|
+
* this client's own "server gone" states.
|
|
82
|
+
*/
|
|
83
|
+
export function isLikelyConnectionError(err) {
|
|
84
|
+
const msg = String((err && err.message) || err || "");
|
|
85
|
+
return /fetch failed|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EPIPE|socket hang up|network error|HTTP 40[134]\b|not connected|not found|not available/i.test(
|
|
86
|
+
msg,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
73
90
|
/**
|
|
74
91
|
* MCP Client — manages connections to MCP servers.
|
|
75
92
|
*/
|
|
@@ -78,6 +95,23 @@ export class MCPClient extends EventEmitter {
|
|
|
78
95
|
super();
|
|
79
96
|
this.servers = new Map(); // name → { process, state, tools, resources, config }
|
|
80
97
|
this._nextId = 1;
|
|
98
|
+
this._reconnectors = new Map(); // name → async () => config|null
|
|
99
|
+
this._reconnecting = new Map(); // name → in-flight reconnect promise
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Register a reconnector for a server: an async function that returns a
|
|
104
|
+
* FRESH connection config (or null when the server can't be found anymore).
|
|
105
|
+
* When a `callTool` on that server fails with a connection-shaped error,
|
|
106
|
+
* the client re-resolves the config, reconnects, and retries the call once.
|
|
107
|
+
*
|
|
108
|
+
* Used by the IDE bridge: a window reload / extension update restarts the
|
|
109
|
+
* editor's MCP server on a new port with a new token, so the original
|
|
110
|
+
* config is permanently dead but a lockfile re-scan finds the new one.
|
|
111
|
+
*/
|
|
112
|
+
setReconnector(name, fn) {
|
|
113
|
+
if (typeof fn === "function") this._reconnectors.set(name, fn);
|
|
114
|
+
else this._reconnectors.delete(name);
|
|
81
115
|
}
|
|
82
116
|
|
|
83
117
|
/**
|
|
@@ -286,12 +320,31 @@ export class MCPClient extends EventEmitter {
|
|
|
286
320
|
}
|
|
287
321
|
|
|
288
322
|
/**
|
|
289
|
-
* Call a tool on a specific server.
|
|
323
|
+
* Call a tool on a specific server. If the server has a registered
|
|
324
|
+
* reconnector and the call fails with a connection-shaped error (server
|
|
325
|
+
* restarted / token rotated / entry dropped), re-resolve the config,
|
|
326
|
+
* reconnect, and retry the call exactly once.
|
|
290
327
|
* @param {string} serverName - Server name
|
|
291
328
|
* @param {string} toolName - Tool name
|
|
292
329
|
* @param {object} args - Tool arguments
|
|
293
330
|
*/
|
|
294
331
|
async callTool(serverName, toolName, args = {}) {
|
|
332
|
+
try {
|
|
333
|
+
return await this._callToolOnce(serverName, toolName, args);
|
|
334
|
+
} catch (err) {
|
|
335
|
+
if (
|
|
336
|
+
!this._reconnectors.has(serverName) ||
|
|
337
|
+
!isLikelyConnectionError(err)
|
|
338
|
+
) {
|
|
339
|
+
throw err;
|
|
340
|
+
}
|
|
341
|
+
const reconnected = await this._tryReconnect(serverName);
|
|
342
|
+
if (!reconnected) throw err;
|
|
343
|
+
return await this._callToolOnce(serverName, toolName, args);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async _callToolOnce(serverName, toolName, args) {
|
|
295
348
|
const entry = this.servers.get(serverName);
|
|
296
349
|
if (!entry) throw new Error(`Server "${serverName}" not found`);
|
|
297
350
|
if (entry.state !== ServerState.CONNECTED) {
|
|
@@ -306,6 +359,38 @@ export class MCPClient extends EventEmitter {
|
|
|
306
359
|
return result;
|
|
307
360
|
}
|
|
308
361
|
|
|
362
|
+
/**
|
|
363
|
+
* Re-resolve a server's config via its reconnector and reconnect.
|
|
364
|
+
* Single-flight per server: concurrent failing calls share one attempt
|
|
365
|
+
* (the IDE context injector fires getSelection + getOpenEditors in
|
|
366
|
+
* parallel — a double connect would throw "already connected").
|
|
367
|
+
* Resolves true on success, false on any failure (original error wins).
|
|
368
|
+
*/
|
|
369
|
+
_tryReconnect(name) {
|
|
370
|
+
const inFlight = this._reconnecting.get(name);
|
|
371
|
+
if (inFlight) return inFlight;
|
|
372
|
+
const p = (async () => {
|
|
373
|
+
try {
|
|
374
|
+
const fresh = await this._reconnectors.get(name)();
|
|
375
|
+
if (!fresh) return false;
|
|
376
|
+
try {
|
|
377
|
+
await this.disconnect(name);
|
|
378
|
+
} catch {
|
|
379
|
+
// entry may already be gone — connect() below is what matters
|
|
380
|
+
}
|
|
381
|
+
await this.connect(name, fresh);
|
|
382
|
+
this.emit("server-reconnected", { name, url: fresh.url || null });
|
|
383
|
+
return true;
|
|
384
|
+
} catch {
|
|
385
|
+
return false;
|
|
386
|
+
} finally {
|
|
387
|
+
this._reconnecting.delete(name);
|
|
388
|
+
}
|
|
389
|
+
})();
|
|
390
|
+
this._reconnecting.set(name, p);
|
|
391
|
+
return p;
|
|
392
|
+
}
|
|
393
|
+
|
|
309
394
|
/**
|
|
310
395
|
* List resources from a specific server or all servers. Each resource is
|
|
311
396
|
* annotated with its owning `server` (mirrors `listTools`).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-checkpoint default resolution — Claude-Code parity: checkpoints are ON
|
|
3
|
+
* by default inside a git repository (the shadow-commit engine never touches
|
|
4
|
+
* the working tree or real index, so the only cost is a plumbing commit per
|
|
5
|
+
* mutating tool), OFF elsewhere (the copy-based fallback writes real files
|
|
6
|
+
* under the user home — too surprising as a silent default).
|
|
7
|
+
*
|
|
8
|
+
* Explicit flags always win: `--checkpoint` forces on anywhere (copy engine
|
|
9
|
+
* included), `--no-checkpoint` forces off.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fsDefault from "fs";
|
|
13
|
+
import pathDefault from "path";
|
|
14
|
+
|
|
15
|
+
export const _deps = { fs: fsDefault, path: pathDefault };
|
|
16
|
+
|
|
17
|
+
/** Walk up from cwd looking for a `.git` marker. */
|
|
18
|
+
export function isInsideGitRepo(cwd, deps = _deps) {
|
|
19
|
+
let dir = deps.path.resolve(cwd || ".");
|
|
20
|
+
for (;;) {
|
|
21
|
+
try {
|
|
22
|
+
if (deps.fs.existsSync(deps.path.join(dir, ".git"))) return true;
|
|
23
|
+
} catch {
|
|
24
|
+
/* keep walking */
|
|
25
|
+
}
|
|
26
|
+
const parent = deps.path.dirname(dir);
|
|
27
|
+
if (parent === dir) return false;
|
|
28
|
+
dir = parent;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {object} opts
|
|
34
|
+
* @param {boolean} [opts.flagValue] commander's options.checkpoint
|
|
35
|
+
* @param {string} [opts.flagSource] commander getOptionValueSource("checkpoint")
|
|
36
|
+
* ("cli" when --checkpoint/--no-checkpoint given)
|
|
37
|
+
* @param {string} [opts.cwd]
|
|
38
|
+
* @param {object} [opts.deps]
|
|
39
|
+
* @returns {boolean}
|
|
40
|
+
*/
|
|
41
|
+
export function resolveAutoCheckpoint({
|
|
42
|
+
flagValue,
|
|
43
|
+
flagSource,
|
|
44
|
+
cwd = process.cwd(),
|
|
45
|
+
deps = _deps,
|
|
46
|
+
} = {}) {
|
|
47
|
+
if (flagSource === "cli") return flagValue === true;
|
|
48
|
+
return isInsideGitRepo(cwd, deps);
|
|
49
|
+
}
|
|
@@ -363,7 +363,7 @@ async function initHub() {
|
|
|
363
363
|
// Phase 2a: register `douyin.pull-im-db` extension so the
|
|
364
364
|
// douyinAdbSync hub method can pull <uid>_im.db cohort from the
|
|
365
365
|
// user's Android Douyin App.
|
|
366
|
-
const { createDouyinDbExtension } =
|
|
366
|
+
const { createDouyinDbExtension, createDouyinWatchExtension } =
|
|
367
367
|
await import("@chainlesschain/personal-data-hub/adapters/social-douyin-adb");
|
|
368
368
|
// Phase 3a: register `weibo.cookies` extension for the Weibo
|
|
369
369
|
// C-path collector (m.weibo.cn cookies + 4 HTTP endpoints).
|
|
@@ -377,7 +377,7 @@ async function initHub() {
|
|
|
377
377
|
// C-path collector (www.toutiao.com cookies + 4 endpoints, 3 of
|
|
378
378
|
// which need _signature signing — CLI context falls back to -99
|
|
379
379
|
// short-circuit, desktop wiring upgrades via ToutiaoSignBridge).
|
|
380
|
-
const { createToutiaoCookiesExtension } =
|
|
380
|
+
const { createToutiaoCookiesExtension, createToutiaoAccountExtension } =
|
|
381
381
|
await import("@chainlesschain/personal-data-hub/adapters/social-toutiao-adb");
|
|
382
382
|
// Phase 6d: register `kuaishou.cookies` extension. Profile from
|
|
383
383
|
// cookie's api_ph payload (no HTTP); 3 GraphQL POST endpoints need
|
|
@@ -389,9 +389,11 @@ async function initHub() {
|
|
|
389
389
|
extensions: {
|
|
390
390
|
"bilibili.cookies": createBilibiliCookiesExtension(),
|
|
391
391
|
"douyin.pull-im-db": createDouyinDbExtension(),
|
|
392
|
+
"douyin.watch-history": createDouyinWatchExtension(),
|
|
392
393
|
"weibo.cookies": createWeiboCookiesExtension(),
|
|
393
394
|
"xhs.cookies": createXhsCookiesExtension(),
|
|
394
395
|
"toutiao.cookies": createToutiaoCookiesExtension(),
|
|
396
|
+
"toutiao.account": createToutiaoAccountExtension(),
|
|
395
397
|
"kuaishou.cookies": createKuaishouCookiesExtension(),
|
|
396
398
|
},
|
|
397
399
|
});
|
|
@@ -1037,6 +1039,46 @@ async function initHub() {
|
|
|
1037
1039
|
}
|
|
1038
1040
|
},
|
|
1039
1041
|
|
|
1042
|
+
// ─── Douyin watch-history (video_record.db, plaintext, no X-Bogus) ───
|
|
1043
|
+
// Pulls video_record.db via the douyin.watch-history extension → reads
|
|
1044
|
+
// record_<uid> (aid + view_time_timestamp + enter_from) → history events
|
|
1045
|
+
// → syncAdapter("social-douyin") snapshot mode. Distinct from
|
|
1046
|
+
// douyinAdbSync (IM db). No SQLCipher, no signing.
|
|
1047
|
+
async douyinWatchSync(opts = {}) {
|
|
1048
|
+
if (!hostAdbBridge) {
|
|
1049
|
+
return {
|
|
1050
|
+
ok: false,
|
|
1051
|
+
reason: "BRIDGE_UNAVAILABLE",
|
|
1052
|
+
message:
|
|
1053
|
+
"host-adb-bridge failed to initialize at hub boot — check `adb` is on PATH or set ADB_PATH env var",
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
let collector;
|
|
1057
|
+
try {
|
|
1058
|
+
const mod =
|
|
1059
|
+
await import("@chainlesschain/personal-data-hub/adapters/social-douyin-adb");
|
|
1060
|
+
collector = mod.default ? mod.default : mod;
|
|
1061
|
+
} catch (err) {
|
|
1062
|
+
return {
|
|
1063
|
+
ok: false,
|
|
1064
|
+
reason: "MODULE_LOAD_FAILED",
|
|
1065
|
+
message: err && err.message ? err.message : String(err),
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
try {
|
|
1069
|
+
const report = await collector.collectWatchHistoryAndSync(
|
|
1070
|
+
hostAdbBridge,
|
|
1071
|
+
registry,
|
|
1072
|
+
opts,
|
|
1073
|
+
);
|
|
1074
|
+
return { ok: true, report };
|
|
1075
|
+
} catch (err) {
|
|
1076
|
+
const msg = err && err.message ? err.message : String(err);
|
|
1077
|
+
const m = msg.match(/^(DOUYIN_[A-Z_]+)/);
|
|
1078
|
+
return { ok: false, reason: m ? m[1] : "SYNC_FAILED", message: msg };
|
|
1079
|
+
}
|
|
1080
|
+
},
|
|
1081
|
+
|
|
1040
1082
|
// ─── Phase 3a — Weibo C 路径 one-shot sync ──────────────────────────
|
|
1041
1083
|
//
|
|
1042
1084
|
// Pulls m.weibo.cn cookies via the weibo.cookies extension → fetchUid
|
|
@@ -86,7 +86,10 @@ export function fileCandidates(prefix, { cwd = process.cwd(), deps } = {}) {
|
|
|
86
86
|
* slashCommands?: string[], deps? }
|
|
87
87
|
*/
|
|
88
88
|
export function makeAtCompleter(opts = {}) {
|
|
89
|
-
|
|
89
|
+
// Lazy when not pinned: `/cd` mid-session moves process.cwd() and the
|
|
90
|
+
// completer must follow (explicit opts.cwd stays static for tests).
|
|
91
|
+
const baseCwd = opts.cwd || null;
|
|
92
|
+
const getCwd = () => baseCwd || process.cwd();
|
|
90
93
|
const getIde = opts.getIdeOpenFiles || null;
|
|
91
94
|
const now = opts.deps?.now || Date.now;
|
|
92
95
|
let ideFiles = [];
|
|
@@ -105,7 +108,7 @@ export function makeAtCompleter(opts = {}) {
|
|
|
105
108
|
? files
|
|
106
109
|
.filter((f) => typeof f === "string" && f.length > 0)
|
|
107
110
|
.map((f) => {
|
|
108
|
-
const rel = path.relative(
|
|
111
|
+
const rel = path.relative(getCwd(), f);
|
|
109
112
|
// Keep workspace files relative (the natural @ref form);
|
|
110
113
|
// out-of-workspace files keep their absolute path. On
|
|
111
114
|
// Windows a cross-drive relative() returns an *absolute*
|
|
@@ -145,7 +148,7 @@ export function makeAtCompleter(opts = {}) {
|
|
|
145
148
|
refreshIde(); // async top-up for the NEXT tab; this one uses the cache
|
|
146
149
|
const norm = fwd(at.prefix).toLowerCase();
|
|
147
150
|
const fromIde = ideFiles.filter((f) => f.toLowerCase().startsWith(norm));
|
|
148
|
-
const fromFs = fileCandidates(at.prefix, { cwd, deps: opts.deps });
|
|
151
|
+
const fromFs = fileCandidates(at.prefix, { cwd: getCwd(), deps: opts.deps });
|
|
149
152
|
const merged = [...new Set([...fromIde, ...fromFs])].slice(
|
|
150
153
|
0,
|
|
151
154
|
MAX_CANDIDATES,
|