@letrquan/book 0.2.0 → 0.4.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3771 -40
- package/README.md +135 -1311
- package/dist/additional-roots-VAWRQ625.js +32 -0
- package/dist/{app-MOXXWEOY.js → app-J7BVPSUH.js} +9648 -5509
- package/dist/app-J7BVPSUH.js.map +1 -0
- package/dist/chunk-2AAOF5B5.js +28 -0
- package/dist/chunk-2AAOF5B5.js.map +1 -0
- package/dist/chunk-32WBCUAN.js +660 -0
- package/dist/chunk-32WBCUAN.js.map +1 -0
- package/dist/chunk-4DKHLZRI.js +120 -0
- package/dist/chunk-4DKHLZRI.js.map +1 -0
- package/dist/{chunk-CAGOB2N4.js → chunk-62EEAUID.js} +6 -4
- package/dist/chunk-62EEAUID.js.map +1 -0
- package/dist/chunk-AHC7OU75.js +1124 -0
- package/dist/chunk-AHC7OU75.js.map +1 -0
- package/dist/{chunk-E75IPST5.js → chunk-AJZWLKLQ.js} +73 -70
- package/dist/chunk-AJZWLKLQ.js.map +1 -0
- package/dist/chunk-B5FC3MYZ.js +395 -0
- package/dist/chunk-B5FC3MYZ.js.map +1 -0
- package/dist/{chunk-KWHQ2XXL.js → chunk-DNFQTSMJ.js} +55 -13
- package/dist/chunk-DNFQTSMJ.js.map +1 -0
- package/dist/{chunk-J7MIZ5CA.js → chunk-E5WLQSKA.js} +719 -89
- package/dist/chunk-E5WLQSKA.js.map +1 -0
- package/dist/chunk-ECLC4CIJ.js +1181 -0
- package/dist/chunk-ECLC4CIJ.js.map +1 -0
- package/dist/chunk-EEX6QNXV.js +127 -0
- package/dist/chunk-EEX6QNXV.js.map +1 -0
- package/dist/chunk-EFXVSQ5J.js +931 -0
- package/dist/chunk-EFXVSQ5J.js.map +1 -0
- package/dist/{chunk-V4BOYR52.js → chunk-ENLB7NYV.js} +1304 -326
- package/dist/chunk-ENLB7NYV.js.map +1 -0
- package/dist/chunk-FPR7SKIE.js +6926 -0
- package/dist/chunk-FPR7SKIE.js.map +1 -0
- package/dist/{chunk-DGMQEI6Y.js → chunk-HFB74CLM.js} +30 -9
- package/dist/chunk-HFB74CLM.js.map +1 -0
- package/dist/{chunk-5RDRAO4B.js → chunk-HOZXKOE3.js} +37 -1
- package/dist/chunk-HOZXKOE3.js.map +1 -0
- package/dist/chunk-IDYWN37T.js +36 -0
- package/dist/chunk-IDYWN37T.js.map +1 -0
- package/dist/chunk-J4F2QLM2.js +273 -0
- package/dist/chunk-J4F2QLM2.js.map +1 -0
- package/dist/chunk-JGF5HLSI.js +1065 -0
- package/dist/chunk-JGF5HLSI.js.map +1 -0
- package/dist/{chunk-XIFFTATU.js → chunk-KDNWMKXV.js} +7166 -4237
- package/dist/chunk-KDNWMKXV.js.map +1 -0
- package/dist/chunk-KXAYUIKP.js +90 -0
- package/dist/chunk-KXAYUIKP.js.map +1 -0
- package/dist/{chunk-3U5IBM24.js → chunk-MEOB2DZA.js} +12 -2
- package/dist/chunk-MEOB2DZA.js.map +1 -0
- package/dist/{chunk-WKBO5R4O.js → chunk-MV5PQNIA.js} +2 -2
- package/dist/chunk-MV5PQNIA.js.map +1 -0
- package/dist/chunk-N2G4Q7ME.js +2672 -0
- package/dist/chunk-N2G4Q7ME.js.map +1 -0
- package/dist/chunk-OJBMDM5H.js +299 -0
- package/dist/chunk-OJBMDM5H.js.map +1 -0
- package/dist/{chunk-URLQAXTR.js → chunk-PTDC7ZUP.js} +2 -2
- package/dist/chunk-PTDC7ZUP.js.map +1 -0
- package/dist/{chunk-574C6MQR.js → chunk-REASC2QG.js} +173 -64
- package/dist/chunk-REASC2QG.js.map +1 -0
- package/dist/{chunk-7N4J557C.js → chunk-RT5SATF7.js} +1 -1
- package/dist/chunk-RT5SATF7.js.map +1 -0
- package/dist/chunk-SBTU5BEC.js +114 -0
- package/dist/chunk-SBTU5BEC.js.map +1 -0
- package/dist/{chunk-5KLVH3PY.js → chunk-SWPDKLNM.js} +5 -3
- package/dist/chunk-SWPDKLNM.js.map +1 -0
- package/dist/chunk-UCFJ43OS.js +520 -0
- package/dist/chunk-UCFJ43OS.js.map +1 -0
- package/dist/{chunk-VZQXX3WW.js → chunk-W7SLYS3M.js} +80 -16
- package/dist/chunk-W7SLYS3M.js.map +1 -0
- package/dist/{chunk-ANSJPYGI.js → chunk-WV3O5PGX.js} +3 -3
- package/dist/chunk-WV3O5PGX.js.map +1 -0
- package/dist/chunk-XHAO3K7I.js +18 -0
- package/dist/chunk-XHAO3K7I.js.map +1 -0
- package/dist/chunk-XJRJVXJR.js +1050 -0
- package/dist/chunk-XJRJVXJR.js.map +1 -0
- package/dist/chunk-YIL6HTJX.js +663 -0
- package/dist/chunk-YIL6HTJX.js.map +1 -0
- package/dist/{chunk-24AXE6SP.js → chunk-ZPSZZUWF.js} +1 -1
- package/dist/chunk-ZPSZZUWF.js.map +1 -0
- package/dist/{command-approvals-6Y57VCWZ.js → command-approvals-ZRY66LCW.js} +7 -5
- package/dist/context-C3RAP6K5.js +41 -0
- package/dist/{hook-approvals-CTUFKEJT.js → hook-approvals-DPQD5H65.js} +6 -5
- package/dist/index.js +947 -203
- package/dist/index.js.map +1 -1
- package/dist/{interactive-assets-CCAAWVKH.js → interactive-assets-73AQO5WP.js} +18 -9
- package/dist/interactive-assets-73AQO5WP.js.map +1 -0
- package/dist/job-runner.js +166 -111
- package/dist/job-runner.js.map +1 -1
- package/dist/job-supervisor.d.ts +2 -0
- package/dist/job-supervisor.js +121 -0
- package/dist/job-supervisor.js.map +1 -0
- package/dist/loader-DQNPDKOX.js +27 -0
- package/dist/{mcp-approvals-WNPH65BK.js → mcp-approvals-27LJYADQ.js} +6 -5
- package/dist/{mcp-config-YIJIES2C.js → mcp-config-GXSODNCM.js} +4 -3
- package/dist/memory-store-I6Z54VUN.js +51 -0
- package/dist/{model-window-store-YFVNXCIP.js → model-window-store-6VKXXTPJ.js} +9 -4
- package/dist/openai-compatible-IPUNBJ53.js +16 -0
- package/dist/permission-approvals-Q5IMFEZI.js +15 -0
- package/dist/permissions-7Y35X6HZ.js +62 -0
- package/dist/provider-approvals-5AGH3RVT.js +76 -0
- package/dist/sandbox-N2RY72GN.js +42 -0
- package/dist/{scrollback-LUKLIBVI.js → scrollback-GD76GNM4.js} +44 -26
- package/dist/scrollback-GD76GNM4.js.map +1 -0
- package/dist/sdk.d.ts +2739 -2069
- package/dist/sdk.js +77 -42
- package/dist/sdk.js.map +1 -1
- package/dist/settings-loader-KDBH45AQ.js +43 -0
- package/dist/settings-loader-KDBH45AQ.js.map +1 -0
- package/dist/{settings-removed-JWWPT4WA.js → settings-removed-3ATWJEF3.js} +2 -2
- package/dist/settings-removed-3ATWJEF3.js.map +1 -0
- package/dist/shell-selection-Z5WORZU4.js +31 -0
- package/dist/shell-selection-Z5WORZU4.js.map +1 -0
- package/package.json +25 -24
- package/dist/app-MOXXWEOY.js.map +0 -1
- package/dist/chunk-24AXE6SP.js.map +0 -1
- package/dist/chunk-3U5IBM24.js.map +0 -1
- package/dist/chunk-574C6MQR.js.map +0 -1
- package/dist/chunk-5GDZ22YP.js +0 -449
- package/dist/chunk-5GDZ22YP.js.map +0 -1
- package/dist/chunk-5KLVH3PY.js.map +0 -1
- package/dist/chunk-5RDRAO4B.js.map +0 -1
- package/dist/chunk-7N4J557C.js.map +0 -1
- package/dist/chunk-A7JLBK2Y.js +0 -3506
- package/dist/chunk-A7JLBK2Y.js.map +0 -1
- package/dist/chunk-ANSJPYGI.js.map +0 -1
- package/dist/chunk-BTZZUCLI.js +0 -264
- package/dist/chunk-BTZZUCLI.js.map +0 -1
- package/dist/chunk-C7D4NJ2D.js +0 -217
- package/dist/chunk-C7D4NJ2D.js.map +0 -1
- package/dist/chunk-CAGOB2N4.js.map +0 -1
- package/dist/chunk-DGMQEI6Y.js.map +0 -1
- package/dist/chunk-E75IPST5.js.map +0 -1
- package/dist/chunk-EYGD6X2T.js +0 -180
- package/dist/chunk-EYGD6X2T.js.map +0 -1
- package/dist/chunk-GTVB4HEM.js +0 -765
- package/dist/chunk-GTVB4HEM.js.map +0 -1
- package/dist/chunk-J7MIZ5CA.js.map +0 -1
- package/dist/chunk-KWHQ2XXL.js.map +0 -1
- package/dist/chunk-L6LK2QIQ.js +0 -657
- package/dist/chunk-L6LK2QIQ.js.map +0 -1
- package/dist/chunk-MAQXCUR4.js +0 -1007
- package/dist/chunk-MAQXCUR4.js.map +0 -1
- package/dist/chunk-S4XL7HOM.js +0 -402
- package/dist/chunk-S4XL7HOM.js.map +0 -1
- package/dist/chunk-URLQAXTR.js.map +0 -1
- package/dist/chunk-V4BOYR52.js.map +0 -1
- package/dist/chunk-VZQXX3WW.js.map +0 -1
- package/dist/chunk-WKBO5R4O.js.map +0 -1
- package/dist/chunk-XIFFTATU.js.map +0 -1
- package/dist/context-ZC5IFEAX.js +0 -25
- package/dist/interactive-assets-CCAAWVKH.js.map +0 -1
- package/dist/loader-4IDS2RIE.js +0 -15
- package/dist/openai-compatible-RHZUBHV6.js +0 -10
- package/dist/permission-approvals-76PVREFD.js +0 -14
- package/dist/permissions-CHCKTFQQ.js +0 -28
- package/dist/sandbox-CNVAQDA7.js +0 -23
- package/dist/scrollback-LUKLIBVI.js.map +0 -1
- package/dist/settings-loader-M2QP5TX2.js +0 -21
- package/dist/shell-selection-V6QAYXPX.js +0 -17
- package/patches/ink+6.8.0.patch +0 -13
- package/scripts/apply-ink-patch.mjs +0 -29
- package/scripts/ink-patch.mjs +0 -59
- package/scripts/verify-ink-patch.mjs +0 -11
- /package/dist/{command-approvals-6Y57VCWZ.js.map → additional-roots-VAWRQ625.js.map} +0 -0
- /package/dist/{context-ZC5IFEAX.js.map → command-approvals-ZRY66LCW.js.map} +0 -0
- /package/dist/{hook-approvals-CTUFKEJT.js.map → context-C3RAP6K5.js.map} +0 -0
- /package/dist/{loader-4IDS2RIE.js.map → hook-approvals-DPQD5H65.js.map} +0 -0
- /package/dist/{mcp-approvals-WNPH65BK.js.map → loader-DQNPDKOX.js.map} +0 -0
- /package/dist/{mcp-config-YIJIES2C.js.map → mcp-approvals-27LJYADQ.js.map} +0 -0
- /package/dist/{model-window-store-YFVNXCIP.js.map → mcp-config-GXSODNCM.js.map} +0 -0
- /package/dist/{openai-compatible-RHZUBHV6.js.map → memory-store-I6Z54VUN.js.map} +0 -0
- /package/dist/{permission-approvals-76PVREFD.js.map → model-window-store-6VKXXTPJ.js.map} +0 -0
- /package/dist/{permissions-CHCKTFQQ.js.map → openai-compatible-IPUNBJ53.js.map} +0 -0
- /package/dist/{sandbox-CNVAQDA7.js.map → permission-approvals-Q5IMFEZI.js.map} +0 -0
- /package/dist/{settings-loader-M2QP5TX2.js.map → permissions-7Y35X6HZ.js.map} +0 -0
- /package/dist/{settings-removed-JWWPT4WA.js.map → provider-approvals-5AGH3RVT.js.map} +0 -0
- /package/dist/{shell-selection-V6QAYXPX.js.map → sandbox-N2RY72GN.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,98 +1,159 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
checkProviderKey,
|
|
4
|
+
hasInkTrailingNewlineFix,
|
|
5
|
+
inkBuildDir,
|
|
3
6
|
installFrameCapture,
|
|
4
|
-
|
|
5
|
-
|
|
7
|
+
installInkFrameClip,
|
|
8
|
+
installInkOutputCache,
|
|
9
|
+
installInkRenderCull,
|
|
10
|
+
installInkScrollRenderer,
|
|
11
|
+
isCiEnvironment,
|
|
12
|
+
loadInkFlushSync,
|
|
13
|
+
providerKeyTarget,
|
|
14
|
+
resolveInkMaxFps,
|
|
15
|
+
resolveTuiRendererMode,
|
|
16
|
+
validateApiKey,
|
|
17
|
+
writeProviderKey
|
|
18
|
+
} from "./chunk-ECLC4CIJ.js";
|
|
6
19
|
import {
|
|
7
20
|
buildMcpToolDefinitions,
|
|
21
|
+
cleanupRunStatus,
|
|
22
|
+
collectSkippedProjectFileNotices,
|
|
8
23
|
collectWithheldProjectNotices,
|
|
24
|
+
collectWithheldProviderNotices,
|
|
9
25
|
connectMcpServers,
|
|
10
26
|
disconnectMcpServers,
|
|
27
|
+
isReaderGoneError,
|
|
11
28
|
readRunStatus,
|
|
12
|
-
runHeadless
|
|
13
|
-
|
|
29
|
+
runHeadless,
|
|
30
|
+
runStatusPath
|
|
31
|
+
} from "./chunk-E5WLQSKA.js";
|
|
32
|
+
import {
|
|
33
|
+
evaluateMcpServerApproval,
|
|
34
|
+
mcpServerFingerprint,
|
|
35
|
+
mcpServersToRecord,
|
|
36
|
+
partitionMcpServersByApproval,
|
|
37
|
+
persistMcpProjectServerChoice
|
|
38
|
+
} from "./chunk-PTDC7ZUP.js";
|
|
14
39
|
import {
|
|
15
40
|
SessionStore,
|
|
16
|
-
|
|
17
|
-
|
|
41
|
+
discardUntouchedSessions,
|
|
42
|
+
parseSessionDriver,
|
|
43
|
+
resolveSessionBootstrap,
|
|
44
|
+
resolveSessionDriver
|
|
45
|
+
} from "./chunk-ENLB7NYV.js";
|
|
18
46
|
import {
|
|
19
47
|
aggregateToolUse,
|
|
20
48
|
createDefaultRegistry,
|
|
21
49
|
formatToolStatsReport,
|
|
22
|
-
|
|
23
|
-
loadConfig,
|
|
50
|
+
pathExists,
|
|
24
51
|
pruneToolUseRecords,
|
|
25
52
|
readToolUseRecords
|
|
26
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-KDNWMKXV.js";
|
|
27
54
|
import {
|
|
28
|
-
EFFORT_LEVELS,
|
|
29
55
|
applySettingWrite,
|
|
30
56
|
describeSettingShadow,
|
|
31
57
|
discoverAgents,
|
|
32
58
|
estimateUsageCost,
|
|
33
59
|
getPackageVersion,
|
|
34
|
-
isEffortLevel,
|
|
35
|
-
parseEffortLevel,
|
|
36
60
|
readScopeDocument,
|
|
37
61
|
resolvePermissionMode,
|
|
38
62
|
settingsScopeLabel,
|
|
39
63
|
withBuiltInAgents
|
|
40
|
-
} from "./chunk-
|
|
41
|
-
import
|
|
42
|
-
|
|
64
|
+
} from "./chunk-FPR7SKIE.js";
|
|
65
|
+
import {
|
|
66
|
+
EFFORT_LEVELS,
|
|
67
|
+
freezeAgentConfig,
|
|
68
|
+
isEffortLevel,
|
|
69
|
+
loadConfig,
|
|
70
|
+
parseEffortLevel,
|
|
71
|
+
readLegacyBaseUrl
|
|
72
|
+
} from "./chunk-N2G4Q7ME.js";
|
|
73
|
+
import "./chunk-XJRJVXJR.js";
|
|
74
|
+
import "./chunk-AHC7OU75.js";
|
|
75
|
+
import "./chunk-DNFQTSMJ.js";
|
|
76
|
+
import {
|
|
77
|
+
applySettingsEnvOverrides,
|
|
78
|
+
isUserSettingsFile,
|
|
79
|
+
loadSettingsFile,
|
|
80
|
+
resolveSettings,
|
|
81
|
+
resolveSettingsWithProviderGate,
|
|
82
|
+
startupAnimationEnvNote
|
|
83
|
+
} from "./chunk-UCFJ43OS.js";
|
|
84
|
+
import "./chunk-HOZXKOE3.js";
|
|
85
|
+
import "./chunk-EFXVSQ5J.js";
|
|
86
|
+
import "./chunk-62EEAUID.js";
|
|
43
87
|
import {
|
|
44
88
|
discoverCommands
|
|
45
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-AJZWLKLQ.js";
|
|
90
|
+
import "./chunk-KXAYUIKP.js";
|
|
46
91
|
import {
|
|
47
92
|
describeProjectCommandShell,
|
|
48
93
|
displayableCommandName,
|
|
49
94
|
partitionProjectCommands,
|
|
50
95
|
persistProjectCommandChoice,
|
|
51
96
|
projectCommandFingerprint
|
|
52
|
-
} from "./chunk-
|
|
97
|
+
} from "./chunk-SWPDKLNM.js";
|
|
98
|
+
import "./chunk-2AAOF5B5.js";
|
|
53
99
|
import {
|
|
54
100
|
formatMcpServerCommand,
|
|
55
101
|
isValidMcpServerName,
|
|
56
102
|
resolveMcpServerList
|
|
57
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-MEOB2DZA.js";
|
|
104
|
+
import "./chunk-W7SLYS3M.js";
|
|
105
|
+
import "./chunk-JGF5HLSI.js";
|
|
106
|
+
import "./chunk-32WBCUAN.js";
|
|
107
|
+
import "./chunk-RT5SATF7.js";
|
|
58
108
|
import {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
partitionMcpServersByApproval,
|
|
63
|
-
persistMcpProjectServerChoice
|
|
64
|
-
} from "./chunk-URLQAXTR.js";
|
|
65
|
-
import "./chunk-MAQXCUR4.js";
|
|
109
|
+
buildChildEnv
|
|
110
|
+
} from "./chunk-XHAO3K7I.js";
|
|
111
|
+
import "./chunk-SBTU5BEC.js";
|
|
66
112
|
import {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
113
|
+
collectDeclaredDirectories,
|
|
114
|
+
partitionProjectDirectories,
|
|
115
|
+
persistProjectDirectoryChoice,
|
|
116
|
+
realPathOfDirectory
|
|
117
|
+
} from "./chunk-4DKHLZRI.js";
|
|
70
118
|
import {
|
|
71
119
|
DEFAULT_LOCAL_DATA_RETENTION_DAYS,
|
|
72
120
|
cleanupDebugLogs,
|
|
73
121
|
createDebugLogger,
|
|
74
122
|
getDebugLogPath
|
|
75
|
-
} from "./chunk-
|
|
76
|
-
import "./chunk-S4XL7HOM.js";
|
|
77
|
-
import "./chunk-7N4J557C.js";
|
|
123
|
+
} from "./chunk-OJBMDM5H.js";
|
|
78
124
|
import {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
125
|
+
canonicalToolName
|
|
126
|
+
} from "./chunk-YIL6HTJX.js";
|
|
127
|
+
import {
|
|
128
|
+
createUncGuard,
|
|
129
|
+
reachesUncPath
|
|
130
|
+
} from "./chunk-EEX6QNXV.js";
|
|
82
131
|
import {
|
|
83
132
|
partitionProjectAllowRules,
|
|
84
133
|
persistProjectAllowRuleChoice
|
|
85
|
-
} from "./chunk-
|
|
134
|
+
} from "./chunk-MV5PQNIA.js";
|
|
135
|
+
import {
|
|
136
|
+
LEGACY_BASE_URL_ID,
|
|
137
|
+
describeLegacyBaseUrl,
|
|
138
|
+
describeReservedProvider,
|
|
139
|
+
describeWorkspaceProvider,
|
|
140
|
+
environmentModelSelections,
|
|
141
|
+
evaluateLegacyBaseUrl,
|
|
142
|
+
legacyBaseUrlFingerprint,
|
|
143
|
+
persistWorkspaceProviderChoice,
|
|
144
|
+
providerTrustCommand,
|
|
145
|
+
trustCommandText,
|
|
146
|
+
withEnvironmentSelections
|
|
147
|
+
} from "./chunk-B5FC3MYZ.js";
|
|
86
148
|
import {
|
|
87
149
|
collectDeclaredHooks,
|
|
88
150
|
describeDeclaredHook,
|
|
89
151
|
hookFingerprint,
|
|
90
152
|
partitionProjectHooks,
|
|
91
|
-
persistProjectHookChoice
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
import "./chunk-
|
|
95
|
-
import "./chunk-CAGOB2N4.js";
|
|
153
|
+
persistProjectHookChoice,
|
|
154
|
+
renderUntrustedText
|
|
155
|
+
} from "./chunk-WV3O5PGX.js";
|
|
156
|
+
import "./chunk-HFB74CLM.js";
|
|
96
157
|
import {
|
|
97
158
|
DEFAULT_SETTINGS,
|
|
98
159
|
SettingsRepository,
|
|
@@ -102,21 +163,27 @@ import {
|
|
|
102
163
|
parseNumericFlag,
|
|
103
164
|
redactSettingValue,
|
|
104
165
|
redactSettingsForDisplay,
|
|
166
|
+
resolvePrintPrompt,
|
|
105
167
|
writeFileAtomic
|
|
106
|
-
} from "./chunk-
|
|
168
|
+
} from "./chunk-REASC2QG.js";
|
|
169
|
+
import {
|
|
170
|
+
projectFileWriteRefusal
|
|
171
|
+
} from "./chunk-IDYWN37T.js";
|
|
107
172
|
import {
|
|
108
173
|
resolveBookHome
|
|
109
|
-
} from "./chunk-
|
|
174
|
+
} from "./chunk-ZPSZZUWF.js";
|
|
110
175
|
|
|
111
176
|
// src/runtime-env.ts
|
|
112
177
|
if (!process.env.NODE_ENV) {
|
|
113
178
|
process.env.NODE_ENV = "production";
|
|
179
|
+
process.env.BOOK_DEFAULTED_NODE_ENV = "1";
|
|
114
180
|
}
|
|
115
181
|
|
|
116
182
|
// src/index.ts
|
|
117
183
|
import { InvalidArgumentError, program } from "commander";
|
|
118
184
|
|
|
119
185
|
// src/cli/status-cmd.ts
|
|
186
|
+
import { existsSync } from "fs";
|
|
120
187
|
import { join } from "path";
|
|
121
188
|
function isProcessAlive(pid) {
|
|
122
189
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -165,13 +232,17 @@ function buildSessionStatus(meta, loaded, runRecord) {
|
|
|
165
232
|
let costUsd = models.length > 0 && usage ? 0 : null;
|
|
166
233
|
if (usage) {
|
|
167
234
|
for (const model of models) {
|
|
168
|
-
const quote = estimateUsageCost(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
235
|
+
const quote = estimateUsageCost(
|
|
236
|
+
model,
|
|
237
|
+
{
|
|
238
|
+
promptTokens: usage.promptTokens,
|
|
239
|
+
completionTokens: usage.completionTokens,
|
|
240
|
+
totalTokens: usage.totalTokens,
|
|
241
|
+
cacheReadInputTokens: usage.cacheReadInputTokens,
|
|
242
|
+
cacheCreationInputTokens: usage.cacheCreationInputTokens
|
|
243
|
+
},
|
|
244
|
+
"pool"
|
|
245
|
+
);
|
|
175
246
|
if (quote.status !== "known") {
|
|
176
247
|
costUsd = null;
|
|
177
248
|
break;
|
|
@@ -314,7 +385,13 @@ function runStatusCommand(options) {
|
|
|
314
385
|
const stdout = options.stdout ?? process.stdout;
|
|
315
386
|
const home = options.home ?? resolveBookHome();
|
|
316
387
|
const store = options.store ?? new SessionStore(join(home, "sessions"));
|
|
317
|
-
const meta = options.session ? store.findById(options.session) ?? store.findByName(options.session) :
|
|
388
|
+
const meta = options.session ? store.findById(options.session) ?? store.findByName(options.session) : (
|
|
389
|
+
// A print run of host-performed commands records no message, only a run record (#428).
|
|
390
|
+
store.mostRecentInCwd(
|
|
391
|
+
options.workspace,
|
|
392
|
+
(candidate) => candidate.messageCount > 0 || existsSync(runStatusPath(candidate.id, home))
|
|
393
|
+
)
|
|
394
|
+
);
|
|
318
395
|
if (!meta) {
|
|
319
396
|
stdout.write(
|
|
320
397
|
options.session ? `No session found matching "${options.session}".
|
|
@@ -344,9 +421,12 @@ function exit(code = 0) {
|
|
|
344
421
|
function isExiting() {
|
|
345
422
|
return exiting;
|
|
346
423
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
424
|
+
var exitCodeFn = (code) => {
|
|
425
|
+
process.exitCode = code;
|
|
426
|
+
};
|
|
427
|
+
function setExitCode(code) {
|
|
428
|
+
exitCodeFn(code);
|
|
429
|
+
}
|
|
350
430
|
|
|
351
431
|
// src/agents/diagnostics.ts
|
|
352
432
|
var MUTATION_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -436,20 +516,14 @@ function collectAgentDiagnostics(config, profiles) {
|
|
|
436
516
|
}
|
|
437
517
|
|
|
438
518
|
// src/cli/doctor.ts
|
|
439
|
-
var
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
445
|
-
function trustCommandLine(workspace, rest) {
|
|
446
|
-
if (resolve(workspace) === resolve(process.cwd())) return `book trust ${rest}`;
|
|
447
|
-
const quoted = shellArgument(workspace);
|
|
448
|
-
return quoted === null ? null : `book trust ${rest} --workspace ${quoted}`;
|
|
449
|
-
}
|
|
519
|
+
var WORKSPACE_LAYER_TRUST = {
|
|
520
|
+
User: "trusted",
|
|
521
|
+
Project: "repository",
|
|
522
|
+
Local: "local"
|
|
523
|
+
};
|
|
450
524
|
function approvalHint(label, workspace, rest) {
|
|
451
|
-
const command =
|
|
452
|
-
return
|
|
525
|
+
const { command, note } = trustCommandText(workspace, rest);
|
|
526
|
+
return `${label} ${command}${note}`;
|
|
453
527
|
}
|
|
454
528
|
function formatRelativeTime(timestamp, now = Date.now()) {
|
|
455
529
|
const elapsedMs = Math.max(0, now - timestamp);
|
|
@@ -466,8 +540,8 @@ function describeCredentials(config) {
|
|
|
466
540
|
}
|
|
467
541
|
var ABSENT_LAYER = "\0absent-settings-layer";
|
|
468
542
|
async function settingsLayers(workspace) {
|
|
469
|
-
const { join: join8, resolve:
|
|
470
|
-
const root =
|
|
543
|
+
const { join: join8, resolve: resolve3 } = await import("path");
|
|
544
|
+
const root = resolve3(workspace);
|
|
471
545
|
return [
|
|
472
546
|
["User", join8(resolveBookHome(), "settings.json")],
|
|
473
547
|
["Project", join8(root, ".book", "settings.json")],
|
|
@@ -494,15 +568,15 @@ async function attributeFailingLayer(workspace) {
|
|
|
494
568
|
return void 0;
|
|
495
569
|
}
|
|
496
570
|
async function reportUnloadableConfig(workspace, error) {
|
|
497
|
-
const { existsSync:
|
|
498
|
-
const { resolve:
|
|
571
|
+
const { existsSync: existsSync5 } = await import("fs");
|
|
572
|
+
const { resolve: resolve3 } = await import("path");
|
|
499
573
|
console.log("Book Doctor");
|
|
500
574
|
console.log("===========");
|
|
501
575
|
console.log();
|
|
502
576
|
console.log("Version:", getPackageVersion());
|
|
503
577
|
console.log("Node:", process.version);
|
|
504
578
|
console.log("Platform:", process.platform, process.arch);
|
|
505
|
-
console.log("Workspace:",
|
|
579
|
+
console.log("Workspace:", resolve3(workspace));
|
|
506
580
|
console.log();
|
|
507
581
|
console.log("Configuration: FAILED TO LOAD");
|
|
508
582
|
console.log(" " + (error instanceof Error ? error.message : String(error)));
|
|
@@ -511,7 +585,7 @@ async function reportUnloadableConfig(workspace, error) {
|
|
|
511
585
|
const culprit = await attributeFailingLayer(workspace);
|
|
512
586
|
console.log(" Settings layers, in the order they are applied:");
|
|
513
587
|
for (const [label, path] of await settingsLayers(workspace)) {
|
|
514
|
-
const marker =
|
|
588
|
+
const marker = existsSync5(path) ? "[x]" : "[ ]";
|
|
515
589
|
const blame = label === culprit ? " <- the failure appears with this layer" : "";
|
|
516
590
|
console.log(" " + marker + " " + label + ": " + path + blame);
|
|
517
591
|
}
|
|
@@ -553,8 +627,8 @@ async function runDoctorCommand(workspace, options = {}) {
|
|
|
553
627
|
collectRemovedEnvNotices,
|
|
554
628
|
removedAuthStoreNotice,
|
|
555
629
|
readSettingsDocumentForNotices
|
|
556
|
-
} = await import("./settings-removed-
|
|
557
|
-
const { settingsLayerPaths } = await import("./settings-loader-
|
|
630
|
+
} = await import("./settings-removed-3ATWJEF3.js");
|
|
631
|
+
const { settingsLayerPaths } = await import("./settings-loader-KDBH45AQ.js");
|
|
558
632
|
const { join: joinPath } = await import("path");
|
|
559
633
|
const { existsSync: fileExists } = await import("fs");
|
|
560
634
|
const removedNotices = [
|
|
@@ -576,11 +650,13 @@ async function runDoctorCommand(workspace, options = {}) {
|
|
|
576
650
|
}
|
|
577
651
|
console.log();
|
|
578
652
|
console.log("Settings layers:");
|
|
579
|
-
const { existsSync:
|
|
653
|
+
const { existsSync: existsSync5 } = await import("fs");
|
|
580
654
|
const { join: join8 } = await import("path");
|
|
655
|
+
const { isUserSettingsFile: isUserSettingsFile2 } = await import("./settings-loader-KDBH45AQ.js");
|
|
581
656
|
for (const [label, path] of await settingsLayers(config.workspace)) {
|
|
582
|
-
const marker = options.noSettings ? "[-]" :
|
|
583
|
-
|
|
657
|
+
const marker = options.noSettings ? "[-]" : existsSync5(path) ? "[x]" : "[ ]";
|
|
658
|
+
const note = label === "Project" && isUserSettingsFile2(path) ? " (the User file; read once, as User)" : "";
|
|
659
|
+
console.log(" " + marker + " " + label + ": " + path + note);
|
|
584
660
|
}
|
|
585
661
|
if (options.noSettings) {
|
|
586
662
|
console.log(" (--no-settings: every layer skipped, defaults reported below)");
|
|
@@ -591,9 +667,10 @@ async function runDoctorCommand(workspace, options = {}) {
|
|
|
591
667
|
console.log(" Allow rules: " + perms.allow.length);
|
|
592
668
|
console.log(" Ask rules: " + perms.ask.length);
|
|
593
669
|
console.log(" Deny rules: " + perms.deny.length);
|
|
594
|
-
const { loadSettingsFile: loadSettingsFile2 } = await import("./settings-loader-
|
|
595
|
-
const { partitionProjectAllowRules: partitionProjectAllowRules2 } = await import("./permission-approvals-
|
|
596
|
-
const
|
|
670
|
+
const { loadSettingsFile: loadSettingsFile2 } = await import("./settings-loader-KDBH45AQ.js");
|
|
671
|
+
const { partitionProjectAllowRules: partitionProjectAllowRules2 } = await import("./permission-approvals-Q5IMFEZI.js");
|
|
672
|
+
const projectPath = join8(config.workspace, ".book", "settings.json");
|
|
673
|
+
const projectSettings = isUserSettingsFile2(projectPath) ? null : loadSettingsFile2(projectPath);
|
|
597
674
|
const projectAllow = partitionProjectAllowRules2(
|
|
598
675
|
projectSettings?.permissions?.allow ?? [],
|
|
599
676
|
perms.projectAllowRules
|
|
@@ -610,9 +687,33 @@ async function runDoctorCommand(workspace, options = {}) {
|
|
|
610
687
|
);
|
|
611
688
|
}
|
|
612
689
|
}
|
|
690
|
+
const { collectDeclaredDirectories: collectDeclaredDirectories2, partitionProjectDirectories: partitionProjectDirectories2, resolveAdditionalRoots } = await import("./additional-roots-VAWRQ625.js");
|
|
691
|
+
const projectDirectories = partitionProjectDirectories2(
|
|
692
|
+
collectDeclaredDirectories2(config.workspace, projectSettings?.additionalDirectories ?? []),
|
|
693
|
+
settings.projectDirectories
|
|
694
|
+
);
|
|
695
|
+
const honoredAdditionalRoots = resolveAdditionalRoots(
|
|
696
|
+
config.workspace,
|
|
697
|
+
settings.additionalDirectories
|
|
698
|
+
);
|
|
699
|
+
if (honoredAdditionalRoots.length > 0 || projectDirectories.pending.length > 0) {
|
|
700
|
+
console.log("Additional directories (Read, Glob and Grep may reach these):");
|
|
701
|
+
for (const root of honoredAdditionalRoots) console.log(" [x] " + root);
|
|
702
|
+
for (const directory of projectDirectories.pending) {
|
|
703
|
+
console.log(
|
|
704
|
+
" [!] " + directory.realPath + " (not in effect; declared as " + directory.declared + ")"
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
if (projectDirectories.pending.length > 0) {
|
|
708
|
+
console.log(approvalHint(" Approve one:", config.workspace, "dir <path>"));
|
|
709
|
+
console.log(
|
|
710
|
+
approvalHint(" Approve all pending:", config.workspace, "dir --all-pending") + " (add --reject to refuse)"
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
613
714
|
console.log();
|
|
614
|
-
const { discoverCommands: discoverCommands2 } = await import("./loader-
|
|
615
|
-
const { partitionProjectCommands: partitionProjectCommands2, describeProjectCommandShell: describeProjectCommandShell2, displayableCommandName: displayableCommandName2 } = await import("./command-approvals-
|
|
715
|
+
const { discoverCommands: discoverCommands2 } = await import("./loader-DQNPDKOX.js");
|
|
716
|
+
const { partitionProjectCommands: partitionProjectCommands2, describeProjectCommandShell: describeProjectCommandShell2, displayableCommandName: displayableCommandName2 } = await import("./command-approvals-ZRY66LCW.js");
|
|
616
717
|
const gatedCommands = partitionProjectCommands2(discoverCommands2(config.workspace), settings);
|
|
617
718
|
if (gatedCommands.approved.length + gatedCommands.pending.length + gatedCommands.rejected.length > 0) {
|
|
618
719
|
console.log("Project commands that run shell (require approval):");
|
|
@@ -632,6 +733,90 @@ async function runDoctorCommand(workspace, options = {}) {
|
|
|
632
733
|
}
|
|
633
734
|
console.log();
|
|
634
735
|
}
|
|
736
|
+
const providerGate = config.workspaceProviderGate;
|
|
737
|
+
if (providerGate) {
|
|
738
|
+
const {
|
|
739
|
+
describeDroppedOverride,
|
|
740
|
+
describeLegacyBaseUrl: describeLegacyBaseUrl2,
|
|
741
|
+
describeReservedProvider: describeReservedProvider2,
|
|
742
|
+
describeUnaffectedSelection,
|
|
743
|
+
describeWithheldSelection,
|
|
744
|
+
describeWorkspaceProvider: describeWorkspaceProvider2
|
|
745
|
+
} = await import("./provider-approvals-5AGH3RVT.js");
|
|
746
|
+
const STATE_MARKS = {
|
|
747
|
+
approved: { mark: "[x]", suffix: "" },
|
|
748
|
+
rejected: { mark: "[-]", suffix: " (rejected)" },
|
|
749
|
+
pending: { mark: "[!]", suffix: " (not in effect)" }
|
|
750
|
+
};
|
|
751
|
+
const providerGroups = ["approved", "rejected", "pending"].map((state) => ({
|
|
752
|
+
...STATE_MARKS[state],
|
|
753
|
+
state,
|
|
754
|
+
entries: providerGate.partition[state]
|
|
755
|
+
}));
|
|
756
|
+
const legacy = providerGate.legacyBaseUrl;
|
|
757
|
+
const anyPending = providerGate.partition.pending.length > 0 || legacy?.state === "pending";
|
|
758
|
+
if (providerGate.declared.length > 0 || legacy) {
|
|
759
|
+
console.log("Workspace-declared providers (require approval):");
|
|
760
|
+
for (const group of providerGroups) {
|
|
761
|
+
for (const entry of group.entries) {
|
|
762
|
+
const routed = group.state === "pending" ? providerGate.unaffectedSelections.filter((s) => s.providerId === entry.id) : [];
|
|
763
|
+
const [first, ...rest] = describeWorkspaceProvider2(entry, config.workspace, routed);
|
|
764
|
+
console.log(` ${group.mark} ${first}${group.suffix}`);
|
|
765
|
+
for (const line of rest) console.log(` ${line}`);
|
|
766
|
+
console.log(` declared in: ${entry.layers.join(", ")}`);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
for (const entry of providerGate.partition.reserved) {
|
|
770
|
+
console.log(` [!] ${describeReservedProvider2(entry)}`);
|
|
771
|
+
}
|
|
772
|
+
if (legacy) {
|
|
773
|
+
const { mark, suffix } = STATE_MARKS[legacy.state];
|
|
774
|
+
const [first, ...rest] = describeLegacyBaseUrl2(legacy.baseUrl, {
|
|
775
|
+
providers: settings.provider,
|
|
776
|
+
workspace: config.workspace
|
|
777
|
+
});
|
|
778
|
+
console.log(` ${mark} ${first}${suffix}`);
|
|
779
|
+
for (const line of rest) console.log(` ${line}`);
|
|
780
|
+
}
|
|
781
|
+
for (const entry of providerGate.partition.pending) {
|
|
782
|
+
const { command, note } = providerTrustCommand(config.workspace, entry.id);
|
|
783
|
+
console.log(` Approve ${renderUntrustedText(entry.id, 64)}:`);
|
|
784
|
+
console.log(` ${command}${note}`);
|
|
785
|
+
}
|
|
786
|
+
if (legacy?.state === "pending") {
|
|
787
|
+
console.log(
|
|
788
|
+
approvalHint(" Approve the legacy baseUrl:", config.workspace, "provider .bookrc.json")
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
if (anyPending) {
|
|
792
|
+
console.log(
|
|
793
|
+
approvalHint(" Approve all pending:", config.workspace, "provider --all-pending") + " (add --reject to refuse)"
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
console.log();
|
|
797
|
+
}
|
|
798
|
+
if (providerGate.droppedOverrides.length > 0) {
|
|
799
|
+
console.log("Workspace overrides of your providers (dropped):");
|
|
800
|
+
for (const dropped of providerGate.droppedOverrides) {
|
|
801
|
+
console.log(` [!] ${describeDroppedOverride(dropped)}`);
|
|
802
|
+
}
|
|
803
|
+
console.log();
|
|
804
|
+
}
|
|
805
|
+
if (providerGate.withheldSelections.length > 0) {
|
|
806
|
+
console.log("Withheld model selections:");
|
|
807
|
+
for (const selection of providerGate.withheldSelections) {
|
|
808
|
+
console.log(` [!] ${describeWithheldSelection(selection)}`);
|
|
809
|
+
}
|
|
810
|
+
console.log();
|
|
811
|
+
}
|
|
812
|
+
if (providerGate.unaffectedSelections.length > 0) {
|
|
813
|
+
console.log("Your model selections that share a prefix with a withheld provider:");
|
|
814
|
+
for (const selection of providerGate.unaffectedSelections) {
|
|
815
|
+
console.log(` [x] ${describeUnaffectedSelection(selection)}`);
|
|
816
|
+
}
|
|
817
|
+
console.log();
|
|
818
|
+
}
|
|
819
|
+
}
|
|
635
820
|
const hooks = settings.hooks;
|
|
636
821
|
const hookLists = Object.entries(hooks).filter(
|
|
637
822
|
(pair) => Array.isArray(pair[1])
|
|
@@ -643,7 +828,7 @@ async function runDoctorCommand(workspace, options = {}) {
|
|
|
643
828
|
if (entries.length > 0) console.log(" " + event + ": " + entries.length);
|
|
644
829
|
}
|
|
645
830
|
if (hookTotal === 0) console.log(" (none)");
|
|
646
|
-
const { collectDeclaredHooks: collectDeclaredHooks2, describeDeclaredHook: describeDeclaredHook2, partitionProjectHooks: partitionProjectHooks2 } = await import("./hook-approvals-
|
|
831
|
+
const { collectDeclaredHooks: collectDeclaredHooks2, describeDeclaredHook: describeDeclaredHook2, partitionProjectHooks: partitionProjectHooks2 } = await import("./hook-approvals-DPQD5H65.js");
|
|
647
832
|
const declaredProjectHooks = collectDeclaredHooks2(projectSettings);
|
|
648
833
|
if (declaredProjectHooks.length > 0) {
|
|
649
834
|
const hookPartition = partitionProjectHooks2(
|
|
@@ -671,8 +856,8 @@ async function runDoctorCommand(workspace, options = {}) {
|
|
|
671
856
|
}
|
|
672
857
|
}
|
|
673
858
|
console.log();
|
|
674
|
-
const { formatMcpServerCommand: formatMcpServerCommand2, resolveMcpServerList: resolveMcpServerList2 } = await import("./mcp-config-
|
|
675
|
-
const { evaluateMcpServerApproval: evaluateMcpServerApproval2 } = await import("./mcp-approvals-
|
|
859
|
+
const { formatMcpServerCommand: formatMcpServerCommand2, resolveMcpServerList: resolveMcpServerList2 } = await import("./mcp-config-GXSODNCM.js");
|
|
860
|
+
const { evaluateMcpServerApproval: evaluateMcpServerApproval2 } = await import("./mcp-approvals-27LJYADQ.js");
|
|
676
861
|
const mcpServers = resolveMcpServerList2(config.workspace);
|
|
677
862
|
console.log("MCP Servers:");
|
|
678
863
|
for (const server of mcpServers) {
|
|
@@ -681,8 +866,8 @@ async function runDoctorCommand(workspace, options = {}) {
|
|
|
681
866
|
}
|
|
682
867
|
if (mcpServers.length === 0) console.log(" (none)");
|
|
683
868
|
console.log();
|
|
684
|
-
const { createSandbox, sandboxPolicySummary } = await import("./sandbox-
|
|
685
|
-
const { hasAdjudicationPolicy } = await import("./permissions-
|
|
869
|
+
const { createSandbox, sandboxPolicySummary } = await import("./sandbox-N2RY72GN.js");
|
|
870
|
+
const { hasAdjudicationPolicy } = await import("./permissions-7Y35X6HZ.js");
|
|
686
871
|
const sandbox = createSandbox(settings.sandbox);
|
|
687
872
|
const policy = sandboxPolicySummary(settings.sandbox, {
|
|
688
873
|
sandboxActive: sandbox !== null,
|
|
@@ -695,12 +880,49 @@ async function runDoctorCommand(workspace, options = {}) {
|
|
|
695
880
|
console.log(" Excluded commands: " + settings.sandbox.excludedCommands.length);
|
|
696
881
|
console.log(" Unsandboxed commands: " + policy.unsandboxedCommands);
|
|
697
882
|
console.log(" Auto-allow Bash: " + policy.autoAllowBash);
|
|
883
|
+
console.log(" Git directories: " + policy.gitDirectories);
|
|
884
|
+
if (!options.noSettings) {
|
|
885
|
+
const {
|
|
886
|
+
formatIgnoredWorkspaceSandboxKey,
|
|
887
|
+
ignoredWorkspaceSandboxKeys,
|
|
888
|
+
loadSettingsFile: loadSettingsFile3,
|
|
889
|
+
workspaceLayerThatEnabledSandbox
|
|
890
|
+
} = await import("./settings-loader-KDBH45AQ.js");
|
|
891
|
+
const layers = (await settingsLayers(config.workspace)).map(([label, path]) => ({
|
|
892
|
+
label,
|
|
893
|
+
path,
|
|
894
|
+
document: label === "Project" && isUserSettingsFile2(path) ? null : loadSettingsFile3(path)
|
|
895
|
+
}));
|
|
896
|
+
const activation = workspaceLayerThatEnabledSandbox(
|
|
897
|
+
layers.map((layer) => ({
|
|
898
|
+
path: layer.path,
|
|
899
|
+
trust: WORKSPACE_LAYER_TRUST[layer.label] ?? "local",
|
|
900
|
+
document: layer.document
|
|
901
|
+
}))
|
|
902
|
+
);
|
|
903
|
+
for (const { label, path, document } of layers) {
|
|
904
|
+
if (label === "User") continue;
|
|
905
|
+
for (const entry of ignoredWorkspaceSandboxKeys(document ?? {})) {
|
|
906
|
+
console.log(
|
|
907
|
+
` Ignored from ${path}: ${formatIgnoredWorkspaceSandboxKey(entry)} \u2014 workspace settings may only tighten the sandbox; set it in ~/.book/settings.json or pass --settings`
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
if (path === activation) {
|
|
911
|
+
console.log(
|
|
912
|
+
` From ${path}: sandbox.enabled=true \u2014 a workspace file turned the sandbox on, so sandbox.autoAllowBashIfSandboxed=false with it; set autoAllowBashIfSandboxed in ~/.book/settings.json to have it back.`
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
698
917
|
console.log();
|
|
699
|
-
const { describeShell, resolveShell } = await import("./shell-selection-
|
|
918
|
+
const { describeShell, resolveShell } = await import("./shell-selection-Z5WORZU4.js");
|
|
700
919
|
const shell = config.shell ?? resolveShell({ requested: settings.shell });
|
|
701
920
|
console.log("Shell:");
|
|
702
921
|
console.log(" Bash tool runs: " + describeShell(shell));
|
|
703
922
|
console.log(" Selected by: " + shell.source);
|
|
923
|
+
if (process.platform === "win32" && (shell.kind === "bash" || shell.kind === "sh")) {
|
|
924
|
+
console.log(" msys: " + (shell.msys ? "yes" : "no"));
|
|
925
|
+
}
|
|
704
926
|
if (shell.warning) console.log(` [!] ${shell.warning}`);
|
|
705
927
|
console.log();
|
|
706
928
|
console.log("Managed agents:");
|
|
@@ -719,14 +941,14 @@ async function runDoctorCommand(workspace, options = {}) {
|
|
|
719
941
|
join8(config.workspace, ".claude", "agents"),
|
|
720
942
|
join8(config.workspace, ".agents", "agents")
|
|
721
943
|
]) {
|
|
722
|
-
if (
|
|
944
|
+
if (existsSync5(directory)) {
|
|
723
945
|
console.log(
|
|
724
946
|
` [!] Third-party definitions found at ${directory}; preview them with /agents import ${directory}.`
|
|
725
947
|
);
|
|
726
948
|
}
|
|
727
949
|
}
|
|
728
950
|
console.log();
|
|
729
|
-
const { createModelWindowStore } = await import("./model-window-store-
|
|
951
|
+
const { createModelWindowStore } = await import("./model-window-store-6VKXXTPJ.js");
|
|
730
952
|
const windowStore = createModelWindowStore();
|
|
731
953
|
const allLearned = windowStore.all();
|
|
732
954
|
const learnedModels = Object.keys(allLearned);
|
|
@@ -742,6 +964,41 @@ async function runDoctorCommand(workspace, options = {}) {
|
|
|
742
964
|
}
|
|
743
965
|
}
|
|
744
966
|
console.log();
|
|
967
|
+
const {
|
|
968
|
+
DEFAULT_MAX_INDEX_LINES,
|
|
969
|
+
MAX_INDEX_BYTES,
|
|
970
|
+
findLegacyMemoryBackup,
|
|
971
|
+
getMemoryHealth,
|
|
972
|
+
legacyBackupNotice,
|
|
973
|
+
loadMemoryContext
|
|
974
|
+
} = await import("./memory-store-I6Z54VUN.js");
|
|
975
|
+
const memoryContext = loadMemoryContext(config.workspace);
|
|
976
|
+
const memoryHealth = getMemoryHealth(memoryContext);
|
|
977
|
+
const memoryBackup = findLegacyMemoryBackup(memoryContext.dir);
|
|
978
|
+
const lastWrite = memoryHealth.lastWrite ? memoryHealth.lastWrite.toISOString() : "never";
|
|
979
|
+
console.log("Memory:");
|
|
980
|
+
console.log(" Memories: " + memoryHealth.fileCount);
|
|
981
|
+
if (memoryHealth.legacySupersededCount > 0) {
|
|
982
|
+
console.log(
|
|
983
|
+
` Legacy superseded: ${memoryHealth.legacySupersededCount} (retired by the old save tool; they load like any other)`
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
console.log(` Index lines: ${memoryHealth.indexLineCount} / ${DEFAULT_MAX_INDEX_LINES}`);
|
|
987
|
+
console.log(
|
|
988
|
+
` Index bytes: ${memoryHealth.indexByteCount.toLocaleString("en-US")} / ${MAX_INDEX_BYTES.toLocaleString("en-US")}`
|
|
989
|
+
);
|
|
990
|
+
if (memoryHealth.wasLineTruncated || memoryHealth.wasByteTruncated) {
|
|
991
|
+
const limits = [
|
|
992
|
+
memoryHealth.wasLineTruncated ? `${DEFAULT_MAX_INDEX_LINES} lines` : "",
|
|
993
|
+
memoryHealth.wasByteTruncated ? `${MAX_INDEX_BYTES.toLocaleString("en-US")} bytes` : ""
|
|
994
|
+
].filter(Boolean);
|
|
995
|
+
console.log(
|
|
996
|
+
` Index cut: MEMORY.md is over the ${limits.join(" and ")} limit; a session loads only part of it. Consolidate it.`
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
console.log(" Last write: " + lastWrite);
|
|
1000
|
+
if (memoryBackup) console.log(" " + legacyBackupNotice(memoryBackup));
|
|
1001
|
+
console.log();
|
|
745
1002
|
console.log("Environment:");
|
|
746
1003
|
for (const key of ["BOOK_API_KEY", "BOOK_BASE_URL", "BOOK_MODEL", "BOOK_WORKSPACE"]) {
|
|
747
1004
|
const val = process.env[key];
|
|
@@ -790,9 +1047,190 @@ async function runToolStatsCommand(options) {
|
|
|
790
1047
|
}
|
|
791
1048
|
}
|
|
792
1049
|
|
|
1050
|
+
// src/cli/provider-key-cmd.ts
|
|
1051
|
+
import { createInterface } from "readline";
|
|
1052
|
+
import { Writable } from "stream";
|
|
1053
|
+
var USAGE = "Usage: book config provider-key <provider-id> (the key is read from stdin)";
|
|
1054
|
+
var MAX_PIPED_BYTES = 64 * 1024;
|
|
1055
|
+
async function readPiped(input) {
|
|
1056
|
+
const chunks = [];
|
|
1057
|
+
let size = 0;
|
|
1058
|
+
for await (const chunk of input) {
|
|
1059
|
+
const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
1060
|
+
size += buffer.length;
|
|
1061
|
+
if (size > MAX_PIPED_BYTES) return void 0;
|
|
1062
|
+
chunks.push(buffer);
|
|
1063
|
+
}
|
|
1064
|
+
return Buffer.concat(chunks).toString("utf-8").replace(/\r?\n$/, "");
|
|
1065
|
+
}
|
|
1066
|
+
async function readTerminalLine(input, stderr, prompt, hidden) {
|
|
1067
|
+
stderr.write(prompt);
|
|
1068
|
+
const output = hidden ? new Writable({
|
|
1069
|
+
write(_chunk, _encoding, done) {
|
|
1070
|
+
done();
|
|
1071
|
+
}
|
|
1072
|
+
}) : stderr;
|
|
1073
|
+
const rl = createInterface({ input, output, terminal: true });
|
|
1074
|
+
try {
|
|
1075
|
+
return await new Promise((resolve3) => {
|
|
1076
|
+
rl.once("line", resolve3);
|
|
1077
|
+
rl.once("SIGINT", () => resolve3(void 0));
|
|
1078
|
+
rl.once("close", () => resolve3(void 0));
|
|
1079
|
+
});
|
|
1080
|
+
} finally {
|
|
1081
|
+
rl.close();
|
|
1082
|
+
if (hidden) stderr.write("\n");
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
async function askAfterFailure(input, stderr) {
|
|
1086
|
+
const answer = await readTerminalLine(
|
|
1087
|
+
input,
|
|
1088
|
+
stderr,
|
|
1089
|
+
"[s] save anyway [r] retry [e] enter a different key [c] cancel: ",
|
|
1090
|
+
false
|
|
1091
|
+
);
|
|
1092
|
+
switch (answer?.trim().toLowerCase()) {
|
|
1093
|
+
case "s":
|
|
1094
|
+
return "save";
|
|
1095
|
+
case "r":
|
|
1096
|
+
return "retry";
|
|
1097
|
+
case "e":
|
|
1098
|
+
return "edit";
|
|
1099
|
+
default:
|
|
1100
|
+
return "cancel";
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
async function runProviderKeyCommand(workspace, providerId, extra, options = {}) {
|
|
1104
|
+
const stdin = options.stdin ?? process.stdin;
|
|
1105
|
+
const stderr = options.stderr ?? process.stderr;
|
|
1106
|
+
if (extra !== void 0) {
|
|
1107
|
+
console.error(
|
|
1108
|
+
"provider-key reads the key from stdin, never from an argument; nothing was saved. A key typed on the command line is in your shell history: consider rotating it."
|
|
1109
|
+
);
|
|
1110
|
+
console.error(USAGE);
|
|
1111
|
+
return exit(1);
|
|
1112
|
+
}
|
|
1113
|
+
if (!providerId) {
|
|
1114
|
+
console.error(USAGE);
|
|
1115
|
+
return exit(1);
|
|
1116
|
+
}
|
|
1117
|
+
if (options.ignoresSettingsFlags) {
|
|
1118
|
+
console.error(
|
|
1119
|
+
"provider-key edits your user settings (<BOOK_HOME>/settings.json) whatever the settings layers in use; --settings and --no-settings do not apply to it."
|
|
1120
|
+
);
|
|
1121
|
+
return exit(1);
|
|
1122
|
+
}
|
|
1123
|
+
if (options.scope && options.scope !== "user") {
|
|
1124
|
+
console.error(
|
|
1125
|
+
`provider-key changes providers in your user settings only, not ${settingsScopeLabel(options.scope)} settings.`
|
|
1126
|
+
);
|
|
1127
|
+
return exit(1);
|
|
1128
|
+
}
|
|
1129
|
+
const target = providerKeyTarget(workspace, providerId);
|
|
1130
|
+
if (target.kind === "unreadable") {
|
|
1131
|
+
console.error(`Could not read user settings (${target.path}): ${target.error}`);
|
|
1132
|
+
return exit(1);
|
|
1133
|
+
}
|
|
1134
|
+
if (target.kind === "workspace-only") {
|
|
1135
|
+
console.error(
|
|
1136
|
+
`Provider "${providerId}" is declared by this repository (${target.declaredIn}), not your user settings (${target.path}). provider-key changes only providers you configured.`
|
|
1137
|
+
);
|
|
1138
|
+
return exit(1);
|
|
1139
|
+
}
|
|
1140
|
+
if (target.kind === "unknown") {
|
|
1141
|
+
console.error(
|
|
1142
|
+
// The id is not repeated: one that names no provider is as likely to be a key typed in
|
|
1143
|
+
// its place, and the command promises never to echo a key.
|
|
1144
|
+
`No provider with that id in user settings (${target.path}). ` + (target.configured.length > 0 ? `Configured: ${target.configured.join(", ")}.` : "No providers are configured there yet; add one with /providers in the TUI.")
|
|
1145
|
+
);
|
|
1146
|
+
return exit(1);
|
|
1147
|
+
}
|
|
1148
|
+
const interactive = stdin.isTTY === true;
|
|
1149
|
+
const readKey = async () => {
|
|
1150
|
+
const raw = interactive ? await readTerminalLine(stdin, stderr, `API key for ${providerId}: `, true) : await readPiped(stdin);
|
|
1151
|
+
if (raw === void 0) {
|
|
1152
|
+
console.error(
|
|
1153
|
+
interactive ? "Cancelled; nothing was saved." : `The input is longer than ${MAX_PIPED_BYTES} bytes, which no API key is; nothing was saved.`
|
|
1154
|
+
);
|
|
1155
|
+
return exit(1);
|
|
1156
|
+
}
|
|
1157
|
+
try {
|
|
1158
|
+
return validateApiKey(raw);
|
|
1159
|
+
} catch (caught) {
|
|
1160
|
+
console.error(caught instanceof Error ? caught.message : "Invalid API key.");
|
|
1161
|
+
return exit(1);
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
let apiKey = await readKey();
|
|
1165
|
+
let tested = false;
|
|
1166
|
+
while (options.check !== false) {
|
|
1167
|
+
const result = await checkProviderKey({
|
|
1168
|
+
provider: target.provider,
|
|
1169
|
+
apiKey,
|
|
1170
|
+
workspace,
|
|
1171
|
+
retry: DEFAULT_SETTINGS.retry,
|
|
1172
|
+
discover: options.discover
|
|
1173
|
+
});
|
|
1174
|
+
if (result.ok) {
|
|
1175
|
+
tested = true;
|
|
1176
|
+
break;
|
|
1177
|
+
}
|
|
1178
|
+
console.error(`The key check failed: ${result.error}`);
|
|
1179
|
+
if (options.force) {
|
|
1180
|
+
console.warn("Saving anyway (--force).");
|
|
1181
|
+
break;
|
|
1182
|
+
}
|
|
1183
|
+
if (!interactive) {
|
|
1184
|
+
console.error(
|
|
1185
|
+
"Nothing was saved. Pass --force to save anyway, or --no-check to skip the check."
|
|
1186
|
+
);
|
|
1187
|
+
return exit(1);
|
|
1188
|
+
}
|
|
1189
|
+
const choice = await askAfterFailure(stdin, stderr);
|
|
1190
|
+
if (choice === "save") break;
|
|
1191
|
+
if (choice === "cancel") {
|
|
1192
|
+
console.error("Cancelled; nothing was saved.");
|
|
1193
|
+
return exit(1);
|
|
1194
|
+
}
|
|
1195
|
+
if (choice === "edit") apiKey = await readKey();
|
|
1196
|
+
}
|
|
1197
|
+
const written = writeProviderKey(workspace, providerId, apiKey);
|
|
1198
|
+
if (!written.ok) {
|
|
1199
|
+
console.error(written.error);
|
|
1200
|
+
return exit(1);
|
|
1201
|
+
}
|
|
1202
|
+
console.log(
|
|
1203
|
+
`Updated the API key of provider "${providerId}" in user settings (${written.path})` + (tested ? "; the endpoint accepted it." : ".")
|
|
1204
|
+
);
|
|
1205
|
+
if (written.localCleared) {
|
|
1206
|
+
console.log(
|
|
1207
|
+
`Removed the connection keys of the same-named entry in ${written.localCleared}, which would otherwise look like they override it.`
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
if (written.localError) {
|
|
1211
|
+
console.warn(`\u26A0 Could not clean up the local entry: ${written.localError}`);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
793
1215
|
// src/cli/config-cmd.ts
|
|
794
1216
|
async function runConfigCommand(workspace, action, key, value, settingsOptions = {}) {
|
|
795
|
-
const resolveMergedSettings = () =>
|
|
1217
|
+
const resolveMergedSettings = () => applySettingsEnvOverrides(
|
|
1218
|
+
settingsOptions.noSettings ? structuredClone(DEFAULT_SETTINGS) : resolveSettings(workspace, settingsOptions.settingsOverridePath)
|
|
1219
|
+
);
|
|
1220
|
+
if (action === "provider-key") {
|
|
1221
|
+
await runProviderKeyCommand(workspace, key, value, {
|
|
1222
|
+
...settingsOptions.providerKey,
|
|
1223
|
+
scope: settingsOptions.scope,
|
|
1224
|
+
ignoresSettingsFlags: Boolean(
|
|
1225
|
+
settingsOptions.settingsOverridePath || settingsOptions.noSettings
|
|
1226
|
+
)
|
|
1227
|
+
});
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
if (settingsOptions.providerKey?.force || settingsOptions.providerKey?.check === false) {
|
|
1231
|
+
console.error("--force and --no-check apply to provider-key only.");
|
|
1232
|
+
exit(1);
|
|
1233
|
+
}
|
|
796
1234
|
const scopeRead = settingsOptions.scope ? readScopeDocument(settingsOptions.scope, workspace) : void 0;
|
|
797
1235
|
if (!action || action === "list") {
|
|
798
1236
|
if (scopeRead) {
|
|
@@ -808,6 +1246,8 @@ async function runConfigCommand(workspace, action, key, value, settingsOptions =
|
|
|
808
1246
|
}
|
|
809
1247
|
console.log("Resolved settings:");
|
|
810
1248
|
console.log(JSON.stringify(redactSettingsForDisplay(resolveMergedSettings()), null, 2));
|
|
1249
|
+
const listNote = startupAnimationEnvNote();
|
|
1250
|
+
if (listNote) console.log(`\u26A0 ${listNote}`);
|
|
811
1251
|
return;
|
|
812
1252
|
}
|
|
813
1253
|
if (action === "get") {
|
|
@@ -829,6 +1269,10 @@ async function runConfigCommand(workspace, action, key, value, settingsOptions =
|
|
|
829
1269
|
);
|
|
830
1270
|
} else {
|
|
831
1271
|
console.log(JSON.stringify(redactSettingValue(key, val), null, 2));
|
|
1272
|
+
if (!scopeRead && key === "ui.startupAnimation") {
|
|
1273
|
+
const note = startupAnimationEnvNote();
|
|
1274
|
+
if (note) console.log(`\u26A0 ${note}`);
|
|
1275
|
+
}
|
|
832
1276
|
}
|
|
833
1277
|
return;
|
|
834
1278
|
}
|
|
@@ -871,7 +1315,7 @@ async function runConfigCommand(workspace, action, key, value, settingsOptions =
|
|
|
871
1315
|
exit(1);
|
|
872
1316
|
}
|
|
873
1317
|
const scope = settingsOptions.scope ?? "user";
|
|
874
|
-
const layer = readScopeDocument(scope, workspace);
|
|
1318
|
+
const layer = readScopeDocument(scope, workspace, { forWrite: true });
|
|
875
1319
|
if (layer.status === "unreadable") {
|
|
876
1320
|
console.error(
|
|
877
1321
|
`Could not read ${settingsScopeLabel(scope)} settings (${layer.path}): ${layer.error ?? "unknown error"}`
|
|
@@ -893,7 +1337,7 @@ async function runConfigCommand(workspace, action, key, value, settingsOptions =
|
|
|
893
1337
|
return;
|
|
894
1338
|
}
|
|
895
1339
|
console.error(
|
|
896
|
-
"Unknown action: " + action + ". Use: get <key>, set <key> <value>, unset <key>, or
|
|
1340
|
+
"Unknown action: " + action + ". Use: get <key>, set <key> <value>, unset <key>, list, or provider-key <id>"
|
|
897
1341
|
);
|
|
898
1342
|
exit(1);
|
|
899
1343
|
}
|
|
@@ -911,21 +1355,25 @@ async function runTrustCommand(kind, target, options) {
|
|
|
911
1355
|
if (options.allPending && target !== void 0) {
|
|
912
1356
|
fail(`book trust ${kind}: pass either a target or --all-pending, not both`);
|
|
913
1357
|
}
|
|
914
|
-
|
|
1358
|
+
const listsProviders = kind === "provider" && !options.allPending && target === void 0;
|
|
1359
|
+
if (!options.allPending && target === void 0 && !listsProviders) {
|
|
915
1360
|
fail(
|
|
916
1361
|
`book trust ${kind}: name what to trust, or pass --all-pending. Run \`book doctor\` to list what is withheld.`
|
|
917
1362
|
);
|
|
918
1363
|
}
|
|
919
1364
|
let settings;
|
|
1365
|
+
let providerGate;
|
|
920
1366
|
try {
|
|
921
|
-
settings =
|
|
1367
|
+
({ settings, providerGate } = resolveSettingsWithProviderGate(workspace));
|
|
922
1368
|
} catch (error) {
|
|
923
1369
|
return fail(
|
|
924
1370
|
`book trust ${kind}: cannot read settings for ${workspace}: ${error instanceof Error ? error.message : String(error)}`
|
|
925
1371
|
);
|
|
926
1372
|
}
|
|
927
|
-
const
|
|
1373
|
+
const projectPath = join2(workspace, ".book", "settings.json");
|
|
1374
|
+
const projectSettings = isUserSettingsFile(projectPath) ? null : loadSettingsFile(projectPath);
|
|
928
1375
|
const decisions = [];
|
|
1376
|
+
const directoryDecisions = [];
|
|
929
1377
|
if (kind === "command") {
|
|
930
1378
|
const partition = partitionProjectCommands(discoverCommands(workspace), settings);
|
|
931
1379
|
const push = (command) => {
|
|
@@ -988,6 +1436,120 @@ ${known}` : "\nNothing is awaiting a decision.")
|
|
|
988
1436
|
}
|
|
989
1437
|
decisions.push({ key: target, label: describeDeclaredHook(hook).headline });
|
|
990
1438
|
}
|
|
1439
|
+
} else if (kind === "dir") {
|
|
1440
|
+
const declared = collectDeclaredDirectories(
|
|
1441
|
+
workspace,
|
|
1442
|
+
projectSettings?.additionalDirectories ?? []
|
|
1443
|
+
);
|
|
1444
|
+
const partition = partitionProjectDirectories(declared, settings.projectDirectories);
|
|
1445
|
+
const targetRealPath = realPathOfDirectory(workspace, target ?? "");
|
|
1446
|
+
const entry = declared.find(
|
|
1447
|
+
(candidate) => candidate.realPath === targetRealPath || candidate.declared === target
|
|
1448
|
+
);
|
|
1449
|
+
if (options.allPending) {
|
|
1450
|
+
if (partition.pending.length === 0) {
|
|
1451
|
+
console.log(`No project-declared directories are awaiting a decision in ${workspace}.`);
|
|
1452
|
+
return;
|
|
1453
|
+
}
|
|
1454
|
+
for (const pending of partition.pending) {
|
|
1455
|
+
console.log(`${pending.declared} is really:`);
|
|
1456
|
+
console.log(` ${pending.realPath}`);
|
|
1457
|
+
directoryDecisions.push(pending);
|
|
1458
|
+
}
|
|
1459
|
+
} else if (!entry) {
|
|
1460
|
+
const known = partition.pending.map((pending) => ` ${pending.realPath}`).join("\n");
|
|
1461
|
+
return fail(
|
|
1462
|
+
`book trust dir: ${workspace} declares no project additionalDirectories entry at "${target}".` + (known ? `
|
|
1463
|
+
Awaiting a decision:
|
|
1464
|
+
${known}` : "\nNothing is awaiting a decision.")
|
|
1465
|
+
);
|
|
1466
|
+
} else {
|
|
1467
|
+
if (!options.reject) {
|
|
1468
|
+
console.log(`${entry.declared} is really:`);
|
|
1469
|
+
console.log(` ${entry.realPath}`);
|
|
1470
|
+
}
|
|
1471
|
+
directoryDecisions.push(entry);
|
|
1472
|
+
}
|
|
1473
|
+
} else if (kind === "provider") {
|
|
1474
|
+
const unaffected = withEnvironmentSelections(
|
|
1475
|
+
providerGate.unaffectedSelections,
|
|
1476
|
+
environmentModelSelections(void 0),
|
|
1477
|
+
new Set(providerGate.withheld)
|
|
1478
|
+
);
|
|
1479
|
+
const candidates = ["approved", "pending", "rejected"].flatMap(
|
|
1480
|
+
(state) => providerGate.partition[state].map((entry) => ({
|
|
1481
|
+
id: entry.id,
|
|
1482
|
+
fingerprint: entry.fingerprint,
|
|
1483
|
+
state,
|
|
1484
|
+
lines: describeWorkspaceProvider(
|
|
1485
|
+
entry,
|
|
1486
|
+
workspace,
|
|
1487
|
+
unaffected.filter((selection) => selection.providerId === entry.id)
|
|
1488
|
+
)
|
|
1489
|
+
}))
|
|
1490
|
+
);
|
|
1491
|
+
const legacyBaseUrl = readLegacyBaseUrl(workspace);
|
|
1492
|
+
if (legacyBaseUrl !== void 0) {
|
|
1493
|
+
candidates.push({
|
|
1494
|
+
id: LEGACY_BASE_URL_ID,
|
|
1495
|
+
fingerprint: legacyBaseUrlFingerprint(legacyBaseUrl),
|
|
1496
|
+
state: evaluateLegacyBaseUrl(settings.workspaceProviders, legacyBaseUrl),
|
|
1497
|
+
lines: describeLegacyBaseUrl(legacyBaseUrl, { providers: settings.provider, workspace })
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
const pending = candidates.filter((candidate) => candidate.state === "pending");
|
|
1501
|
+
const printCandidate = (candidate, prefix) => {
|
|
1502
|
+
const [first, ...rest] = candidate.lines;
|
|
1503
|
+
console.log(`${prefix}${first}`);
|
|
1504
|
+
for (const line of rest) console.log(` ${line}`);
|
|
1505
|
+
};
|
|
1506
|
+
const push = (candidate) => {
|
|
1507
|
+
if (!options.reject) printCandidate(candidate, " ");
|
|
1508
|
+
decisions.push({
|
|
1509
|
+
key: candidate.id,
|
|
1510
|
+
label: candidate.id,
|
|
1511
|
+
fingerprint: candidate.fingerprint
|
|
1512
|
+
});
|
|
1513
|
+
};
|
|
1514
|
+
const reserved = providerGate.partition.reserved.map(
|
|
1515
|
+
(entry) => ` [!] ${describeReservedProvider(entry)}`
|
|
1516
|
+
);
|
|
1517
|
+
if (listsProviders) {
|
|
1518
|
+
for (const line of reserved) console.log(line);
|
|
1519
|
+
if (pending.length === 0) {
|
|
1520
|
+
console.log(`No workspace-declared providers are awaiting a decision in ${workspace}.`);
|
|
1521
|
+
return;
|
|
1522
|
+
}
|
|
1523
|
+
console.log(`Workspace-declared providers awaiting a decision in ${workspace}:`);
|
|
1524
|
+
for (const candidate of pending) printCandidate(candidate, " [!] ");
|
|
1525
|
+
for (const candidate of pending) {
|
|
1526
|
+
const { command, note } = providerTrustCommand(workspace, candidate.id);
|
|
1527
|
+
console.log(`Approve ${renderUntrustedText(candidate.id, 64)}:`);
|
|
1528
|
+
console.log(` ${command}${note}`);
|
|
1529
|
+
}
|
|
1530
|
+
const all = trustCommandText(workspace, "provider --all-pending");
|
|
1531
|
+
console.log(`Approve all: ${all.command}${all.note} (add --reject to refuse)`);
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
if (options.allPending) {
|
|
1535
|
+
if (pending.length === 0) {
|
|
1536
|
+
console.log(`No workspace-declared providers are awaiting a decision in ${workspace}.`);
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
for (const candidate of pending) push(candidate);
|
|
1540
|
+
} else {
|
|
1541
|
+
const candidate = candidates.find((known) => known.id === target);
|
|
1542
|
+
if (!candidate) {
|
|
1543
|
+
const known = candidates.map((entry) => ` ${renderUntrustedText(entry.id, 64)} (${entry.state})`).join("\n");
|
|
1544
|
+
return fail(
|
|
1545
|
+
`book trust provider: ${workspace} declares no provider "${target}" that can be decided.` + (known ? `
|
|
1546
|
+
Declared by the workspace:
|
|
1547
|
+
${known}` : "\nNothing is declared.") + (reserved.length > 0 ? `
|
|
1548
|
+
${reserved.join("\n")}` : "")
|
|
1549
|
+
);
|
|
1550
|
+
}
|
|
1551
|
+
push(candidate);
|
|
1552
|
+
}
|
|
991
1553
|
} else {
|
|
992
1554
|
const declared = projectSettings?.permissions?.allow ?? [];
|
|
993
1555
|
const partition = partitionProjectAllowRules(declared, settings.permissions.projectAllowRules);
|
|
@@ -1010,8 +1572,13 @@ ${known}` : "\nNothing is awaiting a decision.")
|
|
|
1010
1572
|
}
|
|
1011
1573
|
}
|
|
1012
1574
|
const failures = [];
|
|
1575
|
+
for (const directory of directoryDecisions) {
|
|
1576
|
+
const result = persistProjectDirectoryChoice(workspace, directory.realPath, choice);
|
|
1577
|
+
if (result.ok) console.log(`${verb} ${directory.realPath}`);
|
|
1578
|
+
else failures.push(`${directory.realPath}: ${result.error ?? "unknown error"}`);
|
|
1579
|
+
}
|
|
1013
1580
|
for (const decision of decisions) {
|
|
1014
|
-
const result = kind === "command" ? persistProjectCommandChoice(workspace, decision.key, decision.fingerprint, choice) : kind === "hook" ? persistProjectHookChoice(workspace, decision.key, choice) : persistProjectAllowRuleChoice(workspace, decision.key, choice);
|
|
1581
|
+
const result = kind === "command" ? persistProjectCommandChoice(workspace, decision.key, decision.fingerprint, choice) : kind === "provider" ? persistWorkspaceProviderChoice(workspace, decision.key, decision.fingerprint, choice) : kind === "hook" ? persistProjectHookChoice(workspace, decision.key, choice) : persistProjectAllowRuleChoice(workspace, decision.key, choice);
|
|
1015
1582
|
if (result.ok) console.log(`${verb} ${decision.label}`);
|
|
1016
1583
|
else failures.push(`${decision.label}: ${result.error ?? "unknown error"}`);
|
|
1017
1584
|
}
|
|
@@ -1023,10 +1590,6 @@ ${failures.map((f) => ` ${f}`).join("\n")}`
|
|
|
1023
1590
|
}
|
|
1024
1591
|
}
|
|
1025
1592
|
|
|
1026
|
-
// src/cli/run.ts
|
|
1027
|
-
import { render } from "ink";
|
|
1028
|
-
import { createElement } from "react";
|
|
1029
|
-
|
|
1030
1593
|
// src/mcp-host.ts
|
|
1031
1594
|
var log = createDebugLogger("mcp");
|
|
1032
1595
|
var MAX_EVENTS = 50;
|
|
@@ -1221,13 +1784,49 @@ var McpSessionHost = class {
|
|
|
1221
1784
|
}
|
|
1222
1785
|
};
|
|
1223
1786
|
|
|
1787
|
+
// src/cli/print-interrupt.ts
|
|
1788
|
+
function installPrintInterrupt(options = {}) {
|
|
1789
|
+
const controller = new AbortController();
|
|
1790
|
+
let interrupted;
|
|
1791
|
+
const onSignal = (signal) => {
|
|
1792
|
+
if (interrupted) {
|
|
1793
|
+
(options.onSecondSignal ?? ((second) => exit(second === "SIGINT" ? 130 : 143)))(signal);
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
interrupted = signal;
|
|
1797
|
+
(options.stderr ?? process.stderr).write(
|
|
1798
|
+
"\nCancelling: running SessionEnd hooks. Press Ctrl+C again to exit now.\n"
|
|
1799
|
+
);
|
|
1800
|
+
controller.abort(
|
|
1801
|
+
Object.assign(new Error(`Cancelled by ${signal}`), { bookTerminalReason: "user_cancelled" })
|
|
1802
|
+
);
|
|
1803
|
+
};
|
|
1804
|
+
const onSigint = () => onSignal("SIGINT");
|
|
1805
|
+
const onSigterm = () => onSignal("SIGTERM");
|
|
1806
|
+
process.on("SIGINT", onSigint);
|
|
1807
|
+
process.on("SIGTERM", onSigterm);
|
|
1808
|
+
return {
|
|
1809
|
+
signal: controller.signal,
|
|
1810
|
+
interruptedBy: () => interrupted,
|
|
1811
|
+
dispose: () => {
|
|
1812
|
+
process.off("SIGINT", onSigint);
|
|
1813
|
+
process.off("SIGTERM", onSigterm);
|
|
1814
|
+
}
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
function printExitCode(run) {
|
|
1818
|
+
if (run.interruptedBy) return run.interruptedBy === "SIGINT" ? 130 : 143;
|
|
1819
|
+
if (run.outcome) return run.outcome.status === "failed" ? 1 : 0;
|
|
1820
|
+
return run.aborted ? 0 : 1;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1224
1823
|
// src/cli/run.ts
|
|
1225
1824
|
import { join as join6 } from "path";
|
|
1226
1825
|
|
|
1227
1826
|
// src/rewind/snapshot-store.ts
|
|
1228
1827
|
import {
|
|
1229
1828
|
chmodSync,
|
|
1230
|
-
existsSync,
|
|
1829
|
+
existsSync as existsSync2,
|
|
1231
1830
|
lstatSync,
|
|
1232
1831
|
mkdirSync,
|
|
1233
1832
|
readFileSync,
|
|
@@ -1241,7 +1840,7 @@ import {
|
|
|
1241
1840
|
} from "fs";
|
|
1242
1841
|
import { lstat, mkdir, readFile, readdir, readlink, rename, rm, writeFile } from "fs/promises";
|
|
1243
1842
|
import { createHash, randomUUID } from "crypto";
|
|
1244
|
-
import { dirname, isAbsolute, join as join3, relative, resolve
|
|
1843
|
+
import { dirname, isAbsolute, join as join3, relative, resolve, sep } from "path";
|
|
1245
1844
|
import ignore from "ignore";
|
|
1246
1845
|
var REWIND_MAX_ENTRIES = 2e4;
|
|
1247
1846
|
var REWIND_MAX_FILE_BYTES = 16 * 1024 * 1024;
|
|
@@ -1274,7 +1873,7 @@ var DEFAULT_IGNORE_PATTERNS = [
|
|
|
1274
1873
|
function sha256(value) {
|
|
1275
1874
|
return createHash("sha256").update(value).digest("hex");
|
|
1276
1875
|
}
|
|
1277
|
-
function
|
|
1876
|
+
function pathExists2(path) {
|
|
1278
1877
|
try {
|
|
1279
1878
|
lstatSync(path);
|
|
1280
1879
|
return true;
|
|
@@ -1283,7 +1882,7 @@ function pathExists(path) {
|
|
|
1283
1882
|
}
|
|
1284
1883
|
}
|
|
1285
1884
|
function normalizeWorkspace(workspace) {
|
|
1286
|
-
const normalized =
|
|
1885
|
+
const normalized = resolve(workspace);
|
|
1287
1886
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
1288
1887
|
}
|
|
1289
1888
|
function rewindWorkspaceHash(workspace) {
|
|
@@ -1339,11 +1938,12 @@ function createRewindSnapshotStore(workspace, root) {
|
|
|
1339
1938
|
}
|
|
1340
1939
|
function readCustomIgnorePatterns(workspace) {
|
|
1341
1940
|
const path = join3(workspace, ".book", "rewindignore");
|
|
1342
|
-
if (!
|
|
1941
|
+
if (reachesUncPath(path) || !existsSync2(path)) return [];
|
|
1343
1942
|
return readFileSync(path, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
1344
1943
|
}
|
|
1345
1944
|
async function readCustomIgnorePatternsAsync(workspace) {
|
|
1346
1945
|
const path = join3(workspace, ".book", "rewindignore");
|
|
1946
|
+
if (reachesUncPath(path)) return [];
|
|
1347
1947
|
try {
|
|
1348
1948
|
return (await readFile(path, "utf-8")).split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
1349
1949
|
} catch (error) {
|
|
@@ -1364,33 +1964,39 @@ function expandNegatedParents(patterns) {
|
|
|
1364
1964
|
}
|
|
1365
1965
|
return expanded;
|
|
1366
1966
|
}
|
|
1367
|
-
function readOptionalText(path) {
|
|
1967
|
+
function readOptionalText(path, reachesUnc) {
|
|
1968
|
+
if (reachesUnc(path)) return void 0;
|
|
1368
1969
|
try {
|
|
1369
1970
|
return readFileSync(path, "utf-8").trim();
|
|
1370
1971
|
} catch {
|
|
1371
1972
|
return void 0;
|
|
1372
1973
|
}
|
|
1373
1974
|
}
|
|
1374
|
-
function resolveGitDirectory(workspace) {
|
|
1975
|
+
function resolveGitDirectory(workspace, reachesUnc) {
|
|
1375
1976
|
const dotGit = join3(workspace, ".git");
|
|
1977
|
+
if (reachesUnc(dotGit)) return void 0;
|
|
1376
1978
|
try {
|
|
1377
1979
|
if (lstatSync(dotGit).isDirectory()) return dotGit;
|
|
1378
1980
|
} catch {
|
|
1379
1981
|
return void 0;
|
|
1380
1982
|
}
|
|
1381
|
-
const match = /^gitdir:\s*(.+)$/i.exec(readOptionalText(dotGit) ?? "");
|
|
1382
|
-
|
|
1983
|
+
const match = /^gitdir:\s*(.+)$/i.exec(readOptionalText(dotGit, reachesUnc) ?? "");
|
|
1984
|
+
if (!match) return void 0;
|
|
1985
|
+
const gitDirectory = resolve(dirname(dotGit), match[1]);
|
|
1986
|
+
return reachesUnc(gitDirectory) ? void 0 : gitDirectory;
|
|
1383
1987
|
}
|
|
1384
|
-
function resolveCommonGitDirectory(gitDirectory) {
|
|
1385
|
-
const commonDirectory = readOptionalText(join3(gitDirectory, "commondir"));
|
|
1386
|
-
|
|
1988
|
+
function resolveCommonGitDirectory(gitDirectory, reachesUnc) {
|
|
1989
|
+
const commonDirectory = readOptionalText(join3(gitDirectory, "commondir"), reachesUnc);
|
|
1990
|
+
if (!commonDirectory) return gitDirectory;
|
|
1991
|
+
const common = resolve(gitDirectory, commonDirectory);
|
|
1992
|
+
return reachesUnc(common) ? gitDirectory : common;
|
|
1387
1993
|
}
|
|
1388
1994
|
function parseGitHash(value) {
|
|
1389
1995
|
const hash = value?.trim();
|
|
1390
1996
|
return hash && /^[0-9a-f]{40,64}$/i.test(hash) ? hash : void 0;
|
|
1391
1997
|
}
|
|
1392
|
-
function readPackedRef(directory, ref) {
|
|
1393
|
-
const packedRefs = readOptionalText(join3(directory, "packed-refs"));
|
|
1998
|
+
function readPackedRef(directory, ref, reachesUnc) {
|
|
1999
|
+
const packedRefs = readOptionalText(join3(directory, "packed-refs"), reachesUnc);
|
|
1394
2000
|
if (!packedRefs) return void 0;
|
|
1395
2001
|
for (const line of packedRefs.split(/\r?\n/)) {
|
|
1396
2002
|
if (!line || line.startsWith("#") || line.startsWith("^")) continue;
|
|
@@ -1401,12 +2007,12 @@ function readPackedRef(directory, ref) {
|
|
|
1401
2007
|
}
|
|
1402
2008
|
return void 0;
|
|
1403
2009
|
}
|
|
1404
|
-
function resolveGitRef(gitDirectory, commonDirectory, ref, depth = 0) {
|
|
2010
|
+
function resolveGitRef(gitDirectory, commonDirectory, ref, reachesUnc, depth = 0) {
|
|
1405
2011
|
if (depth > 4 || !ref.startsWith("refs/") || ref.includes("..") || ref.includes("\\") || ref.includes("\0")) {
|
|
1406
2012
|
return void 0;
|
|
1407
2013
|
}
|
|
1408
2014
|
for (const directory of /* @__PURE__ */ new Set([gitDirectory, commonDirectory])) {
|
|
1409
|
-
const value = readOptionalText(join3(directory, ...ref.split("/")));
|
|
2015
|
+
const value = readOptionalText(join3(directory, ...ref.split("/")), reachesUnc);
|
|
1410
2016
|
const hash = parseGitHash(value);
|
|
1411
2017
|
if (hash) return hash;
|
|
1412
2018
|
if (value?.startsWith("ref:")) {
|
|
@@ -1414,24 +2020,27 @@ function resolveGitRef(gitDirectory, commonDirectory, ref, depth = 0) {
|
|
|
1414
2020
|
gitDirectory,
|
|
1415
2021
|
commonDirectory,
|
|
1416
2022
|
value.slice("ref:".length).trim(),
|
|
2023
|
+
reachesUnc,
|
|
1417
2024
|
depth + 1
|
|
1418
2025
|
);
|
|
1419
2026
|
if (resolved) return resolved;
|
|
1420
2027
|
}
|
|
1421
2028
|
}
|
|
1422
|
-
return readPackedRef(gitDirectory, ref) ?? (commonDirectory === gitDirectory ? void 0 : readPackedRef(commonDirectory, ref));
|
|
2029
|
+
return readPackedRef(gitDirectory, ref, reachesUnc) ?? (commonDirectory === gitDirectory ? void 0 : readPackedRef(commonDirectory, ref, reachesUnc));
|
|
1423
2030
|
}
|
|
1424
2031
|
function gitHead(workspace) {
|
|
1425
|
-
const
|
|
2032
|
+
const reachesUnc = createUncGuard();
|
|
2033
|
+
const gitDirectory = resolveGitDirectory(workspace, reachesUnc);
|
|
1426
2034
|
if (!gitDirectory) return void 0;
|
|
1427
|
-
const head = readOptionalText(join3(gitDirectory, "HEAD"));
|
|
2035
|
+
const head = readOptionalText(join3(gitDirectory, "HEAD"), reachesUnc);
|
|
1428
2036
|
const directHash = parseGitHash(head);
|
|
1429
2037
|
if (directHash) return directHash;
|
|
1430
2038
|
if (!head?.startsWith("ref:")) return void 0;
|
|
1431
2039
|
return resolveGitRef(
|
|
1432
2040
|
gitDirectory,
|
|
1433
|
-
resolveCommonGitDirectory(gitDirectory),
|
|
1434
|
-
head.slice("ref:".length).trim()
|
|
2041
|
+
resolveCommonGitDirectory(gitDirectory, reachesUnc),
|
|
2042
|
+
head.slice("ref:".length).trim(),
|
|
2043
|
+
reachesUnc
|
|
1435
2044
|
);
|
|
1436
2045
|
}
|
|
1437
2046
|
async function gitHeadAsync(workspace) {
|
|
@@ -1449,7 +2058,7 @@ function assertSafeRelativePath(path) {
|
|
|
1449
2058
|
}
|
|
1450
2059
|
function resolveEntryPath(workspace, path) {
|
|
1451
2060
|
assertSafeRelativePath(path);
|
|
1452
|
-
const target =
|
|
2061
|
+
const target = resolve(workspace, ...path.split("/"));
|
|
1453
2062
|
const rel = relative(workspace, target);
|
|
1454
2063
|
if (rel.startsWith(`..${sep}`) || rel === ".." || isAbsolute(rel)) {
|
|
1455
2064
|
throw new Error(`Rewind path escapes the workspace: ${path}`);
|
|
@@ -1539,7 +2148,7 @@ function writeAtomic(path, contents) {
|
|
|
1539
2148
|
writeFileSync(temporary, contents);
|
|
1540
2149
|
renameSync(temporary, path);
|
|
1541
2150
|
} finally {
|
|
1542
|
-
if (
|
|
2151
|
+
if (pathExists2(temporary)) unlinkSync(temporary);
|
|
1543
2152
|
}
|
|
1544
2153
|
}
|
|
1545
2154
|
async function writeAtomicAsync(path, contents) {
|
|
@@ -1552,18 +2161,10 @@ async function writeAtomicAsync(path, contents) {
|
|
|
1552
2161
|
await rm(temporary, { force: true });
|
|
1553
2162
|
}
|
|
1554
2163
|
}
|
|
1555
|
-
async function pathExistsAsync(path) {
|
|
1556
|
-
try {
|
|
1557
|
-
await lstat(path);
|
|
1558
|
-
return true;
|
|
1559
|
-
} catch {
|
|
1560
|
-
return false;
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
2164
|
var RewindSnapshotStore = class {
|
|
1564
2165
|
constructor(workspace, root = persistentRewindRoot(workspace), limits = {}) {
|
|
1565
2166
|
this.root = root;
|
|
1566
|
-
this.workspace =
|
|
2167
|
+
this.workspace = resolve(workspace);
|
|
1567
2168
|
this.limits = {
|
|
1568
2169
|
maxEntries: limits.maxEntries ?? REWIND_MAX_ENTRIES,
|
|
1569
2170
|
maxFileBytes: limits.maxFileBytes ?? REWIND_MAX_FILE_BYTES,
|
|
@@ -1626,7 +2227,7 @@ var RewindSnapshotStore = class {
|
|
|
1626
2227
|
}
|
|
1627
2228
|
const blobHash = sha256(contents);
|
|
1628
2229
|
const blobPath = join3(this.blobsRoot, blobHash);
|
|
1629
|
-
if (!
|
|
2230
|
+
if (!existsSync2(blobPath)) writeAtomic(blobPath, contents);
|
|
1630
2231
|
const snapshotEntry = {
|
|
1631
2232
|
path: entry.path.replaceAll("\\", "/"),
|
|
1632
2233
|
kind: entry.kind,
|
|
@@ -1713,7 +2314,7 @@ var RewindSnapshotStore = class {
|
|
|
1713
2314
|
}
|
|
1714
2315
|
const blobHash = sha256(contents);
|
|
1715
2316
|
const blobPath = join3(this.blobsRoot, blobHash);
|
|
1716
|
-
if (!await
|
|
2317
|
+
if (!await pathExists(blobPath)) await writeAtomicAsync(blobPath, contents);
|
|
1717
2318
|
const snapshotEntry = {
|
|
1718
2319
|
path: entry.path.replaceAll("\\", "/"),
|
|
1719
2320
|
kind: entry.kind,
|
|
@@ -1754,14 +2355,14 @@ var RewindSnapshotStore = class {
|
|
|
1754
2355
|
getManifest(id) {
|
|
1755
2356
|
if (!/^[a-zA-Z0-9-]+$/.test(id)) return void 0;
|
|
1756
2357
|
const path = join3(this.manifestsRoot, `${id}.json`);
|
|
1757
|
-
if (!
|
|
2358
|
+
if (!existsSync2(path)) return void 0;
|
|
1758
2359
|
try {
|
|
1759
2360
|
const stored = JSON.parse(readFileSync(path, "utf-8"));
|
|
1760
2361
|
let manifest = stored;
|
|
1761
2362
|
if (!Array.isArray(stored.entries) && stored.entrySetHash) {
|
|
1762
2363
|
if (!/^[a-f0-9]{64}$/.test(stored.entrySetHash)) return void 0;
|
|
1763
2364
|
const entriesPath = join3(this.entrySetsRoot, `${stored.entrySetHash}.json`);
|
|
1764
|
-
if (!
|
|
2365
|
+
if (!existsSync2(entriesPath)) return void 0;
|
|
1765
2366
|
const serializedEntries = readFileSync(entriesPath, "utf-8");
|
|
1766
2367
|
if (sha256(serializedEntries) !== stored.entrySetHash) return void 0;
|
|
1767
2368
|
manifest = {
|
|
@@ -1832,12 +2433,12 @@ var RewindSnapshotStore = class {
|
|
|
1832
2433
|
discardManifest(id) {
|
|
1833
2434
|
if (!/^[a-zA-Z0-9-]+$/.test(id)) return;
|
|
1834
2435
|
const path = join3(this.manifestsRoot, `${id}.json`);
|
|
1835
|
-
if (
|
|
2436
|
+
if (existsSync2(path)) unlinkSync(path);
|
|
1836
2437
|
}
|
|
1837
2438
|
writeManifest(manifest) {
|
|
1838
2439
|
const entriesHash = sha256(JSON.stringify(manifest.entries));
|
|
1839
2440
|
const entriesPath = join3(this.entrySetsRoot, `${entriesHash}.json`);
|
|
1840
|
-
if (!
|
|
2441
|
+
if (!existsSync2(entriesPath)) writeAtomic(entriesPath, JSON.stringify(manifest.entries));
|
|
1841
2442
|
const metadata = { ...manifest };
|
|
1842
2443
|
delete metadata.entries;
|
|
1843
2444
|
writeAtomic(
|
|
@@ -1848,7 +2449,7 @@ var RewindSnapshotStore = class {
|
|
|
1848
2449
|
async writeManifestAsync(manifest) {
|
|
1849
2450
|
const entriesHash = sha256(JSON.stringify(manifest.entries));
|
|
1850
2451
|
const entriesPath = join3(this.entrySetsRoot, `${entriesHash}.json`);
|
|
1851
|
-
if (!await
|
|
2452
|
+
if (!await pathExists(entriesPath)) {
|
|
1852
2453
|
await writeAtomicAsync(entriesPath, JSON.stringify(manifest.entries));
|
|
1853
2454
|
}
|
|
1854
2455
|
const metadata = { ...manifest };
|
|
@@ -1920,7 +2521,7 @@ var RewindSnapshotStore = class {
|
|
|
1920
2521
|
throw new Error(`Invalid blob hash for ${entry.path}.`);
|
|
1921
2522
|
}
|
|
1922
2523
|
const blobPath = join3(this.blobsRoot, entry.blobHash);
|
|
1923
|
-
if (!
|
|
2524
|
+
if (!existsSync2(blobPath)) throw new Error(`Missing rewind blob for ${entry.path}.`);
|
|
1924
2525
|
const contents = readFileSync(blobPath);
|
|
1925
2526
|
if (sha256(contents) !== entry.blobHash)
|
|
1926
2527
|
throw new Error(`Corrupt rewind blob for ${entry.path}.`);
|
|
@@ -1943,7 +2544,7 @@ var RewindSnapshotStore = class {
|
|
|
1943
2544
|
for (const entry of manifest.entries) {
|
|
1944
2545
|
const target = resolveEntryPath(this.workspace, entry.path);
|
|
1945
2546
|
mkdirSync(dirname(target), { recursive: true });
|
|
1946
|
-
if (
|
|
2547
|
+
if (pathExists2(target) && lstatSync(target).isDirectory()) {
|
|
1947
2548
|
throw new Error(`Cannot restore ${entry.path} because a directory exists at that path.`);
|
|
1948
2549
|
}
|
|
1949
2550
|
const contents = readFileSync(join3(this.blobsRoot, entry.blobHash));
|
|
@@ -1957,25 +2558,25 @@ var RewindSnapshotStore = class {
|
|
|
1957
2558
|
}
|
|
1958
2559
|
renameSync(temporary, target);
|
|
1959
2560
|
} finally {
|
|
1960
|
-
if (
|
|
2561
|
+
if (pathExists2(temporary)) unlinkSync(temporary);
|
|
1961
2562
|
}
|
|
1962
2563
|
}
|
|
1963
2564
|
}
|
|
1964
2565
|
};
|
|
1965
2566
|
|
|
1966
2567
|
// src/rewind/environment.ts
|
|
1967
|
-
import { existsSync as
|
|
2568
|
+
import { existsSync as existsSync3, mkdtempSync, rmSync as rmSync2 } from "fs";
|
|
1968
2569
|
import { tmpdir } from "os";
|
|
1969
|
-
import { join as join4, resolve as
|
|
2570
|
+
import { join as join4, resolve as resolve2, sep as sep2 } from "path";
|
|
1970
2571
|
function createEphemeralRewindEnvironment(workspace, temporaryRoot = tmpdir()) {
|
|
1971
|
-
const resolvedTemporaryRoot =
|
|
2572
|
+
const resolvedTemporaryRoot = resolve2(temporaryRoot);
|
|
1972
2573
|
const root = mkdtempSync(join4(resolvedTemporaryRoot, "book-rewind-"));
|
|
1973
2574
|
let disposed = false;
|
|
1974
2575
|
const dispose = () => {
|
|
1975
2576
|
if (disposed) return;
|
|
1976
2577
|
disposed = true;
|
|
1977
|
-
const resolvedRoot =
|
|
1978
|
-
if (resolvedRoot.startsWith(`${resolvedTemporaryRoot}${sep2}`) && resolvedRoot.split(sep2).at(-1)?.startsWith("book-rewind-") &&
|
|
2578
|
+
const resolvedRoot = resolve2(root);
|
|
2579
|
+
if (resolvedRoot.startsWith(`${resolvedTemporaryRoot}${sep2}`) && resolvedRoot.split(sep2).at(-1)?.startsWith("book-rewind-") && existsSync3(resolvedRoot)) {
|
|
1979
2580
|
rmSync2(resolvedRoot, { recursive: true, force: true });
|
|
1980
2581
|
}
|
|
1981
2582
|
process.off("exit", dispose);
|
|
@@ -1989,34 +2590,63 @@ function createEphemeralRewindEnvironment(workspace, temporaryRoot = tmpdir()) {
|
|
|
1989
2590
|
};
|
|
1990
2591
|
}
|
|
1991
2592
|
|
|
1992
|
-
// src/cli/ink-
|
|
1993
|
-
import {
|
|
1994
|
-
import {
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2593
|
+
// src/cli/ink-frame-throttle.ts
|
|
2594
|
+
import { join as join5 } from "path";
|
|
2595
|
+
import { pathToFileURL } from "url";
|
|
2596
|
+
function createFrameThrottle(draw, intervalMs, now = () => performance.now()) {
|
|
2597
|
+
let lastDrawAt = Number.NEGATIVE_INFINITY;
|
|
2598
|
+
let timer = null;
|
|
2599
|
+
const fire = () => {
|
|
2600
|
+
timer = null;
|
|
2601
|
+
lastDrawAt = now();
|
|
2602
|
+
draw();
|
|
2603
|
+
};
|
|
2604
|
+
const throttled = (() => {
|
|
2605
|
+
if (timer !== null) return;
|
|
2606
|
+
const wait = lastDrawAt + intervalMs - now();
|
|
2607
|
+
if (wait <= 0) fire();
|
|
2608
|
+
else timer = setTimeout(fire, wait);
|
|
2609
|
+
});
|
|
2610
|
+
throttled.flush = () => {
|
|
2611
|
+
if (timer === null) return;
|
|
2612
|
+
clearTimeout(timer);
|
|
2613
|
+
fire();
|
|
2614
|
+
};
|
|
2615
|
+
throttled.cancel = () => {
|
|
2616
|
+
if (timer === null) return;
|
|
2617
|
+
clearTimeout(timer);
|
|
2618
|
+
timer = null;
|
|
2619
|
+
};
|
|
2620
|
+
return throttled;
|
|
2621
|
+
}
|
|
2622
|
+
function isThrottledInk(ink) {
|
|
2623
|
+
if (typeof ink !== "object" || ink === null) return false;
|
|
2624
|
+
const candidate = ink;
|
|
2625
|
+
return typeof candidate.onRender === "function" && typeof candidate.renderThrottleMs === "number" && candidate.renderThrottleMs > 0 && typeof candidate.throttledOnRender?.flush === "function" && typeof candidate.throttledOnRender.cancel === "function" && typeof candidate.rootNode?.onRender === "function";
|
|
2626
|
+
}
|
|
2627
|
+
async function installInkFrameThrottle(stdout) {
|
|
1998
2628
|
try {
|
|
1999
|
-
const
|
|
2000
|
-
const
|
|
2001
|
-
|
|
2629
|
+
const module = await import(pathToFileURL(join5(inkBuildDir(), "instances.js")).href);
|
|
2630
|
+
const ink = module.default.get(stdout);
|
|
2631
|
+
if (!isThrottledInk(ink)) return false;
|
|
2632
|
+
ink.throttledOnRender.flush();
|
|
2633
|
+
const throttle = createFrameThrottle(() => ink.onRender(), ink.renderThrottleMs);
|
|
2634
|
+
ink.throttledOnRender = throttle;
|
|
2635
|
+
ink.rootNode.onRender = () => {
|
|
2636
|
+
ink.hasPendingThrottledRender = true;
|
|
2637
|
+
throttle();
|
|
2638
|
+
};
|
|
2639
|
+
return true;
|
|
2002
2640
|
} catch {
|
|
2003
2641
|
return false;
|
|
2004
2642
|
}
|
|
2005
2643
|
}
|
|
2006
2644
|
|
|
2007
|
-
// src/cli/tui-renderer-mode.ts
|
|
2008
|
-
function resolveTuiRendererMode(value = process.env.BOOK_TUI_RENDERER, context = {}) {
|
|
2009
|
-
if (context.isTTY === false || context.screenReader) return "safe";
|
|
2010
|
-
if (context.incrementalRendererPatched === false) return "safe";
|
|
2011
|
-
if (value === "incremental") return "incremental";
|
|
2012
|
-
if (value === "experimental-scroll") return "experimental-scroll";
|
|
2013
|
-
if (value === void 0) return context.platform === "win32" ? "safe" : "incremental";
|
|
2014
|
-
return "safe";
|
|
2015
|
-
}
|
|
2016
|
-
|
|
2017
2645
|
// src/cli/run.ts
|
|
2018
2646
|
import { spawn } from "child_process";
|
|
2019
|
-
|
|
2647
|
+
function sessionRoot() {
|
|
2648
|
+
return join6(resolveBookHome(), "sessions");
|
|
2649
|
+
}
|
|
2020
2650
|
var ENTER_ALT_SCREEN = "\x1B[?1049h";
|
|
2021
2651
|
var EXIT_ALT_SCREEN = "\x1B[?1049l";
|
|
2022
2652
|
var DISABLE_MOUSE_REPORTING = "\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?1015l";
|
|
@@ -2060,7 +2690,8 @@ function writeTerminalControl(stdout, sequence) {
|
|
|
2060
2690
|
{
|
|
2061
2691
|
detached: true,
|
|
2062
2692
|
stdio: "ignore",
|
|
2063
|
-
windowsHide: true
|
|
2693
|
+
windowsHide: true,
|
|
2694
|
+
env: buildChildEnv()
|
|
2064
2695
|
}
|
|
2065
2696
|
);
|
|
2066
2697
|
bridge.on("error", () => {
|
|
@@ -2072,6 +2703,12 @@ function writeTerminalControl(stdout, sequence) {
|
|
|
2072
2703
|
function collectSnapshotReferences(store, cwd) {
|
|
2073
2704
|
return store.listSnapshotReferences(cwd);
|
|
2074
2705
|
}
|
|
2706
|
+
function printPipeErrorHandler(stream, printFormat, readerGone) {
|
|
2707
|
+
return (error) => {
|
|
2708
|
+
if (!isReaderGoneError(error)) throw error;
|
|
2709
|
+
if (stream === "stdout" && printFormat === "stream-json") readerGone.abort();
|
|
2710
|
+
};
|
|
2711
|
+
}
|
|
2075
2712
|
function enterInteractiveScreen(stdout = process.stdout, bridgeWslTerminal = shouldBridgeWslTerminal()) {
|
|
2076
2713
|
if (!stdout.isTTY && !bridgeWslTerminal) return () => {
|
|
2077
2714
|
};
|
|
@@ -2093,9 +2730,17 @@ function enterInteractiveScreen(stdout = process.stdout, bridgeWslTerminal = sho
|
|
|
2093
2730
|
return restore;
|
|
2094
2731
|
}
|
|
2095
2732
|
async function runMainAction(options) {
|
|
2733
|
+
let printInterrupt;
|
|
2734
|
+
let printSignal;
|
|
2735
|
+
let launched;
|
|
2736
|
+
const createdSessions = /* @__PURE__ */ new Set();
|
|
2096
2737
|
try {
|
|
2097
2738
|
const requestedWorkspace = options.workspace;
|
|
2098
2739
|
const effortOverride = options.effort ? parseEffortLevel(String(options.effort), "--effort") : void 0;
|
|
2740
|
+
const explicitDriver = options.sessionDriver === void 0 ? void 0 : parseSessionDriver(String(options.sessionDriver));
|
|
2741
|
+
if (options.sessionDriver !== void 0 && !explicitDriver) {
|
|
2742
|
+
throw new Error("--session-driver must be human or agent");
|
|
2743
|
+
}
|
|
2099
2744
|
const config = loadConfig(requestedWorkspace, {
|
|
2100
2745
|
settingsOverridePath: options.settings,
|
|
2101
2746
|
noSettings: options.settings === false,
|
|
@@ -2105,6 +2750,11 @@ async function runMainAction(options) {
|
|
|
2105
2750
|
allowMissingApiKey: options.print === void 0 && !options.scrollback
|
|
2106
2751
|
});
|
|
2107
2752
|
if (config.modelProviderWarning) console.warn("\u26A0 " + config.modelProviderWarning);
|
|
2753
|
+
const providerNotices = options.print === void 0 && config.workspaceProviderGate ? collectWithheldProviderNotices(config.workspaceProviderGate, config.workspace) : [];
|
|
2754
|
+
const instructionNotices = collectSkippedProjectFileNotices(config.workspace);
|
|
2755
|
+
if (options.scrollback) {
|
|
2756
|
+
for (const notice of [...providerNotices, ...instructionNotices]) console.warn(notice);
|
|
2757
|
+
}
|
|
2108
2758
|
const interactiveMaxTurns = parseNumericFlag(options.maxTurns, "--max-turns", {
|
|
2109
2759
|
integer: true
|
|
2110
2760
|
});
|
|
@@ -2123,14 +2773,22 @@ async function runMainAction(options) {
|
|
|
2123
2773
|
config.provider = raw;
|
|
2124
2774
|
}
|
|
2125
2775
|
}
|
|
2776
|
+
const sessionDriver = resolveSessionDriver({ explicit: explicitDriver, env: process.env });
|
|
2777
|
+
config.sessionDriver = sessionDriver.driver;
|
|
2778
|
+
createDebugLogger("memory").info("session driver", { ...sessionDriver });
|
|
2126
2779
|
freezeAgentConfig(config);
|
|
2127
2780
|
cleanupDebugLogs(DEFAULT_LOCAL_DATA_RETENTION_DAYS, getDebugLogPath());
|
|
2781
|
+
cleanupRunStatus(DEFAULT_LOCAL_DATA_RETENTION_DAYS);
|
|
2128
2782
|
if (options.print !== void 0) {
|
|
2129
|
-
for (const notice of
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2783
|
+
for (const notice of [
|
|
2784
|
+
...collectWithheldProjectNotices({
|
|
2785
|
+
workspace: config.workspace,
|
|
2786
|
+
settings: config.settings,
|
|
2787
|
+
settingsEnabled: options.settings !== false,
|
|
2788
|
+
providerGate: config.workspaceProviderGate
|
|
2789
|
+
}),
|
|
2790
|
+
...instructionNotices
|
|
2791
|
+
]) {
|
|
2134
2792
|
console.warn(notice);
|
|
2135
2793
|
}
|
|
2136
2794
|
const declaredMcpServers = resolveMcpServerList(config.workspace);
|
|
@@ -2150,16 +2808,29 @@ async function runMainAction(options) {
|
|
|
2150
2808
|
registry.registerAll(mcp.tools);
|
|
2151
2809
|
}
|
|
2152
2810
|
const mode2 = resolvePermissionMode(config.settings, options.permissionMode);
|
|
2153
|
-
const sessionStore2 = options.sessionPersistence ? new SessionStore(
|
|
2811
|
+
const sessionStore2 = options.sessionPersistence ? new SessionStore(sessionRoot()) : void 0;
|
|
2154
2812
|
const bootstrap2 = resolveSessionBootstrap(sessionStore2, {
|
|
2155
2813
|
cwd: config.workspace,
|
|
2156
2814
|
resume: options.resume,
|
|
2157
2815
|
continue: options.continue,
|
|
2158
2816
|
sessionId: options.sessionId,
|
|
2159
2817
|
sessionName: options.name,
|
|
2160
|
-
forkSession: options.forkSession
|
|
2818
|
+
forkSession: options.forkSession,
|
|
2819
|
+
driver: config.sessionDriver
|
|
2161
2820
|
});
|
|
2821
|
+
launched = { store: sessionStore2, bootstrap: bootstrap2 };
|
|
2162
2822
|
sessionStore2?.cleanup(DEFAULT_LOCAL_DATA_RETENTION_DAYS, /* @__PURE__ */ new Set([bootstrap2.sessionId]));
|
|
2823
|
+
const printFormat = options.outputFormat;
|
|
2824
|
+
const readerGone = new AbortController();
|
|
2825
|
+
printInterrupt = installPrintInterrupt();
|
|
2826
|
+
const callerSignal = options.signal;
|
|
2827
|
+
printSignal = AbortSignal.any(
|
|
2828
|
+
[callerSignal, readerGone.signal, printInterrupt.signal].filter(
|
|
2829
|
+
(signal) => signal !== void 0
|
|
2830
|
+
)
|
|
2831
|
+
);
|
|
2832
|
+
process.stderr.on("error", printPipeErrorHandler("stderr", printFormat, readerGone));
|
|
2833
|
+
process.stdout.on("error", printPipeErrorHandler("stdout", printFormat, readerGone));
|
|
2163
2834
|
result = await runHeadless(config, registry, {
|
|
2164
2835
|
prompt: typeof options.print === "string" ? options.print : void 0,
|
|
2165
2836
|
inputFormat: options.inputFormat,
|
|
@@ -2168,13 +2839,15 @@ async function runMainAction(options) {
|
|
|
2168
2839
|
transcript: bootstrap2.transcript,
|
|
2169
2840
|
compactBoundaries: bootstrap2.compactBoundaries,
|
|
2170
2841
|
plan: bootstrap2.plan,
|
|
2842
|
+
goal: bootstrap2.goal,
|
|
2171
2843
|
carriedUsage: bootstrap2.carriedUsage,
|
|
2172
2844
|
carriedModels: bootstrap2.carriedModels,
|
|
2173
2845
|
mode: mode2,
|
|
2174
|
-
signal:
|
|
2846
|
+
signal: printSignal,
|
|
2175
2847
|
maxTurns: parseNumericFlag(options.maxTurns, "--max-turns", { integer: true }),
|
|
2176
2848
|
maxBudgetUsd: parseNumericFlag(options.maxBudgetUsd, "--max-budget-usd"),
|
|
2177
2849
|
verbose: options.verbose,
|
|
2850
|
+
quiet: options.quiet,
|
|
2178
2851
|
jsonSchema: options.jsonSchema ? JSON.parse(options.jsonSchema) : void 0,
|
|
2179
2852
|
sessionStore: sessionStore2,
|
|
2180
2853
|
sessionId: bootstrap2.sessionId,
|
|
@@ -2184,23 +2857,28 @@ async function runMainAction(options) {
|
|
|
2184
2857
|
persistSession: options.sessionPersistence,
|
|
2185
2858
|
includeHookEvents: options.includeHookEvents,
|
|
2186
2859
|
includePartialMessages: options.includePartialMessages,
|
|
2860
|
+
includeResultMessages: options.includeResultMessages,
|
|
2187
2861
|
promptSuggestions: options.promptSuggestions
|
|
2188
2862
|
});
|
|
2189
2863
|
} finally {
|
|
2864
|
+
printInterrupt?.dispose();
|
|
2190
2865
|
await disconnectMcpServers(mcp.connections);
|
|
2191
2866
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2867
|
+
const code = printExitCode({
|
|
2868
|
+
outcome: result?.outcome,
|
|
2869
|
+
aborted: printSignal?.aborted === true,
|
|
2870
|
+
interruptedBy: printInterrupt?.interruptedBy()
|
|
2871
|
+
});
|
|
2872
|
+
if (code !== 0) setExitCode(code);
|
|
2195
2873
|
return;
|
|
2196
2874
|
}
|
|
2197
2875
|
const mode = resolvePermissionMode(config.settings, options.permissionMode);
|
|
2198
2876
|
if (options.scrollback) {
|
|
2199
|
-
const { runScrollbackSession } = await import("./scrollback-
|
|
2877
|
+
const { runScrollbackSession } = await import("./scrollback-GD76GNM4.js");
|
|
2200
2878
|
await runScrollbackSession(config, { mode });
|
|
2201
2879
|
return;
|
|
2202
2880
|
}
|
|
2203
|
-
const sessionStore = options.sessionPersistence ? new SessionStore(
|
|
2881
|
+
const sessionStore = options.sessionPersistence ? new SessionStore(sessionRoot()) : void 0;
|
|
2204
2882
|
const ephemeralRewind = sessionStore ? void 0 : createEphemeralRewindEnvironment(config.workspace);
|
|
2205
2883
|
const timelineStore = sessionStore ?? ephemeralRewind.timelineStore;
|
|
2206
2884
|
let snapshotStore = sessionStore ? createRewindSnapshotStore(config.workspace) : ephemeralRewind.snapshotStore;
|
|
@@ -2219,26 +2897,30 @@ async function runMainAction(options) {
|
|
|
2219
2897
|
continue: options.continue,
|
|
2220
2898
|
sessionId: options.sessionId,
|
|
2221
2899
|
sessionName: options.name,
|
|
2222
|
-
forkSession: options.forkSession
|
|
2900
|
+
forkSession: options.forkSession,
|
|
2901
|
+
driver: config.sessionDriver
|
|
2223
2902
|
});
|
|
2903
|
+
launched = { store: sessionStore, bootstrap };
|
|
2224
2904
|
sessionStore?.cleanup(DEFAULT_LOCAL_DATA_RETENTION_DAYS, /* @__PURE__ */ new Set([bootstrap.sessionId]));
|
|
2225
2905
|
if (!sessionStore) {
|
|
2226
2906
|
timelineStore.create({
|
|
2227
2907
|
id: bootstrap.sessionId,
|
|
2228
2908
|
cwd: config.workspace,
|
|
2229
|
-
name: bootstrap.sessionName
|
|
2909
|
+
name: bootstrap.sessionName,
|
|
2910
|
+
driver: config.sessionDriver
|
|
2230
2911
|
});
|
|
2231
2912
|
}
|
|
2232
|
-
const [{ App }, { loadInteractiveAssets }] = await Promise.all([
|
|
2233
|
-
import("./app-
|
|
2234
|
-
import("./interactive-assets-
|
|
2913
|
+
const [{ App }, { loadInteractiveAssets }, { render }, { createElement }] = await Promise.all([
|
|
2914
|
+
import("./app-J7BVPSUH.js"),
|
|
2915
|
+
import("./interactive-assets-73AQO5WP.js"),
|
|
2916
|
+
import("ink"),
|
|
2917
|
+
import("react")
|
|
2235
2918
|
]);
|
|
2236
2919
|
const interactiveAssets = loadInteractiveAssets(config);
|
|
2237
2920
|
const mcpHost = new McpSessionHost(config.workspace, config.settings);
|
|
2238
2921
|
mcpHost.start();
|
|
2239
2922
|
let app;
|
|
2240
2923
|
const redrawViewport = () => {
|
|
2241
|
-
app?.clear();
|
|
2242
2924
|
process.stdout.write("\x1B[H\x1B[2J");
|
|
2243
2925
|
};
|
|
2244
2926
|
const restoreScreen = config.accessibility.screenReader ? () => {
|
|
@@ -2247,27 +2929,42 @@ async function runMainAction(options) {
|
|
|
2247
2929
|
const rendererMode = resolveTuiRendererMode(process.env.BOOK_TUI_RENDERER, {
|
|
2248
2930
|
isTTY: process.stdout.isTTY === true,
|
|
2249
2931
|
screenReader: config.accessibility.screenReader,
|
|
2250
|
-
|
|
2932
|
+
incrementalRendererFixed: hasInkTrailingNewlineFix(),
|
|
2251
2933
|
platform: process.platform
|
|
2252
2934
|
});
|
|
2253
2935
|
await installInkScrollRenderer(rendererMode === "experimental-scroll");
|
|
2254
2936
|
await installFrameCapture();
|
|
2937
|
+
await installInkOutputCache();
|
|
2938
|
+
await installInkRenderCull();
|
|
2939
|
+
await installInkFrameClip();
|
|
2940
|
+
const flushSync = await loadInkFlushSync();
|
|
2255
2941
|
app = render(
|
|
2256
2942
|
createElement(App, {
|
|
2257
2943
|
config,
|
|
2258
2944
|
permissionMode: mode,
|
|
2259
2945
|
interactiveAssets,
|
|
2260
2946
|
redrawViewport,
|
|
2947
|
+
flushSync,
|
|
2261
2948
|
mcp: mcpHost,
|
|
2262
|
-
session: {
|
|
2949
|
+
session: {
|
|
2950
|
+
...bootstrap,
|
|
2951
|
+
store: sessionStore,
|
|
2952
|
+
timelineStore,
|
|
2953
|
+
snapshotStore,
|
|
2954
|
+
createdSessions
|
|
2955
|
+
},
|
|
2956
|
+
startupNotices: [...providerNotices, ...instructionNotices]
|
|
2263
2957
|
}),
|
|
2264
2958
|
{
|
|
2265
2959
|
exitOnCtrlC: false,
|
|
2266
2960
|
isScreenReaderEnabled: config.accessibility.screenReader,
|
|
2267
2961
|
incrementalRendering: rendererMode !== "safe",
|
|
2268
|
-
maxFps:
|
|
2962
|
+
maxFps: resolveInkMaxFps(process.platform),
|
|
2963
|
+
// Ink 7 also turns live frames off for a non-TTY stdout, which the WSL bridge uses.
|
|
2964
|
+
interactive: !isCiEnvironment()
|
|
2269
2965
|
}
|
|
2270
2966
|
);
|
|
2967
|
+
await installInkFrameThrottle(process.stdout);
|
|
2271
2968
|
await app.waitUntilExit();
|
|
2272
2969
|
} finally {
|
|
2273
2970
|
app?.cleanup();
|
|
@@ -2278,12 +2975,23 @@ async function runMainAction(options) {
|
|
|
2278
2975
|
} catch (e) {
|
|
2279
2976
|
if (isExiting()) throw e;
|
|
2280
2977
|
console.error(e instanceof Error ? e.message : String(e));
|
|
2281
|
-
|
|
2978
|
+
if (options.print !== void 0) {
|
|
2979
|
+
printInterrupt?.dispose();
|
|
2980
|
+
const code = printExitCode({
|
|
2981
|
+
aborted: printSignal?.aborted === true,
|
|
2982
|
+
interruptedBy: printInterrupt?.interruptedBy()
|
|
2983
|
+
});
|
|
2984
|
+
if (code !== 0) setExitCode(code);
|
|
2985
|
+
return;
|
|
2986
|
+
}
|
|
2987
|
+
setExitCode(1);
|
|
2988
|
+
} finally {
|
|
2989
|
+
if (launched) discardUntouchedSessions(launched.store, launched.bootstrap, createdSessions);
|
|
2282
2990
|
}
|
|
2283
2991
|
}
|
|
2284
2992
|
|
|
2285
2993
|
// src/cli/mcp.ts
|
|
2286
|
-
import { existsSync as
|
|
2994
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
2287
2995
|
import { join as join7 } from "path";
|
|
2288
2996
|
function configPath(workspace, scope, home) {
|
|
2289
2997
|
return scope === "project" ? join7(workspace, ".mcp.json") : home ? join7(home, ".book", "mcp.json") : join7(resolveBookHome(), "mcp.json");
|
|
@@ -2296,10 +3004,12 @@ function validScope(scope) {
|
|
|
2296
3004
|
return normalized;
|
|
2297
3005
|
}
|
|
2298
3006
|
function readConfigDocument(path) {
|
|
2299
|
-
|
|
3007
|
+
const refusal = projectFileWriteRefusal(path);
|
|
3008
|
+
if (refusal) throw new Error(`Cannot update ${path}: ${refusal}.`);
|
|
3009
|
+
if (!existsSync4(path)) return {};
|
|
2300
3010
|
let parsed;
|
|
2301
3011
|
try {
|
|
2302
|
-
parsed = JSON.parse(
|
|
3012
|
+
parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
2303
3013
|
} catch (error) {
|
|
2304
3014
|
throw new Error(
|
|
2305
3015
|
`Cannot update ${path}: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -2480,13 +3190,19 @@ function parseEffortOption(value) {
|
|
|
2480
3190
|
function resolveWorkspace(workspace) {
|
|
2481
3191
|
return workspace ?? program.opts().workspace;
|
|
2482
3192
|
}
|
|
2483
|
-
program.name("book").description("AI coding agent with rich TUI").enablePositionalOptions().version(getPackageVersion()).option("-w, --workspace <path>", "Workspace root directory", process.cwd()).option("-m, --model <model>", "Model to use").option(
|
|
3193
|
+
program.name("book").description("AI coding agent with rich TUI").enablePositionalOptions().version(getPackageVersion()).argument("[prompt]", "Prompt for print mode; the same as the -p value, in either position").option("-w, --workspace <path>", "Workspace root directory", process.cwd()).option("-m, --model <model>", "Model to use").option(
|
|
2484
3194
|
"-p, --print [prompt]",
|
|
2485
|
-
"Print mode (non-interactive). Reads prompt from the flag or stdin."
|
|
3195
|
+
"Print mode (non-interactive). Reads the prompt from the flag, the [prompt] argument, or stdin."
|
|
2486
3196
|
).option("--output-format <format>", "text | json | stream-json (print mode)", "text").option("--input-format <format>", "text | stream-json (print mode)", "text").option("--max-turns <n>", "Max agent turns (print mode; omit for unlimited)").option("--max-budget-usd <amount>", "Max USD spend (print mode)").option(
|
|
2487
3197
|
"--permission-mode <mode>",
|
|
2488
3198
|
"default | acceptEdits | plan | auto | dontAsk | bypassPermissions (overrides settings.defaultMode)"
|
|
2489
|
-
).option("--verbose", "
|
|
3199
|
+
).option("--verbose", "Print mode: add each tool result to the progress lines on stderr").option("-q, --quiet", "Print mode: no progress lines on stderr").option("--json-schema <schema>", "Return validated JSON matching a JSON Schema (print mode)").option("-r, --resume <id|name>", "Resume a session by id or name").option("-c, --continue", "Resume the most recent session in this directory that has a message").option("--session-id <uuid>", "Use a specific session id").option("-n, --name <name>", "Set a display name for the session").option("--no-session-persistence", "Do not save the session to disk").option("--fork-session", "On resume, create a new session id instead of reusing").option(
|
|
3200
|
+
"--session-driver <driver>",
|
|
3201
|
+
"human or agent (default: agent under another agent harness); an agent-driven session saves only what it learned, never its instructions"
|
|
3202
|
+
).option("--include-hook-events", "Emit hook lifecycle events in stream-json output").option("--include-partial-messages", "Emit partial assistant text deltas in stream-json output").option(
|
|
3203
|
+
"--include-result-messages",
|
|
3204
|
+
"Include the full message history in the stream-json result event"
|
|
3205
|
+
).option("--prompt-suggestions", "Ask model for follow-up prompt suggestions after completion").option("--agents <mode>", "Managed agents: adaptive, manual, off").option("--scrollback", "Use terminal-native scrollback instead of the full-screen TUI").option("--settings <path>", "Path to an ad-hoc settings file (overrides all scopes)").option("--no-settings", "Skip all settings.json layers (use defaults + legacy .bookrc.json)").option(
|
|
2490
3206
|
"--effort <level>",
|
|
2491
3207
|
"Thinking effort: low, medium, high, xhigh, max (default: high)",
|
|
2492
3208
|
parseEffortOption
|
|
@@ -2496,7 +3212,10 @@ program.command("doctor").description("Diagnose configuration and environment").
|
|
|
2496
3212
|
noSettings: options.settings === false
|
|
2497
3213
|
});
|
|
2498
3214
|
});
|
|
2499
|
-
program.command("status").description("Report what a session is working on and what it has spent (no credentials needed)").argument(
|
|
3215
|
+
program.command("status").description("Report what a session is working on and what it has spent (no credentials needed)").argument(
|
|
3216
|
+
"[session]",
|
|
3217
|
+
"Session id or name (defaults to the most recent session in this workspace with a message or a run record)"
|
|
3218
|
+
).option("-w, --workspace <path>", "Workspace root directory (defaults to the root -w, then cwd)").option("--json", "Emit the status as JSON").action((session, options) => {
|
|
2500
3219
|
const code = runStatusCommand({
|
|
2501
3220
|
session,
|
|
2502
3221
|
workspace: resolveWorkspace(options.workspace),
|
|
@@ -2552,7 +3271,29 @@ trustCommand.command("rule").description("Approve or reject a project-declared p
|
|
|
2552
3271
|
});
|
|
2553
3272
|
}
|
|
2554
3273
|
);
|
|
2555
|
-
|
|
3274
|
+
trustCommand.command("dir").description("Approve or reject a project-declared additionalDirectories entry").argument("[path]", "The declared entry, or the real path `book doctor` reported for it").option("-w, --workspace <path>", "Workspace root directory (defaults to the root -w, then cwd)").option("--all-pending", "Apply to every directory currently awaiting a decision").option("--reject", "Record a refusal instead of an approval").action(
|
|
3275
|
+
async (path, options) => {
|
|
3276
|
+
await runTrustCommand("dir", path, {
|
|
3277
|
+
...options,
|
|
3278
|
+
workspace: resolveWorkspace(options.workspace)
|
|
3279
|
+
});
|
|
3280
|
+
}
|
|
3281
|
+
);
|
|
3282
|
+
trustCommand.command("provider").description("Approve or reject a provider (origin and key) a repository declared").argument(
|
|
3283
|
+
"[id]",
|
|
3284
|
+
"Provider id, or .bookrc.json for a legacy baseUrl; omit to list what is pending"
|
|
3285
|
+
).option("-w, --workspace <path>", "Workspace root directory (defaults to the root -w, then cwd)").option("--all-pending", "Apply to every provider currently awaiting a decision").option("--reject", "Record a refusal instead of an approval").action(
|
|
3286
|
+
async (id, options) => {
|
|
3287
|
+
await runTrustCommand("provider", id, {
|
|
3288
|
+
...options,
|
|
3289
|
+
workspace: resolveWorkspace(options.workspace)
|
|
3290
|
+
});
|
|
3291
|
+
}
|
|
3292
|
+
);
|
|
3293
|
+
program.command("config").description("Read and write settings").option("-w, --workspace <path>", "Workspace root directory (defaults to the root -w, then cwd)").option("--settings <path>", "Path to an ad-hoc settings file (overrides all scopes)").option("--no-settings", "Skip all settings.json layers (use defaults + legacy .bookrc.json)").option("-g, --global", "Act on user-global settings (<BOOK_HOME>/settings.json) [default]").option("--project", "Act on checked-in project settings (.book/settings.json)").option("--local", "Act on gitignored project-local settings (.book/settings.local.json)").option("--no-check", "provider-key: save without testing the key against the endpoint").option("--force", "provider-key: save even when the endpoint rejects the key").argument(
|
|
3294
|
+
"[action]",
|
|
3295
|
+
"get <key>, set <key> <value>, unset <key>, list, or provider-key <id> (key read from stdin)"
|
|
3296
|
+
).argument("[key]", "Dot-separated key path (e.g. permissions.deny), or the provider id").argument("[value]", "Value to set (JSON-parsed)").addHelpText("after", `
|
|
2556
3297
|
${formatSettingsKeyHelp()}`).action(
|
|
2557
3298
|
async (action, key, value, options) => {
|
|
2558
3299
|
const rootSettings = program.opts().settings;
|
|
@@ -2571,12 +3312,15 @@ ${formatSettingsKeyHelp()}`).action(
|
|
|
2571
3312
|
await runConfigCommand(resolveWorkspace(options.workspace), action, key, value, {
|
|
2572
3313
|
settingsOverridePath: typeof settings === "string" ? settings : void 0,
|
|
2573
3314
|
noSettings: settings === false,
|
|
2574
|
-
scope: selected[0]?.[0]
|
|
3315
|
+
scope: selected[0]?.[0],
|
|
3316
|
+
providerKey: { check: options.check, force: options.force }
|
|
2575
3317
|
});
|
|
2576
3318
|
}
|
|
2577
3319
|
);
|
|
2578
|
-
program.action(async (options) => {
|
|
2579
|
-
|
|
3320
|
+
program.action(async (prompt, options) => {
|
|
3321
|
+
const resolved = resolvePrintPrompt(options.print, prompt);
|
|
3322
|
+
if ("error" in resolved) program.error(`error: ${resolved.error}`);
|
|
3323
|
+
await runMainAction({ ...options, print: resolved.print });
|
|
2580
3324
|
});
|
|
2581
3325
|
program.parse();
|
|
2582
3326
|
//# sourceMappingURL=index.js.map
|