0sec-cli 0.21.4 → 0.22.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/0.js +56 -56
- package/README.md +48 -63
- package/chunks/{adapt-loop-IGBRQAXO.js → adapt-loop-7I57LXXR.js} +4 -4
- package/chunks/{artifact-scraper-LNQJUORS.js → artifact-scraper-PYZ3RU3O.js} +2 -2
- package/chunks/{assumption-mining-4MJTP6H5.js → assumption-mining-MFLF43IR.js} +16 -16
- package/chunks/{chunk-Z3IKZX3X.js → chunk-23DG5YKS.js} +1 -1
- package/chunks/{chunk-BACXRTQ4.js → chunk-2GV4HQKE.js} +87 -767
- package/chunks/{chunk-QZVG3BG4.js → chunk-5IBCBQEH.js} +1 -2
- package/chunks/{chunk-NDJU5ZZ7.js → chunk-5MWAG3FT.js} +3 -3
- package/chunks/{chunk-PC6RCKQV.js → chunk-76KSBWOQ.js} +59 -26
- package/chunks/chunk-7DEHG7YT.js +2143 -0
- package/chunks/{chunk-CL5KAUM6.js → chunk-7UFUALR2.js} +274 -8
- package/chunks/{chunk-IMGWPNQM.js → chunk-CAYANSH6.js} +67 -574
- package/chunks/{chunk-P6YG6TEZ.js → chunk-DKSCN2AB.js} +1 -1
- package/chunks/{chunk-O6SD4A36.js → chunk-DM5C5L5D.js} +1 -1
- package/chunks/{chunk-FAH6V4QM.js → chunk-EGZGZFAH.js} +1116 -2291
- package/chunks/chunk-HR2TU42B.js +1228 -0
- package/chunks/{chunk-TF46UWFU.js → chunk-HUNAPTKV.js} +1 -1
- package/chunks/chunk-IO7R7WBM.js +114 -0
- package/chunks/{chunk-W4JOXE4N.js → chunk-IXT64DV5.js} +709 -1929
- package/chunks/{chunk-HBKCGJAV.js → chunk-LCRTAQN2.js} +1 -1
- package/chunks/{chunk-WPQLBN4S.js → chunk-LPTRPRZR.js} +1 -1
- package/chunks/{chunk-Y5H6E24H.js → chunk-LYUAM3TG.js} +1 -1
- package/chunks/{chunk-NOPBXZUV.js → chunk-MICHDTBK.js} +1 -1
- package/chunks/{chunk-CAJJRUTV.js → chunk-OAYUPGF6.js} +1 -1
- package/chunks/{chunk-FWAYJ2IV.js → chunk-OWLJYJ5O.js} +8 -8
- package/chunks/chunk-RE5EZDG2.js +5523 -0
- package/chunks/{chunk-RF5QGKLR.js → chunk-RRD4XXPU.js} +3 -3
- package/chunks/{chunk-2WSXFFJZ.js → chunk-RSIUX3RW.js} +2 -2
- package/chunks/{chunk-ANDKY54G.js → chunk-SJBCK2EI.js} +4019 -7299
- package/chunks/{chunk-G34QP2XW.js → chunk-TM74SSHC.js} +1 -1
- package/chunks/{chunk-VOAXLO4A.js → chunk-TO35Y326.js} +5 -721
- package/chunks/{chunk-SBMB4OL3.js → chunk-TZFIAMWZ.js} +62 -8
- package/chunks/chunk-UELXHK2Y.js +1950 -0
- package/chunks/{chunk-YHWWF4QY.js → chunk-UU7PQP6N.js} +3 -3
- package/chunks/{chunk-E2UOY5PJ.js → chunk-UV5QWZGE.js} +12 -70
- package/chunks/{chunk-YA4PUM4H.js → chunk-VMRRNRMR.js} +1 -1
- package/chunks/{chunk-WZISK5VC.js → chunk-VNRY6AWF.js} +53 -7
- package/chunks/{chunk-ER3SUMYJ.js → chunk-W6UWZFKL.js} +0 -195
- package/chunks/{chunk-5T6D5BVY.js → chunk-XFCTUKXR.js} +1 -1
- package/chunks/{chunk-YPOA3W4O.js → chunk-YIQSGNAA.js} +875 -108
- package/chunks/{codex-models-TYYV4JNN.js → codex-models-7TLJSY3L.js} +5 -3
- package/chunks/{commands-WF5RG4I2.js → commands-NEI7L3QA.js} +2151 -3642
- package/chunks/{db-U3WQZVAO.js → db-5AP3JB4W.js} +3 -3
- package/chunks/{disclose-67GZCDBO.js → disclose-LDNKAVRH.js} +10 -8
- package/chunks/{dist-F6X62TQK.js → dist-3FNZIBB4.js} +223 -192
- package/chunks/{dist-EVTN3UTT.js → dist-5YHYNLI3.js} +2 -2
- package/chunks/{dist-ACC3DZUZ.js → dist-DD4H6HMQ.js} +3 -1
- package/chunks/{eval-runner-SA6OUGHZ.js → eval-runner-43BO2C4T.js} +11 -11
- package/chunks/{exploit-agent-36Q7XQFP.js → exploit-agent-N4A5ELBK.js} +1 -1
- package/chunks/{exploit-autoclimb-J4OTSTZ6.js → exploit-autoclimb-N7P6KIF5.js} +1 -1
- package/chunks/{exploit-climb-PYEJDIC4.js → exploit-climb-WNEJUB5W.js} +2 -2
- package/chunks/fix-Z452M4EC.js +26 -0
- package/chunks/{harness-GDUMKGSE.js → harness-QJJJVSG4.js} +3 -3
- package/chunks/{hunt-scan-UDJSCISU.js → hunt-scan-K6D2V3PX.js} +14 -14
- package/chunks/{identity-JTIVJSV7.js → identity-62SVRW4H.js} +1 -1
- package/chunks/{kernel-primitive-F2IXUFAX.js → kernel-primitive-TKUACPKD.js} +2 -2
- package/chunks/{kernel-vm-runner-276LHIOV.js → kernel-vm-runner-RAAO2PGT.js} +2 -2
- package/chunks/{memsafety-scan-NHUOGTEX.js → memsafety-scan-FCPAXIZE.js} +2 -2
- package/chunks/{native-loop-3BSYVDUI.js → native-loop-2ZRJENZ3.js} +10 -10
- package/chunks/{npm-detectors-VIYJXKFQ.js → npm-detectors-Z2ZIQCN4.js} +2 -2
- package/chunks/orchestrate-2GWGNTBF.js +58 -0
- package/chunks/{process-PNMC36JI.js → process-N245QIQG.js} +1 -4
- package/chunks/{run-CBGE6DRQ.js → run-U3XU4CTC.js} +9940 -12572
- package/chunks/{runtime-PXO5G6UV.js → runtime-G75TEWQY.js} +4 -5
- package/chunks/{scope-NJHIDDTN.js → scope-WXI46UHJ.js} +4 -1
- package/chunks/{session-store-46WBZM22.js → session-store-27L3ZU73.js} +2 -2
- package/chunks/{variant-candidates-UH2WC2HA.js → variant-candidates-YWZORZMK.js} +3 -3
- package/chunks/{web-recon-prepass-SFXU44SS.js → web-recon-prepass-YVMFXCIK.js} +3 -3
- package/cli-path-traversal-fixture.js +352 -0
- package/dashboard/assets/bot-Bg1Tp9_d.js +1 -0
- package/dashboard/assets/dist-B_dDCPbu.js +1 -0
- package/dashboard/assets/findings-page-mayGhNx6.js +5 -0
- package/dashboard/assets/format--susmHX7.js +1 -0
- package/dashboard/assets/input-TuVPR3Zl.js +1 -0
- package/dashboard/assets/jsx-runtime-MGseXjj5.js +3 -0
- package/dashboard/assets/live-page-HWWHX99U.js +1 -0
- package/dashboard/assets/meta-tile-ckHo-w7k.js +1 -0
- package/dashboard/assets/operations-BxZ0OoXO.js +10 -0
- package/dashboard/assets/operations-app-D3d35rgo.js +46 -0
- package/dashboard/assets/overview-page-W_4IteEr.js +1 -0
- package/dashboard/assets/page-header-Bw9AJgBV.js +1 -0
- package/dashboard/assets/scans-page-CgzVXHnn.js +1 -0
- package/dashboard/assets/siren-KQj8QAnJ.js +1 -0
- package/dashboard/assets/table-8EDklulN.js +1 -0
- package/dashboard/assets/tabs-CFHPOwg8.js +1 -0
- package/dashboard/index.html +2 -3
- package/node_modules/node-addon-api/LICENSE.md +9 -0
- package/node_modules/node-addon-api/README.md +95 -0
- package/node_modules/node-addon-api/common.gypi +21 -0
- package/node_modules/node-addon-api/except.gypi +25 -0
- package/node_modules/node-addon-api/index.js +14 -0
- package/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
- package/node_modules/node-addon-api/napi-inl.h +7250 -0
- package/node_modules/node-addon-api/napi.h +3463 -0
- package/node_modules/node-addon-api/node_addon_api.gyp +42 -0
- package/node_modules/node-addon-api/node_api.gyp +9 -0
- package/node_modules/node-addon-api/noexcept.gypi +26 -0
- package/node_modules/node-addon-api/nothing.c +0 -0
- package/node_modules/node-addon-api/package-support.json +21 -0
- package/node_modules/node-addon-api/package.json +481 -0
- package/node_modules/node-addon-api/tools/README.md +73 -0
- package/node_modules/node-addon-api/tools/check-napi.js +99 -0
- package/node_modules/node-addon-api/tools/clang-format.js +71 -0
- package/node_modules/node-addon-api/tools/conversion.js +301 -0
- package/node_modules/node-gyp-build/LICENSE +21 -0
- package/node_modules/node-gyp-build/README.md +58 -0
- package/node_modules/node-gyp-build/SECURITY.md +5 -0
- package/node_modules/node-gyp-build/bin.js +84 -0
- package/node_modules/node-gyp-build/build-test.js +19 -0
- package/node_modules/node-gyp-build/index.js +6 -0
- package/node_modules/node-gyp-build/node-gyp-build.js +207 -0
- package/node_modules/node-gyp-build/optional.js +7 -0
- package/node_modules/node-gyp-build/package.json +43 -0
- package/node_modules/tree-sitter/LICENSE +21 -0
- package/node_modules/tree-sitter/README.md +90 -0
- package/node_modules/tree-sitter/binding.gyp +76 -0
- package/node_modules/tree-sitter/index.js +895 -0
- package/node_modules/tree-sitter/package.json +62 -0
- package/node_modules/tree-sitter/prebuilds/darwin-arm64/tree-sitter.node +0 -0
- package/node_modules/tree-sitter/prebuilds/darwin-x64/tree-sitter.node +0 -0
- package/node_modules/tree-sitter/prebuilds/linux-x64/tree-sitter.node +0 -0
- package/node_modules/tree-sitter/prebuilds/win32-x64/tree-sitter.node +0 -0
- package/node_modules/tree-sitter/src/addon_data.h +47 -0
- package/node_modules/tree-sitter/src/binding.cc +35 -0
- package/node_modules/tree-sitter/src/conversions.cc +140 -0
- package/node_modules/tree-sitter/src/conversions.h +22 -0
- package/node_modules/tree-sitter/src/language.cc +106 -0
- package/node_modules/tree-sitter/src/language.h +17 -0
- package/node_modules/tree-sitter/src/logger.cc +70 -0
- package/node_modules/tree-sitter/src/logger.h +19 -0
- package/node_modules/tree-sitter/src/lookaheaditerator.cc +122 -0
- package/node_modules/tree-sitter/src/lookaheaditerator.h +33 -0
- package/node_modules/tree-sitter/src/node.cc +1056 -0
- package/node_modules/tree-sitter/src/node.h +30 -0
- package/node_modules/tree-sitter/src/parser.cc +298 -0
- package/node_modules/tree-sitter/src/parser.h +35 -0
- package/node_modules/tree-sitter/src/query.cc +383 -0
- package/node_modules/tree-sitter/src/query.h +39 -0
- package/node_modules/tree-sitter/src/tree.cc +307 -0
- package/node_modules/tree-sitter/src/tree.h +45 -0
- package/node_modules/tree-sitter/src/tree_cursor.cc +213 -0
- package/node_modules/tree-sitter/src/tree_cursor.h +52 -0
- package/node_modules/tree-sitter/tree-sitter.d.ts +211 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/LICENSE +21 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/README.md +21 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/README.md +6 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/include/tree_sitter/api.h +1266 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/alloc.c +48 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/alloc.h +41 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/array.h +290 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/atomic.h +68 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/clock.h +146 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/error_costs.h +11 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/get_changed_ranges.c +501 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/get_changed_ranges.h +36 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/host.h +21 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/language.c +221 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/language.h +299 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/length.h +52 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/lexer.c +419 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/lexer.h +49 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/lib.c +14 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/node.c +774 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/parser.c +2151 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/parser.h +230 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/point.h +62 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/query.c +4134 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/reduce_action.h +34 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/reusable_node.h +95 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/stack.c +899 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/stack.h +133 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/subtree.c +1060 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/subtree.h +382 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree.c +165 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree.h +31 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree_cursor.c +714 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree_cursor.h +48 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/ICU_SHA +1 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/LICENSE +414 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/README.md +29 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/ptypes.h +1 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/umachine.h +448 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/urename.h +1 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/utf.h +1 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/utf16.h +733 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/utf8.h +881 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode.h +50 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm/stdlib-symbols.txt +23 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm/stdlib.c +109 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm/wasm-stdlib.h +1294 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm_store.c +1823 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm_store.h +31 -0
- package/node_modules/tree-sitter/vendor/tree-sitter/test/fixtures/grammars/.gitkeep +0 -0
- package/node_modules/tree-sitter-c/LICENSE +21 -0
- package/node_modules/tree-sitter-c/README.md +18 -0
- package/node_modules/tree-sitter-c/binding.gyp +20 -0
- package/node_modules/tree-sitter-c/bindings/node/binding.cc +20 -0
- package/node_modules/tree-sitter-c/bindings/node/index.d.ts +28 -0
- package/node_modules/tree-sitter-c/bindings/node/index.js +7 -0
- package/node_modules/tree-sitter-c/grammar.js +1463 -0
- package/node_modules/tree-sitter-c/package.json +108 -0
- package/node_modules/tree-sitter-c/prebuilds/darwin-arm64/tree-sitter-c.node +0 -0
- package/node_modules/tree-sitter-c/prebuilds/darwin-x64/tree-sitter-c.node +0 -0
- package/node_modules/tree-sitter-c/prebuilds/linux-x64/tree-sitter-c.node +0 -0
- package/node_modules/tree-sitter-c/prebuilds/win32-x64/tree-sitter-c.node +0 -0
- package/node_modules/tree-sitter-c/queries/highlights.scm +81 -0
- package/node_modules/tree-sitter-c/queries/tags.scm +9 -0
- package/node_modules/tree-sitter-c/src/grammar.json +9682 -0
- package/node_modules/tree-sitter-c/src/node-types.json +4556 -0
- package/node_modules/tree-sitter-c/src/parser.c +113852 -0
- package/node_modules/tree-sitter-c/src/tree_sitter/alloc.h +54 -0
- package/node_modules/tree-sitter-c/src/tree_sitter/array.h +290 -0
- package/node_modules/tree-sitter-c/src/tree_sitter/parser.h +265 -0
- package/package.json +10 -3
- package/vendor/tree-sitter-0.21.1-node24.json +16 -0
- package/vendor/tree-sitter-0.21.1-node24.tgz +0 -0
- package/chunks/chunk-CGHCEN7W.js +0 -169
- package/chunks/chunk-CXKDWFNO.js +0 -102
- package/chunks/chunk-GIQKESMN.js +0 -687
- package/chunks/chunk-HPQZHOIU.js +0 -67
- package/chunks/chunk-PD2BBBKT.js +0 -245
- package/chunks/chunk-WF5W75WY.js +0 -1070
- package/chunks/fix-J2KBRJDV.js +0 -12
- package/chunks/orchestrate-DHBFRTSI.js +0 -59
- package/chunks/replay-runner-LK2XFIVG.js +0 -39
- package/dashboard/assets/bot-yILPnRgD.js +0 -1
- package/dashboard/assets/chevron-down-BeDJ-Vka.js +0 -1
- package/dashboard/assets/circle-alert-BEnMBMT-.js +0 -1
- package/dashboard/assets/client-DRYsuOGl.js +0 -9
- package/dashboard/assets/copy-Bi5vzoF9.js +0 -1
- package/dashboard/assets/desktop-BT7RkC4q.js +0 -32
- package/dashboard/assets/desktop-OaBI0E0e.css +0 -1
- package/dashboard/assets/dist-CmX-h7a4.js +0 -1
- package/dashboard/assets/dist-DAwYQmf_.js +0 -45
- package/dashboard/assets/findings-page-B4rwF6fZ.js +0 -5
- package/dashboard/assets/format-LNQSSpn6.js +0 -1
- package/dashboard/assets/jsx-runtime-C7oxC63R.js +0 -1
- package/dashboard/assets/live-page-DoNwpPAO.js +0 -1
- package/dashboard/assets/meta-tile-BMtBC-Qq.js +0 -1
- package/dashboard/assets/operations-app-ER0gg7GN.js +0 -2
- package/dashboard/assets/operations-e2CGYKB1.js +0 -4
- package/dashboard/assets/overview-page-CmeWH17A.js +0 -1
- package/dashboard/assets/page-header-CIJyycfz.js +0 -1
- package/dashboard/assets/play-Dwo-9xt6.js +0 -1
- package/dashboard/assets/scans-page-CuhzWhHx.js +0 -1
- package/dashboard/assets/search-CiooTLck.js +0 -1
- package/dashboard/assets/siren-CCBwTSuk.js +0 -1
- package/dashboard/assets/table-BgsSBdke.js +0 -1
- package/dashboard/assets/tabs-vn6v7Bk6.js +0 -1
- package/dashboard/desktop.html +0 -21
|
@@ -5,19 +5,21 @@ import {
|
|
|
5
5
|
ANALYTICS_LEVEL_ENV,
|
|
6
6
|
analyticsPipeline,
|
|
7
7
|
levelAtLeast,
|
|
8
|
+
loadCloudCredentials,
|
|
8
9
|
resolveAnalyticsLevel
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-RE5EZDG2.js";
|
|
10
11
|
import {
|
|
11
12
|
DEFAULT_ALLOW_MODEL_SELF_EXTENSION,
|
|
12
13
|
createPresentationEvent,
|
|
13
14
|
homeStateDir
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-76KSBWOQ.js";
|
|
15
16
|
|
|
16
17
|
// packages/cli/src/tui/settings-store.ts
|
|
17
18
|
import { useSyncExternalStore } from "react";
|
|
18
19
|
|
|
19
20
|
// packages/cli/src/tui/settings.ts
|
|
20
|
-
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
21
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
22
|
+
import { randomUUID } from "node:crypto";
|
|
21
23
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
22
24
|
|
|
23
25
|
// packages/cli/src/tui/keybindings.ts
|
|
@@ -202,24 +204,6 @@ var KEYBINDINGS = [
|
|
|
202
204
|
// ── View ────────────────────────────────────────────────────────────────────
|
|
203
205
|
// The only rebindable set: self-contained, stateless toggles that call
|
|
204
206
|
// `updateSetting(...)` and take no part in text entry or modal cycles.
|
|
205
|
-
{
|
|
206
|
-
id: "view.left-sidebar",
|
|
207
|
-
keys: "Ctrl+B",
|
|
208
|
-
defaultChords: ["ctrl+b"],
|
|
209
|
-
rebindable: true,
|
|
210
|
-
description: "Toggle the left sidebar (persists across the session).",
|
|
211
|
-
category: "View",
|
|
212
|
-
handler: 'if (matchesBinding(key, "view.left-sidebar", \u2026)) updateSetting("showLeftSidebar", \u2026)'
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
id: "view.right-sidebar",
|
|
216
|
-
keys: "Ctrl+L",
|
|
217
|
-
defaultChords: ["ctrl+l"],
|
|
218
|
-
rebindable: true,
|
|
219
|
-
description: "Toggle the right sidebar (persists across the session).",
|
|
220
|
-
category: "View",
|
|
221
|
-
handler: 'if (matchesBinding(key, "view.right-sidebar", \u2026)) updateSetting("showRightSidebar", \u2026)'
|
|
222
|
-
},
|
|
223
207
|
{
|
|
224
208
|
id: "view.transcript-detail",
|
|
225
209
|
keys: "Ctrl+R",
|
|
@@ -229,35 +213,6 @@ var KEYBINDINGS = [
|
|
|
229
213
|
category: "View",
|
|
230
214
|
handler: 'if (matchesBinding(key, "view.transcript-detail", \u2026)) updateSetting("transcriptDetail", \u2026)'
|
|
231
215
|
},
|
|
232
|
-
{
|
|
233
|
-
id: "overlay.review-toggle",
|
|
234
|
-
keys: "Ctrl+O",
|
|
235
|
-
defaultChords: ["ctrl+o"],
|
|
236
|
-
rebindable: true,
|
|
237
|
-
description: "Open (and close) the full-screen transcript-review overlay.",
|
|
238
|
-
category: "View",
|
|
239
|
-
handler: 'if (matchesBinding(key, "overlay.review-toggle", \u2026)) setReviewOpen(true) / (reviewOpen) close'
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
id: "overlay.review-top",
|
|
243
|
-
keys: "Ctrl+Home",
|
|
244
|
-
defaultChords: ["ctrl+home"],
|
|
245
|
-
rebindable: false,
|
|
246
|
-
description: "Jump to the top of the transcript-review overlay.",
|
|
247
|
-
category: "View",
|
|
248
|
-
lockReason: "modal",
|
|
249
|
-
handler: 'if (reviewOpen && key.ctrl && key.name === "home") review.scrollY = 0'
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
id: "overlay.review-bottom",
|
|
253
|
-
keys: "Ctrl+End",
|
|
254
|
-
defaultChords: ["ctrl+end"],
|
|
255
|
-
rebindable: false,
|
|
256
|
-
description: "Jump to the bottom of the transcript-review overlay.",
|
|
257
|
-
category: "View",
|
|
258
|
-
lockReason: "modal",
|
|
259
|
-
handler: 'if (reviewOpen && key.ctrl && key.name === "end") review.scrollY = review.maxScrollY'
|
|
260
|
-
},
|
|
261
216
|
// ── Autonomy ─────────────────────────────────────────────────────────────────
|
|
262
217
|
{
|
|
263
218
|
id: "autonomy.cycle-mode",
|
|
@@ -1387,7 +1342,7 @@ var DEFS = [
|
|
|
1387
1342
|
{
|
|
1388
1343
|
key: "showLogo",
|
|
1389
1344
|
label: "Logo",
|
|
1390
|
-
description: 'Block "
|
|
1345
|
+
description: 'Block "0.SECURITY" wordmark shown on an empty transcript.',
|
|
1391
1346
|
kind: "boolean",
|
|
1392
1347
|
default: true,
|
|
1393
1348
|
group: "Display"
|
|
@@ -1416,22 +1371,6 @@ var DEFS = [
|
|
|
1416
1371
|
default: true,
|
|
1417
1372
|
group: "Transcript"
|
|
1418
1373
|
},
|
|
1419
|
-
{
|
|
1420
|
-
key: "showLeftSidebar",
|
|
1421
|
-
label: "Left sidebar",
|
|
1422
|
-
description: "Left sidebar in the chat view: recent sessions to resume, plus this run's findings. Hidden on narrow terminals.",
|
|
1423
|
-
kind: "boolean",
|
|
1424
|
-
default: false,
|
|
1425
|
-
group: "Display"
|
|
1426
|
-
},
|
|
1427
|
-
{
|
|
1428
|
-
key: "showRightSidebar",
|
|
1429
|
-
label: "Right sidebar",
|
|
1430
|
-
description: "Right sidebar: live agents, their activity, the current plan and findings. Hidden on narrow terminals.",
|
|
1431
|
-
kind: "boolean",
|
|
1432
|
-
default: false,
|
|
1433
|
-
group: "Display"
|
|
1434
|
-
},
|
|
1435
1374
|
{
|
|
1436
1375
|
key: "showTimestamps",
|
|
1437
1376
|
label: "Timestamps",
|
|
@@ -1511,9 +1450,9 @@ var DEFS = [
|
|
|
1511
1450
|
{
|
|
1512
1451
|
key: "roleLabelStyle",
|
|
1513
1452
|
label: "Role label",
|
|
1514
|
-
description: '
|
|
1453
|
+
description: 'Labels your messages as "You"; assistant replies stay unlabeled. Full and short include the optional age, glyph shows the name alone, and off hides the label.',
|
|
1515
1454
|
kind: "enum",
|
|
1516
|
-
default: "
|
|
1455
|
+
default: "off",
|
|
1517
1456
|
choices: ["full", "short", "glyph", "off"],
|
|
1518
1457
|
group: "Display"
|
|
1519
1458
|
},
|
|
@@ -1537,10 +1476,10 @@ var DEFS = [
|
|
|
1537
1476
|
{
|
|
1538
1477
|
key: "transcriptDetail",
|
|
1539
1478
|
label: "Transcript detail",
|
|
1540
|
-
description: "
|
|
1479
|
+
description: "Collapsed folds successful tool/reasoning steps and caps output previews at 20 lines; Ctrl+R toggles between that preview and expanded retained detail. Failures always show.",
|
|
1541
1480
|
kind: "enum",
|
|
1542
|
-
default: "
|
|
1543
|
-
choices: ["
|
|
1481
|
+
default: "collapsed",
|
|
1482
|
+
choices: ["collapsed", "expanded"],
|
|
1544
1483
|
group: "Transcript"
|
|
1545
1484
|
},
|
|
1546
1485
|
{
|
|
@@ -1606,7 +1545,7 @@ var DEFS = [
|
|
|
1606
1545
|
label: "Token usage",
|
|
1607
1546
|
description: 'Per-turn "in\u2192out tok" line under each answer.',
|
|
1608
1547
|
kind: "boolean",
|
|
1609
|
-
default:
|
|
1548
|
+
default: false,
|
|
1610
1549
|
group: "Telemetry"
|
|
1611
1550
|
},
|
|
1612
1551
|
{
|
|
@@ -1614,7 +1553,7 @@ var DEFS = [
|
|
|
1614
1553
|
label: "Cost",
|
|
1615
1554
|
description: "Estimated dollar cost, per turn and in the status bar.",
|
|
1616
1555
|
kind: "boolean",
|
|
1617
|
-
default:
|
|
1556
|
+
default: false,
|
|
1618
1557
|
group: "Telemetry"
|
|
1619
1558
|
},
|
|
1620
1559
|
{
|
|
@@ -1646,7 +1585,7 @@ var DEFS = [
|
|
|
1646
1585
|
{
|
|
1647
1586
|
key: "logoAnimation",
|
|
1648
1587
|
label: "Logo animation",
|
|
1649
|
-
description: 'Intro animation for the "
|
|
1588
|
+
description: 'Intro animation for the "0.SECURITY" wordmark: glitch (a neon-flecked scramble that resolves \u2014 the default), rainbow (a looping hue sweep), matrix (a green matrix-rain cascade), wave (a rippling cyan wavefront), neon (a neon-sign warm-up flicker), shimmer (a bright comet with a gradient tail), pulse (the slash breathes), strike (an orange slash strikes through the 0), draw (letters draw in behind a pen tip), fade (a centre-out bloom), typein (per-cell reveal), sweep (a bright bar wipes across) or off (static).',
|
|
1650
1589
|
kind: "enum",
|
|
1651
1590
|
default: "glitch",
|
|
1652
1591
|
choices: [
|
|
@@ -1677,19 +1616,19 @@ var DEFS = [
|
|
|
1677
1616
|
},
|
|
1678
1617
|
{
|
|
1679
1618
|
key: "analyticsLevel",
|
|
1680
|
-
label: "
|
|
1681
|
-
description: "
|
|
1619
|
+
label: "Data sharing",
|
|
1620
|
+
description: "Usage sends counts, timing, cost and error categories. Commands adds tool inputs/outputs and code; Full adds scope and findings. Credentials are scrubbed, but identifying content may remain. Uploads require an endpoint and are not anonymous. Environment opt-outs take precedence. Applies to this computer.",
|
|
1682
1621
|
kind: "enum",
|
|
1683
|
-
default: "
|
|
1622
|
+
default: "off",
|
|
1684
1623
|
choices: ["off", "usage", "commands", "full"],
|
|
1685
1624
|
group: "Privacy"
|
|
1686
1625
|
},
|
|
1687
1626
|
{
|
|
1688
1627
|
key: "diagnosticReporting",
|
|
1689
1628
|
label: "Problem reports",
|
|
1690
|
-
description: "
|
|
1629
|
+
description: "Review diagnostics before sending, send automatically, or keep reports local. Requires a configured endpoint. Off/Usage sharing sends categories only; Commands/Full can include scrubbed errors, stacks and failure output. Applies to this computer.",
|
|
1691
1630
|
kind: "enum",
|
|
1692
|
-
default: "
|
|
1631
|
+
default: "ask",
|
|
1693
1632
|
choices: ["off", "ask", "automatic"],
|
|
1694
1633
|
group: "Privacy"
|
|
1695
1634
|
},
|
|
@@ -1702,6 +1641,15 @@ var DEFS = [
|
|
|
1702
1641
|
choices: ["off", "notify", "automatic"],
|
|
1703
1642
|
group: "Updates"
|
|
1704
1643
|
},
|
|
1644
|
+
{
|
|
1645
|
+
key: "executionProfile",
|
|
1646
|
+
label: "Execution profile",
|
|
1647
|
+
description: "SmolVM runs the whole console, agents, browser and shell in the online Kali workbench. Prepare its approved image and explicit credential grants with 0 workbench setup. Applies to this computer on the next launch; failures never fall back to host execution.",
|
|
1648
|
+
kind: "enum",
|
|
1649
|
+
default: "local",
|
|
1650
|
+
choices: ["local", "smolvm"],
|
|
1651
|
+
group: "Security"
|
|
1652
|
+
},
|
|
1705
1653
|
{
|
|
1706
1654
|
key: "symbolPreset",
|
|
1707
1655
|
label: "Symbols",
|
|
@@ -1741,8 +1689,6 @@ var DEFAULT_SETTINGS = {
|
|
|
1741
1689
|
showRuntimeNotices: true,
|
|
1742
1690
|
showTurnSummary: false,
|
|
1743
1691
|
showSubagents: true,
|
|
1744
|
-
showLeftSidebar: false,
|
|
1745
|
-
showRightSidebar: false,
|
|
1746
1692
|
showTimestamps: false,
|
|
1747
1693
|
showObjective: true,
|
|
1748
1694
|
showScope: true,
|
|
@@ -1750,13 +1696,14 @@ var DEFAULT_SETTINGS = {
|
|
|
1750
1696
|
composerStyle: "border",
|
|
1751
1697
|
busyInputMode: "steer",
|
|
1752
1698
|
onboardingCompleted: false,
|
|
1699
|
+
executionProfile: "local",
|
|
1753
1700
|
allowSubagentPeerMessaging: true,
|
|
1754
1701
|
allowSubagentOperatorMessaging: true,
|
|
1755
1702
|
transcriptStyle: "minimal",
|
|
1756
|
-
roleLabelStyle: "
|
|
1703
|
+
roleLabelStyle: "off",
|
|
1757
1704
|
toolCardStyle: "compact",
|
|
1758
1705
|
richToolCards: true,
|
|
1759
|
-
transcriptDetail: "
|
|
1706
|
+
transcriptDetail: "collapsed",
|
|
1760
1707
|
autoCompaction: true,
|
|
1761
1708
|
compactionThreshold: "80%",
|
|
1762
1709
|
theme: DEFAULT_THEME_NAME,
|
|
@@ -1764,21 +1711,22 @@ var DEFAULT_SETTINGS = {
|
|
|
1764
1711
|
allowDevSourceUpdates: false,
|
|
1765
1712
|
autoEvolveFinderLenses: false,
|
|
1766
1713
|
autoPromoteFinderLenses: false,
|
|
1767
|
-
showTokenUsage:
|
|
1768
|
-
showCost:
|
|
1714
|
+
showTokenUsage: false,
|
|
1715
|
+
showCost: false,
|
|
1769
1716
|
showContextMeter: true,
|
|
1770
1717
|
modelDisplay: "statusbar",
|
|
1771
1718
|
elapsedTimer: "left",
|
|
1772
1719
|
logoAnimation: "glitch",
|
|
1773
1720
|
reduceMotion: false,
|
|
1774
|
-
analyticsLevel: "
|
|
1775
|
-
diagnosticReporting: "
|
|
1721
|
+
analyticsLevel: "off",
|
|
1722
|
+
diagnosticReporting: "ask",
|
|
1776
1723
|
diagnosticReportingPrompted: false,
|
|
1777
1724
|
updatePolicy: "automatic",
|
|
1778
1725
|
symbolPreset: "unicode",
|
|
1779
1726
|
rosterSort: "attention",
|
|
1780
1727
|
leaderKey: "off",
|
|
1781
|
-
keybindings: {}
|
|
1728
|
+
keybindings: {},
|
|
1729
|
+
modelPreference: null
|
|
1782
1730
|
};
|
|
1783
1731
|
var SETTINGS_FILENAME = "tui-settings.json";
|
|
1784
1732
|
function settingsFilePath(homeDir2) {
|
|
@@ -1816,6 +1764,9 @@ function strictValueAt(raw, key) {
|
|
|
1816
1764
|
if (key === "keybindings") {
|
|
1817
1765
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? sanitizeKeybindingOverrides(value) : void 0;
|
|
1818
1766
|
}
|
|
1767
|
+
if (key === "modelPreference") {
|
|
1768
|
+
return normalizeModelPreference(value);
|
|
1769
|
+
}
|
|
1819
1770
|
const def = DEF_BY_KEY.get(key);
|
|
1820
1771
|
if (def?.kind === "boolean") {
|
|
1821
1772
|
return typeof value === "boolean" ? value : void 0;
|
|
@@ -1824,7 +1775,7 @@ function strictValueAt(raw, key) {
|
|
|
1824
1775
|
return typeof value === "string" && choices.includes(value) ? value : void 0;
|
|
1825
1776
|
}
|
|
1826
1777
|
function isOperatorSetting(key) {
|
|
1827
|
-
return key === "onboardingCompleted" || key === "analyticsLevel" || key === "diagnosticReporting" || key === "diagnosticReportingPrompted" || key === "updatePolicy" || key === "allowDevSourceUpdates";
|
|
1778
|
+
return key === "onboardingCompleted" || key === "analyticsLevel" || key === "diagnosticReporting" || key === "diagnosticReportingPrompted" || key === "updatePolicy" || key === "allowDevSourceUpdates" || key === "executionProfile" || key === "modelPreference";
|
|
1828
1779
|
}
|
|
1829
1780
|
function resolveLayeredSettings(globalRaw, projectRaw) {
|
|
1830
1781
|
const settings = {};
|
|
@@ -1862,13 +1813,6 @@ function booleanAt(raw, key) {
|
|
|
1862
1813
|
const value = rawValue(raw, key);
|
|
1863
1814
|
return typeof value === "boolean" ? value : DEFAULT_SETTINGS[key];
|
|
1864
1815
|
}
|
|
1865
|
-
function booleanWithLegacy(raw, key, legacyKey) {
|
|
1866
|
-
const value = rawValue(raw, key);
|
|
1867
|
-
if (typeof value === "boolean") return value;
|
|
1868
|
-
const legacy = rawValue(raw, legacyKey);
|
|
1869
|
-
if (typeof legacy === "boolean") return legacy;
|
|
1870
|
-
return DEFAULT_SETTINGS[key];
|
|
1871
|
-
}
|
|
1872
1816
|
function enumAt(raw, key) {
|
|
1873
1817
|
const def = DEF_BY_KEY.get(key);
|
|
1874
1818
|
const value = rawValue(raw, key);
|
|
@@ -1882,6 +1826,13 @@ function themeAt(raw) {
|
|
|
1882
1826
|
function keybindingsAt(raw) {
|
|
1883
1827
|
return sanitizeKeybindingOverrides(rawValue(raw, "keybindings"));
|
|
1884
1828
|
}
|
|
1829
|
+
function normalizeModelPreference(raw) {
|
|
1830
|
+
if (raw === null) return null;
|
|
1831
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
|
|
1832
|
+
const value = raw;
|
|
1833
|
+
if (typeof value.providerId !== "string" || !value.providerId.trim() || typeof value.model !== "string" || !value.model.trim() || typeof value.connectionIdentity !== "string" || !/^[a-f0-9]{64}$/.test(value.connectionIdentity)) return void 0;
|
|
1834
|
+
return { providerId: value.providerId, model: value.model, connectionIdentity: value.connectionIdentity };
|
|
1835
|
+
}
|
|
1885
1836
|
function normalizeSettings(raw) {
|
|
1886
1837
|
return {
|
|
1887
1838
|
showStatusBar: booleanAt(raw, "showStatusBar"),
|
|
@@ -1892,10 +1843,6 @@ function normalizeSettings(raw) {
|
|
|
1892
1843
|
showRuntimeNotices: booleanAt(raw, "showRuntimeNotices"),
|
|
1893
1844
|
showTurnSummary: booleanAt(raw, "showTurnSummary"),
|
|
1894
1845
|
showSubagents: booleanAt(raw, "showSubagents"),
|
|
1895
|
-
showLeftSidebar: booleanAt(raw, "showLeftSidebar"),
|
|
1896
|
-
// Back-compat: the right sidebar was `showAgentRail` before it grew a
|
|
1897
|
-
// context strip and a left twin. A pre-rename file keeps its choice.
|
|
1898
|
-
showRightSidebar: booleanWithLegacy(raw, "showRightSidebar", "showAgentRail"),
|
|
1899
1846
|
showTimestamps: booleanAt(raw, "showTimestamps"),
|
|
1900
1847
|
showObjective: booleanAt(raw, "showObjective"),
|
|
1901
1848
|
showScope: booleanAt(raw, "showScope"),
|
|
@@ -1903,6 +1850,7 @@ function normalizeSettings(raw) {
|
|
|
1903
1850
|
composerStyle: enumAt(raw, "composerStyle"),
|
|
1904
1851
|
busyInputMode: enumAt(raw, "busyInputMode"),
|
|
1905
1852
|
onboardingCompleted: booleanAt(raw, "onboardingCompleted"),
|
|
1853
|
+
executionProfile: enumAt(raw, "executionProfile"),
|
|
1906
1854
|
allowSubagentPeerMessaging: booleanAt(raw, "allowSubagentPeerMessaging"),
|
|
1907
1855
|
allowSubagentOperatorMessaging: booleanAt(raw, "allowSubagentOperatorMessaging"),
|
|
1908
1856
|
transcriptStyle: enumAt(raw, "transcriptStyle"),
|
|
@@ -1931,25 +1879,37 @@ function normalizeSettings(raw) {
|
|
|
1931
1879
|
symbolPreset: enumAt(raw, "symbolPreset"),
|
|
1932
1880
|
rosterSort: enumAt(raw, "rosterSort"),
|
|
1933
1881
|
leaderKey: enumAt(raw, "leaderKey"),
|
|
1934
|
-
keybindings: keybindingsAt(raw)
|
|
1882
|
+
keybindings: keybindingsAt(raw),
|
|
1883
|
+
modelPreference: normalizeModelPreference(rawValue(raw, "modelPreference")) ?? null
|
|
1935
1884
|
};
|
|
1936
1885
|
}
|
|
1937
1886
|
function loadSettings(homeDir2, projectDir2) {
|
|
1938
1887
|
return loadLayeredSettings({ homeDir: homeDir2, projectDir: projectDir2 }).settings;
|
|
1939
1888
|
}
|
|
1940
|
-
function loadGlobalSettings(homeDir2) {
|
|
1889
|
+
function loadGlobalSettings(homeDir2, options = {}) {
|
|
1941
1890
|
syncThemeChoices(homeDir2);
|
|
1942
|
-
|
|
1891
|
+
const raw = readRawSettingsFile(settingsFilePath(homeDir2));
|
|
1892
|
+
if (options.requireExecutionProfile && strictValueAt(raw, "executionProfile") === void 0) {
|
|
1893
|
+
throw new Error("Configured workbench execution profile is missing or invalid. Run 0 workbench setup or 0 workbench disable to make an explicit operator choice; host fallback is refused.");
|
|
1894
|
+
}
|
|
1895
|
+
return normalizeSettings(raw);
|
|
1943
1896
|
}
|
|
1944
1897
|
function saveSettings(settings, homeDir2) {
|
|
1898
|
+
const path = settingsFilePath(homeDir2);
|
|
1899
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
1945
1900
|
try {
|
|
1946
|
-
const path = settingsFilePath(homeDir2);
|
|
1947
1901
|
mkdirSync2(dirname2(path), { recursive: true });
|
|
1948
|
-
writeFileSync2(
|
|
1949
|
-
`, "utf8");
|
|
1902
|
+
writeFileSync2(temporary, `${JSON.stringify(normalizeSettings(settings), null, 2)}
|
|
1903
|
+
`, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
1904
|
+
renameSync(temporary, path);
|
|
1950
1905
|
return true;
|
|
1951
1906
|
} catch {
|
|
1952
1907
|
return false;
|
|
1908
|
+
} finally {
|
|
1909
|
+
try {
|
|
1910
|
+
rmSync2(temporary, { force: true });
|
|
1911
|
+
} catch {
|
|
1912
|
+
}
|
|
1953
1913
|
}
|
|
1954
1914
|
}
|
|
1955
1915
|
function sanitizeOverrides(raw) {
|
|
@@ -2002,6 +1962,10 @@ function getSettings() {
|
|
|
2002
1962
|
}
|
|
2003
1963
|
return cached.settings;
|
|
2004
1964
|
}
|
|
1965
|
+
function getSettingSources() {
|
|
1966
|
+
getSettings();
|
|
1967
|
+
return cached.sources;
|
|
1968
|
+
}
|
|
2005
1969
|
function setSettings(next) {
|
|
2006
1970
|
const value = normalizeSettings(next);
|
|
2007
1971
|
const saved = saveSettings(value, homeDir);
|
|
@@ -2378,6 +2342,789 @@ function writePresentationLine(text, eventType = "output.stdout") {
|
|
|
2378
2342
|
`, eventType);
|
|
2379
2343
|
}
|
|
2380
2344
|
|
|
2345
|
+
// packages/cli/src/tui/provider-status.ts
|
|
2346
|
+
var PROVIDER_DEFS = [
|
|
2347
|
+
{
|
|
2348
|
+
id: "chatgpt-codex",
|
|
2349
|
+
label: "ChatGPT Codex",
|
|
2350
|
+
methods: ["oauth"],
|
|
2351
|
+
// OAuth, not an API key. Both tokens are accepted and the access token is
|
|
2352
|
+
// read first (llm-api.ts L874-875, L1386-1394), so it leads the list.
|
|
2353
|
+
envVars: ["ZERO_CHATGPT_ACCESS_TOKEN", "ZERO_CHATGPT_OAUTH_REFRESH_TOKEN"],
|
|
2354
|
+
fileSource: "~/.codex/auth.json (override with ZERO_CHATGPT_AUTH_FILE)",
|
|
2355
|
+
hint: "run `codex login` to write ~/.codex/auth.json, or invoke 0 with env ZERO_CHATGPT_OAUTH_REFRESH_TOKEN=..."
|
|
2356
|
+
},
|
|
2357
|
+
{
|
|
2358
|
+
id: "deepseek",
|
|
2359
|
+
label: "DeepSeek",
|
|
2360
|
+
methods: ["api-key"],
|
|
2361
|
+
envVars: ["DEEPSEEK_API_KEY"],
|
|
2362
|
+
hint: "set DEEPSEEK_API_KEY (endpoint override: DEEPSEEK_BASE_URL)"
|
|
2363
|
+
},
|
|
2364
|
+
{
|
|
2365
|
+
id: "openrouter",
|
|
2366
|
+
label: "OpenRouter",
|
|
2367
|
+
// OAuth (PKCE browser sign-in) is preferred; a pasted OPENROUTER_API_KEY
|
|
2368
|
+
// remains a secondary path. The browser flow provisions a durable
|
|
2369
|
+
// `sk-or-...` key and stores it as an api_key record written to
|
|
2370
|
+
// OPENROUTER_API_KEY (envVars[0], which llm-api already reads), so — unlike
|
|
2371
|
+
// the xai/kimi device flows — the credential is a key, not OAuth tokens.
|
|
2372
|
+
methods: ["oauth", "api-key"],
|
|
2373
|
+
envVars: ["OPENROUTER_API_KEY"],
|
|
2374
|
+
hint: "sign in with your OpenRouter account, or set OPENROUTER_API_KEY=sk-or-... from openrouter.ai/keys"
|
|
2375
|
+
},
|
|
2376
|
+
{
|
|
2377
|
+
id: "azure",
|
|
2378
|
+
label: "Azure OpenAI",
|
|
2379
|
+
methods: ["api-key"],
|
|
2380
|
+
// Only the Azure key authenticates; the deployment URL comes from
|
|
2381
|
+
// AZURE_OPENAI_BASE_URL / OPENAI_BASE_URL / ~/.codex/config.toml
|
|
2382
|
+
// (L1435-1445). A key with no reachable base URL still counts as
|
|
2383
|
+
// configured here because detectProvider selects azure on the key alone.
|
|
2384
|
+
envVars: ["AZURE_OPENAI_API_KEY"],
|
|
2385
|
+
hint: "set AZURE_OPENAI_API_KEY plus AZURE_OPENAI_BASE_URL (or [model_providers.azure] in ~/.codex/config.toml)"
|
|
2386
|
+
},
|
|
2387
|
+
{
|
|
2388
|
+
id: "openai",
|
|
2389
|
+
label: "OpenAI",
|
|
2390
|
+
methods: ["api-key"],
|
|
2391
|
+
envVars: ["OPENAI_API_KEY"],
|
|
2392
|
+
hint: "set OPENAI_API_KEY=sk-... from platform.openai.com/api-keys"
|
|
2393
|
+
},
|
|
2394
|
+
{
|
|
2395
|
+
id: "z-ai",
|
|
2396
|
+
label: "Z.ai GLM",
|
|
2397
|
+
methods: ["api-key"],
|
|
2398
|
+
envVars: ["Z_AI_API_KEY"],
|
|
2399
|
+
hint: "set Z_AI_API_KEY from your Z.ai Coding Plan (endpoint override: Z_AI_BASE_URL)"
|
|
2400
|
+
},
|
|
2401
|
+
{
|
|
2402
|
+
id: "kimi",
|
|
2403
|
+
label: "Moonshot Kimi",
|
|
2404
|
+
// OAuth (device sign-in) is preferred; a pasted KIMI_API_KEY remains a
|
|
2405
|
+
// secondary path. The OAuth access token is written to KIMI_API_KEY as a
|
|
2406
|
+
// Bearer (llm-api.ts reads it there, no change needed), and the refresh
|
|
2407
|
+
// token to the 0-owned var so the store can round-trip it.
|
|
2408
|
+
methods: ["oauth", "api-key"],
|
|
2409
|
+
envVars: ["KIMI_API_KEY", "ZERO_KIMI_OAUTH_REFRESH_TOKEN"],
|
|
2410
|
+
hint: "sign in with your Kimi account, or set KIMI_API_KEY from your Kimi coding plan (endpoint override: KIMI_BASE_URL)"
|
|
2411
|
+
},
|
|
2412
|
+
{
|
|
2413
|
+
id: "qwen",
|
|
2414
|
+
label: "Alibaba Qwen",
|
|
2415
|
+
methods: ["api-key"],
|
|
2416
|
+
envVars: ["QWEN_API_KEY"],
|
|
2417
|
+
hint: "set QWEN_API_KEY from Alibaba Model Studio (endpoint override: QWEN_BASE_URL)"
|
|
2418
|
+
},
|
|
2419
|
+
{
|
|
2420
|
+
id: "xai",
|
|
2421
|
+
label: "xAI Grok",
|
|
2422
|
+
// OAuth (device sign-in) is preferred; a pasted XAI_API_KEY remains a
|
|
2423
|
+
// secondary path. The OAuth access token is written to XAI_API_KEY as a
|
|
2424
|
+
// Bearer (llm-api.ts reads it there, no change needed), and the refresh
|
|
2425
|
+
// token to the 0-owned var so the store can round-trip it.
|
|
2426
|
+
methods: ["oauth", "api-key"],
|
|
2427
|
+
envVars: ["XAI_API_KEY", "ZERO_XAI_OAUTH_REFRESH_TOKEN"],
|
|
2428
|
+
hint: "sign in with your xAI account, or set XAI_API_KEY from console.x.ai (endpoint override: XAI_BASE_URL)"
|
|
2429
|
+
},
|
|
2430
|
+
{
|
|
2431
|
+
id: "opencode",
|
|
2432
|
+
label: "OpenCode Zen",
|
|
2433
|
+
methods: ["api-key"],
|
|
2434
|
+
envVars: ["OPENCODE_API_KEY"],
|
|
2435
|
+
hint: "set OPENCODE_API_KEY from opencode.ai/auth (endpoint override: OPENCODE_BASE_URL)"
|
|
2436
|
+
},
|
|
2437
|
+
{
|
|
2438
|
+
id: "copilot",
|
|
2439
|
+
label: "GitHub Copilot",
|
|
2440
|
+
// OAuth only (device-code sign-in). The GitHub device-flow access token is
|
|
2441
|
+
// written to ZERO_COPILOT_GITHUB_TOKEN (envVars[0]) and sent directly as a
|
|
2442
|
+
// Bearer to api.githubcopilot.com — no secondary exchange, no refresh, so
|
|
2443
|
+
// there is no refresh-token env var.
|
|
2444
|
+
methods: ["oauth"],
|
|
2445
|
+
envVars: ["ZERO_COPILOT_GITHUB_TOKEN"],
|
|
2446
|
+
hint: "sign in with your GitHub Copilot account (device sign-in), or set ZERO_COPILOT_GITHUB_TOKEN=... (endpoint override: COPILOT_BASE_URL)"
|
|
2447
|
+
},
|
|
2448
|
+
{
|
|
2449
|
+
id: "google",
|
|
2450
|
+
label: "Google Gemini (Code Assist)",
|
|
2451
|
+
// OAuth only (PKCE browser sign-in — the Gemini CLI flow). The minted
|
|
2452
|
+
// access token is written to ZERO_GEMINI_ACCESS_TOKEN (envVars[0]) and the
|
|
2453
|
+
// refresh token to ZERO_GEMINI_OAUTH_REFRESH_TOKEN (the /REFRESH/i var); the
|
|
2454
|
+
// runtime refreshes on demand against oauth2.googleapis.com. There is no
|
|
2455
|
+
// pasted-key equivalent — Code Assist authenticates only via OAuth.
|
|
2456
|
+
methods: ["oauth"],
|
|
2457
|
+
envVars: ["ZERO_GEMINI_ACCESS_TOKEN", "ZERO_GEMINI_OAUTH_REFRESH_TOKEN"],
|
|
2458
|
+
hint: "sign in with your Google account (browser sign-in), or set ZERO_GEMINI_OAUTH_REFRESH_TOKEN=... for Gemini Code Assist (project override: GOOGLE_CLOUD_PROJECT / ZERO_GEMINI_PROJECT)"
|
|
2459
|
+
},
|
|
2460
|
+
{
|
|
2461
|
+
id: "anthropic",
|
|
2462
|
+
label: "Anthropic",
|
|
2463
|
+
// API key only for now. The schema supports OAuth, but Anthropic/Claude
|
|
2464
|
+
// subscription OAuth is deliberately deferred — do not add "oauth" here
|
|
2465
|
+
// until that workstream lands.
|
|
2466
|
+
methods: ["api-key"],
|
|
2467
|
+
envVars: ["ANTHROPIC_API_KEY"],
|
|
2468
|
+
hint: "set ANTHROPIC_API_KEY=sk-ant-... from console.anthropic.com"
|
|
2469
|
+
}
|
|
2470
|
+
];
|
|
2471
|
+
function primaryMethod(methods) {
|
|
2472
|
+
return methods[0] ?? "api-key";
|
|
2473
|
+
}
|
|
2474
|
+
var PROVIDERS = PROVIDER_DEFS.map((def) => ({
|
|
2475
|
+
...def,
|
|
2476
|
+
auth: primaryMethod(def.methods)
|
|
2477
|
+
}));
|
|
2478
|
+
function providerSupportsMethod(providerId, method) {
|
|
2479
|
+
const info = PROVIDERS.find((candidate) => candidate.id === providerId);
|
|
2480
|
+
return info !== void 0 && info.methods.includes(method);
|
|
2481
|
+
}
|
|
2482
|
+
function hasCredential(value) {
|
|
2483
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
2484
|
+
}
|
|
2485
|
+
function satisfyingVar(info, env) {
|
|
2486
|
+
return info.envVars.find((name) => hasCredential(env[name]));
|
|
2487
|
+
}
|
|
2488
|
+
function providerStates(env) {
|
|
2489
|
+
return PROVIDERS.map((info) => {
|
|
2490
|
+
const via = satisfyingVar(info, env);
|
|
2491
|
+
return via === void 0 ? { ...info, configured: false } : { ...info, configured: true, via };
|
|
2492
|
+
});
|
|
2493
|
+
}
|
|
2494
|
+
function isProviderConfigured(providerId, env) {
|
|
2495
|
+
const info = PROVIDERS.find((candidate) => candidate.id === providerId);
|
|
2496
|
+
return info !== void 0 && satisfyingVar(info, env) !== void 0;
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
// packages/cli/src/tui/credential-store.ts
|
|
2500
|
+
import { chmodSync, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2501
|
+
import { dirname as dirname3, join as join5 } from "node:path";
|
|
2502
|
+
var CREDENTIALS_FILENAME = "credentials.json";
|
|
2503
|
+
var FILE_MODE = 384;
|
|
2504
|
+
var DIR_MODE = 448;
|
|
2505
|
+
function credentialsFilePath(homeDir2) {
|
|
2506
|
+
return join5(homeStateDir(homeDir2), CREDENTIALS_FILENAME);
|
|
2507
|
+
}
|
|
2508
|
+
var STORABLE_PROVIDER_IDS = new Set(
|
|
2509
|
+
PROVIDERS.filter((provider) => provider.methods.includes("api-key")).map((provider) => provider.id)
|
|
2510
|
+
);
|
|
2511
|
+
function hasCredential2(value) {
|
|
2512
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
2513
|
+
}
|
|
2514
|
+
function normalizeCredentials(raw) {
|
|
2515
|
+
const out = {};
|
|
2516
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return out;
|
|
2517
|
+
for (const [providerId, value] of Object.entries(raw)) {
|
|
2518
|
+
if (!STORABLE_PROVIDER_IDS.has(providerId)) continue;
|
|
2519
|
+
if (typeof value !== "string") continue;
|
|
2520
|
+
const secret = value.trim();
|
|
2521
|
+
if (secret.length === 0) continue;
|
|
2522
|
+
out[providerId] = secret;
|
|
2523
|
+
}
|
|
2524
|
+
return out;
|
|
2525
|
+
}
|
|
2526
|
+
function loadCredentials(homeDir2) {
|
|
2527
|
+
return projectApiKeys(loadAccountStore(homeDir2));
|
|
2528
|
+
}
|
|
2529
|
+
function saveCredentials(creds, homeDir2) {
|
|
2530
|
+
const store = loadAccountStore(homeDir2);
|
|
2531
|
+
const desired = normalizeCredentials(creds);
|
|
2532
|
+
return saveAccountStore(replaceApiKeyLayer(store, desired), homeDir2);
|
|
2533
|
+
}
|
|
2534
|
+
function projectApiKeys(store) {
|
|
2535
|
+
const out = {};
|
|
2536
|
+
for (const providerId of Object.keys(store.providers)) {
|
|
2537
|
+
const record = getActiveAccount(store, providerId);
|
|
2538
|
+
if (record?.kind === "api_key") out[providerId] = record.secret;
|
|
2539
|
+
}
|
|
2540
|
+
return out;
|
|
2541
|
+
}
|
|
2542
|
+
function replaceApiKeyLayer(store, desired) {
|
|
2543
|
+
const providers = {};
|
|
2544
|
+
for (const [providerId, entry] of Object.entries(store.providers)) {
|
|
2545
|
+
const kept = {};
|
|
2546
|
+
for (const [accountId, record] of Object.entries(entry.accounts)) {
|
|
2547
|
+
if (record.kind !== "api_key") kept[accountId] = record;
|
|
2548
|
+
}
|
|
2549
|
+
const keptIds = Object.keys(kept);
|
|
2550
|
+
if (keptIds.length === 0) continue;
|
|
2551
|
+
const active = entry.activeAccountId !== void 0 && kept[entry.activeAccountId] !== void 0 ? entry.activeAccountId : keptIds[0];
|
|
2552
|
+
providers[providerId] = { activeAccountId: active, accounts: kept };
|
|
2553
|
+
}
|
|
2554
|
+
for (const [providerId, secret] of Object.entries(desired)) {
|
|
2555
|
+
const existing = providers[providerId];
|
|
2556
|
+
const accounts = existing ? { ...existing.accounts } : {};
|
|
2557
|
+
accounts[DEFAULT_ACCOUNT_ID] = { kind: "api_key", secret };
|
|
2558
|
+
providers[providerId] = { activeAccountId: DEFAULT_ACCOUNT_ID, accounts };
|
|
2559
|
+
}
|
|
2560
|
+
return { version: STORE_VERSION, providers };
|
|
2561
|
+
}
|
|
2562
|
+
function credentialEnvPatch(creds, env) {
|
|
2563
|
+
const stored = normalizeCredentials(creds);
|
|
2564
|
+
const patch = {};
|
|
2565
|
+
for (const info of PROVIDERS) {
|
|
2566
|
+
const secret = stored[info.id];
|
|
2567
|
+
if (secret === void 0) continue;
|
|
2568
|
+
if (info.envVars.some((name) => hasCredential2(env[name]))) continue;
|
|
2569
|
+
const target = info.envVars[0];
|
|
2570
|
+
if (target === void 0) continue;
|
|
2571
|
+
patch[target] = secret;
|
|
2572
|
+
}
|
|
2573
|
+
return patch;
|
|
2574
|
+
}
|
|
2575
|
+
var MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
2576
|
+
var TAIL = 4;
|
|
2577
|
+
var PREFIX = 6;
|
|
2578
|
+
var MIN_LENGTH_FOR_TAIL = 12;
|
|
2579
|
+
var MIN_LENGTH_FOR_PREFIX = 20;
|
|
2580
|
+
function redactSecret(secret) {
|
|
2581
|
+
const value = typeof secret === "string" ? secret.trim() : "";
|
|
2582
|
+
if (value.length < MIN_LENGTH_FOR_TAIL) return MASK;
|
|
2583
|
+
const tail = value.slice(-TAIL);
|
|
2584
|
+
if (value.length < MIN_LENGTH_FOR_PREFIX) return `${MASK}${tail}`;
|
|
2585
|
+
return `${value.slice(0, PREFIX)}\u2026${tail}`;
|
|
2586
|
+
}
|
|
2587
|
+
var STORE_VERSION = 2;
|
|
2588
|
+
var DEFAULT_ACCOUNT_ID = "default";
|
|
2589
|
+
var KNOWN_PROVIDER_IDS = new Set(PROVIDERS.map((provider) => provider.id));
|
|
2590
|
+
function emptyStore() {
|
|
2591
|
+
return { version: STORE_VERSION, providers: {} };
|
|
2592
|
+
}
|
|
2593
|
+
function isPlainObject(value) {
|
|
2594
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2595
|
+
}
|
|
2596
|
+
function isSafeKey(key) {
|
|
2597
|
+
return key.length > 0 && key !== "__proto__" && key !== "constructor" && key !== "prototype";
|
|
2598
|
+
}
|
|
2599
|
+
function trimmedString(value) {
|
|
2600
|
+
if (typeof value !== "string") return void 0;
|
|
2601
|
+
const trimmed = value.trim();
|
|
2602
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
2603
|
+
}
|
|
2604
|
+
function normalizeTokens(raw) {
|
|
2605
|
+
if (!isPlainObject(raw)) return void 0;
|
|
2606
|
+
const tokens = {};
|
|
2607
|
+
const accessToken = trimmedString(raw.accessToken);
|
|
2608
|
+
const refreshToken = trimmedString(raw.refreshToken);
|
|
2609
|
+
if (accessToken !== void 0) tokens.accessToken = accessToken;
|
|
2610
|
+
if (refreshToken !== void 0) tokens.refreshToken = refreshToken;
|
|
2611
|
+
if (tokens.accessToken === void 0 && tokens.refreshToken === void 0) return void 0;
|
|
2612
|
+
if (typeof raw.expiresAt === "number" && Number.isFinite(raw.expiresAt)) tokens.expiresAt = raw.expiresAt;
|
|
2613
|
+
const tokenType = trimmedString(raw.tokenType);
|
|
2614
|
+
if (tokenType !== void 0) tokens.tokenType = tokenType;
|
|
2615
|
+
const scope = trimmedString(raw.scope);
|
|
2616
|
+
if (scope !== void 0) tokens.scope = scope;
|
|
2617
|
+
return tokens;
|
|
2618
|
+
}
|
|
2619
|
+
function normalizeIdentity(raw) {
|
|
2620
|
+
if (!isPlainObject(raw)) return void 0;
|
|
2621
|
+
const identity = {};
|
|
2622
|
+
const label = trimmedString(raw.label);
|
|
2623
|
+
const accountId = trimmedString(raw.accountId);
|
|
2624
|
+
const email = trimmedString(raw.email);
|
|
2625
|
+
if (label !== void 0) identity.label = label;
|
|
2626
|
+
if (accountId !== void 0) identity.accountId = accountId;
|
|
2627
|
+
if (email !== void 0) identity.email = email;
|
|
2628
|
+
return Object.keys(identity).length > 0 ? identity : void 0;
|
|
2629
|
+
}
|
|
2630
|
+
function normalizeRecord(providerId, raw) {
|
|
2631
|
+
if (!isPlainObject(raw)) return void 0;
|
|
2632
|
+
if (raw.kind === "api_key") {
|
|
2633
|
+
if (!providerSupportsMethod(providerId, "api-key")) return void 0;
|
|
2634
|
+
const secret = trimmedString(raw.secret);
|
|
2635
|
+
return secret === void 0 ? void 0 : { kind: "api_key", secret };
|
|
2636
|
+
}
|
|
2637
|
+
if (raw.kind === "oauth") {
|
|
2638
|
+
if (!providerSupportsMethod(providerId, "oauth")) return void 0;
|
|
2639
|
+
const tokens = normalizeTokens(raw.tokens);
|
|
2640
|
+
if (tokens === void 0) return void 0;
|
|
2641
|
+
const identity = normalizeIdentity(raw.identity);
|
|
2642
|
+
return identity === void 0 ? { kind: "oauth", tokens } : { kind: "oauth", tokens, identity };
|
|
2643
|
+
}
|
|
2644
|
+
return void 0;
|
|
2645
|
+
}
|
|
2646
|
+
function normalizeProviderAccounts(providerId, raw) {
|
|
2647
|
+
if (!isPlainObject(raw)) return void 0;
|
|
2648
|
+
const accountsRaw = isPlainObject(raw.accounts) ? raw.accounts : {};
|
|
2649
|
+
const accounts = {};
|
|
2650
|
+
for (const [accountId, recordRaw] of Object.entries(accountsRaw)) {
|
|
2651
|
+
if (!isSafeKey(accountId)) continue;
|
|
2652
|
+
const record = normalizeRecord(providerId, recordRaw);
|
|
2653
|
+
if (record !== void 0) accounts[accountId] = record;
|
|
2654
|
+
}
|
|
2655
|
+
const ids = Object.keys(accounts);
|
|
2656
|
+
if (ids.length === 0) return void 0;
|
|
2657
|
+
const active = typeof raw.activeAccountId === "string" && accounts[raw.activeAccountId] !== void 0 ? raw.activeAccountId : ids[0];
|
|
2658
|
+
return { activeAccountId: active, accounts };
|
|
2659
|
+
}
|
|
2660
|
+
function normalizeAccountStore(raw) {
|
|
2661
|
+
if (!isPlainObject(raw)) return emptyStore();
|
|
2662
|
+
const looksV2 = isPlainObject(raw.providers) || raw.version !== void 0;
|
|
2663
|
+
const store = emptyStore();
|
|
2664
|
+
if (looksV2) {
|
|
2665
|
+
const providersRaw = isPlainObject(raw.providers) ? raw.providers : {};
|
|
2666
|
+
for (const [providerId, entry] of Object.entries(providersRaw)) {
|
|
2667
|
+
if (!isSafeKey(providerId) || !KNOWN_PROVIDER_IDS.has(providerId)) continue;
|
|
2668
|
+
const accounts = normalizeProviderAccounts(providerId, entry);
|
|
2669
|
+
if (accounts !== void 0) store.providers[providerId] = accounts;
|
|
2670
|
+
}
|
|
2671
|
+
return store;
|
|
2672
|
+
}
|
|
2673
|
+
for (const [providerId, secret] of Object.entries(normalizeCredentials(raw))) {
|
|
2674
|
+
store.providers[providerId] = {
|
|
2675
|
+
activeAccountId: DEFAULT_ACCOUNT_ID,
|
|
2676
|
+
accounts: { [DEFAULT_ACCOUNT_ID]: { kind: "api_key", secret } }
|
|
2677
|
+
};
|
|
2678
|
+
}
|
|
2679
|
+
return store;
|
|
2680
|
+
}
|
|
2681
|
+
function loadAccountStore(homeDir2) {
|
|
2682
|
+
try {
|
|
2683
|
+
const text = readFileSync4(credentialsFilePath(homeDir2), "utf8");
|
|
2684
|
+
return normalizeAccountStore(JSON.parse(text));
|
|
2685
|
+
} catch {
|
|
2686
|
+
return emptyStore();
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
function saveAccountStore(store, homeDir2) {
|
|
2690
|
+
try {
|
|
2691
|
+
const path = credentialsFilePath(homeDir2);
|
|
2692
|
+
const dir = dirname3(path);
|
|
2693
|
+
mkdirSync3(dir, { recursive: true, mode: DIR_MODE });
|
|
2694
|
+
chmodSync(dir, DIR_MODE);
|
|
2695
|
+
writeFileSync3(path, `${JSON.stringify(normalizeAccountStore(store), null, 2)}
|
|
2696
|
+
`, {
|
|
2697
|
+
encoding: "utf8",
|
|
2698
|
+
mode: FILE_MODE
|
|
2699
|
+
});
|
|
2700
|
+
chmodSync(path, FILE_MODE);
|
|
2701
|
+
return true;
|
|
2702
|
+
} catch {
|
|
2703
|
+
return false;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
function getActiveAccount(store, providerId) {
|
|
2707
|
+
const entry = store.providers[providerId];
|
|
2708
|
+
if (entry === void 0) return void 0;
|
|
2709
|
+
const id = entry.activeAccountId;
|
|
2710
|
+
if (id !== void 0 && entry.accounts[id] !== void 0) return entry.accounts[id];
|
|
2711
|
+
const first = Object.keys(entry.accounts)[0];
|
|
2712
|
+
return first === void 0 ? void 0 : entry.accounts[first];
|
|
2713
|
+
}
|
|
2714
|
+
function chooseAccountId(record, requested) {
|
|
2715
|
+
if (requested !== void 0 && isSafeKey(requested)) return requested;
|
|
2716
|
+
if (record.kind === "oauth" && record.identity?.accountId !== void 0 && isSafeKey(record.identity.accountId)) {
|
|
2717
|
+
return record.identity.accountId;
|
|
2718
|
+
}
|
|
2719
|
+
if (record.kind === "api_key") return DEFAULT_ACCOUNT_ID;
|
|
2720
|
+
return `account-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2721
|
+
}
|
|
2722
|
+
function addAccount(store, providerId, record, options) {
|
|
2723
|
+
if (!KNOWN_PROVIDER_IDS.has(providerId)) {
|
|
2724
|
+
throw new Error(`unknown provider "${providerId}"`);
|
|
2725
|
+
}
|
|
2726
|
+
const method = record.kind === "api_key" ? "api-key" : "oauth";
|
|
2727
|
+
if (!providerSupportsMethod(providerId, method)) {
|
|
2728
|
+
throw new Error(`provider "${providerId}" does not support ${record.kind} accounts`);
|
|
2729
|
+
}
|
|
2730
|
+
const accountId = chooseAccountId(record, options?.accountId);
|
|
2731
|
+
const existing = store.providers[providerId];
|
|
2732
|
+
const accounts = { ...existing?.accounts ?? {} };
|
|
2733
|
+
accounts[accountId] = record;
|
|
2734
|
+
const makeActive = options?.makeActive ?? true;
|
|
2735
|
+
const activeAccountId = makeActive ? accountId : existing?.activeAccountId !== void 0 && accounts[existing.activeAccountId] !== void 0 ? existing.activeAccountId : accountId;
|
|
2736
|
+
return {
|
|
2737
|
+
store: { version: STORE_VERSION, providers: { ...store.providers, [providerId]: { activeAccountId, accounts } } },
|
|
2738
|
+
accountId
|
|
2739
|
+
};
|
|
2740
|
+
}
|
|
2741
|
+
function accountEnvPatch(store, env) {
|
|
2742
|
+
const normalized = normalizeAccountStore(store);
|
|
2743
|
+
const patch = {};
|
|
2744
|
+
for (const info of PROVIDERS) {
|
|
2745
|
+
const record = getActiveAccount(normalized, info.id);
|
|
2746
|
+
if (record === void 0) continue;
|
|
2747
|
+
if (info.envVars.some((name) => hasCredential2(env[name]))) continue;
|
|
2748
|
+
if (record.kind === "api_key") {
|
|
2749
|
+
const target = info.envVars[0];
|
|
2750
|
+
if (target !== void 0) patch[target] = record.secret;
|
|
2751
|
+
continue;
|
|
2752
|
+
}
|
|
2753
|
+
const accessVar = info.envVars[0];
|
|
2754
|
+
if (accessVar !== void 0 && record.tokens.accessToken !== void 0) {
|
|
2755
|
+
patch[accessVar] = record.tokens.accessToken;
|
|
2756
|
+
}
|
|
2757
|
+
if (record.tokens.refreshToken !== void 0) {
|
|
2758
|
+
const refreshVar = info.envVars.find((name) => /REFRESH/i.test(name));
|
|
2759
|
+
if (refreshVar !== void 0) patch[refreshVar] = record.tokens.refreshToken;
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
return patch;
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
// packages/cli/src/tui/feedback.ts
|
|
2766
|
+
import { appendFileSync, mkdirSync as mkdirSync4 } from "node:fs";
|
|
2767
|
+
import { dirname as dirname4, join as join6 } from "node:path";
|
|
2768
|
+
import { homedir as homedir2 } from "node:os";
|
|
2769
|
+
function feedbackFilePath(homeDir2) {
|
|
2770
|
+
return join6(homeDir2 ?? homedir2(), ".0", "feedback.md");
|
|
2771
|
+
}
|
|
2772
|
+
function formatFeedbackEntry(entry) {
|
|
2773
|
+
const context = [
|
|
2774
|
+
entry.version ? `version ${entry.version}` : null,
|
|
2775
|
+
entry.model ? `model ${entry.model}` : null,
|
|
2776
|
+
entry.mode ? `mode ${entry.mode}` : null
|
|
2777
|
+
].filter((part) => part !== null);
|
|
2778
|
+
const lines = [`## ${entry.timestamp}`];
|
|
2779
|
+
if (context.length > 0) lines.push(`_${context.join(" \xB7 ")}_`);
|
|
2780
|
+
lines.push("", entry.message.trim(), "");
|
|
2781
|
+
return `${lines.join("\n")}
|
|
2782
|
+
`;
|
|
2783
|
+
}
|
|
2784
|
+
function appendFeedback(entry, homeDir2) {
|
|
2785
|
+
const path = feedbackFilePath(homeDir2);
|
|
2786
|
+
try {
|
|
2787
|
+
mkdirSync4(dirname4(path), { recursive: true });
|
|
2788
|
+
appendFileSync(path, formatFeedbackEntry(entry), "utf8");
|
|
2789
|
+
return { ok: true, path };
|
|
2790
|
+
} catch (error) {
|
|
2791
|
+
return {
|
|
2792
|
+
ok: false,
|
|
2793
|
+
path,
|
|
2794
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2795
|
+
};
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
function parseFeedbackCommand(raw) {
|
|
2799
|
+
const text = raw.trim();
|
|
2800
|
+
if (!text) return { kind: "usage" };
|
|
2801
|
+
const separator = text.search(/\s/);
|
|
2802
|
+
const verb = separator < 0 ? text : text.slice(0, separator);
|
|
2803
|
+
const rest = separator < 0 ? "" : text.slice(separator).trim();
|
|
2804
|
+
if (verb === "submit") return rest ? { kind: "submit", message: rest } : { kind: "usage" };
|
|
2805
|
+
if (verb === "send") return rest ? { kind: "usage" } : { kind: "send" };
|
|
2806
|
+
if (verb === "cancel") return rest ? { kind: "usage" } : { kind: "cancel" };
|
|
2807
|
+
return { kind: "record", message: text };
|
|
2808
|
+
}
|
|
2809
|
+
var DEFAULT_FEEDBACK_URL = "";
|
|
2810
|
+
var FEEDBACK_URL_ENV = "ZERO_FEEDBACK_URL";
|
|
2811
|
+
var CLOUD_FEEDBACK_PATH = "/api/cli-feedback";
|
|
2812
|
+
function defaultCloudCredentials(env) {
|
|
2813
|
+
try {
|
|
2814
|
+
const credentials = loadCloudCredentials({
|
|
2815
|
+
env,
|
|
2816
|
+
warn: () => {
|
|
2817
|
+
}
|
|
2818
|
+
});
|
|
2819
|
+
return { host: credentials.host, token: credentials.token };
|
|
2820
|
+
} catch {
|
|
2821
|
+
return null;
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
function cloudFeedbackUrl(host) {
|
|
2825
|
+
try {
|
|
2826
|
+
const url = new URL(host);
|
|
2827
|
+
if (url.hostname === "cloud.0.ai") url.hostname = "cloud.0.security";
|
|
2828
|
+
url.pathname = CLOUD_FEEDBACK_PATH;
|
|
2829
|
+
url.search = "";
|
|
2830
|
+
url.hash = "";
|
|
2831
|
+
return url.toString();
|
|
2832
|
+
} catch {
|
|
2833
|
+
return null;
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
function resolveFeedbackTarget(env = process.env, options = {}) {
|
|
2837
|
+
const configured = env[FEEDBACK_URL_ENV]?.trim();
|
|
2838
|
+
if (configured) return { url: configured };
|
|
2839
|
+
if (DEFAULT_FEEDBACK_URL) return { url: DEFAULT_FEEDBACK_URL };
|
|
2840
|
+
if (options.allowCloud === false) return null;
|
|
2841
|
+
const credentials = (options.cloudCredentials ?? (() => defaultCloudCredentials(env)))();
|
|
2842
|
+
if (!credentials) return null;
|
|
2843
|
+
const url = cloudFeedbackUrl(credentials.host);
|
|
2844
|
+
return url ? { url, authorization: `Bearer ${credentials.token}` } : null;
|
|
2845
|
+
}
|
|
2846
|
+
var FEEDBACK_OPT_OUT_ENV = ["ZERO_OFFLINE", "ZERO_NO_TELEMETRY", "DO_NOT_TRACK"];
|
|
2847
|
+
var FEEDBACK_TIMEOUT_MS = 5e3;
|
|
2848
|
+
var MAX_BODY_BYTES = 64 * 1024;
|
|
2849
|
+
function isOptOutSet(value) {
|
|
2850
|
+
if (value === void 0) return false;
|
|
2851
|
+
const normalized = value.trim().toLowerCase();
|
|
2852
|
+
if (normalized === "") return false;
|
|
2853
|
+
return normalized !== "0" && normalized !== "false" && normalized !== "no";
|
|
2854
|
+
}
|
|
2855
|
+
function submissionBlockedReason(env = process.env, options = {}) {
|
|
2856
|
+
for (const name of FEEDBACK_OPT_OUT_ENV) {
|
|
2857
|
+
if (isOptOutSet(env[name])) return "opt-out";
|
|
2858
|
+
}
|
|
2859
|
+
return targetBlockedReason(resolveFeedbackTarget(env, options));
|
|
2860
|
+
}
|
|
2861
|
+
function targetBlockedReason(target) {
|
|
2862
|
+
if (target === null) return "no-endpoint";
|
|
2863
|
+
let parsed;
|
|
2864
|
+
try {
|
|
2865
|
+
parsed = new URL(target.url);
|
|
2866
|
+
} catch {
|
|
2867
|
+
return "insecure-endpoint";
|
|
2868
|
+
}
|
|
2869
|
+
if (parsed.protocol !== "https:") return "insecure-endpoint";
|
|
2870
|
+
return null;
|
|
2871
|
+
}
|
|
2872
|
+
function describeSkip(reason) {
|
|
2873
|
+
switch (reason) {
|
|
2874
|
+
case "opt-out":
|
|
2875
|
+
return `Submission disabled by ${FEEDBACK_OPT_OUT_ENV.join(" / ")}. Saved locally only.`;
|
|
2876
|
+
case "no-endpoint":
|
|
2877
|
+
return `No feedback endpoint configured (set ${FEEDBACK_URL_ENV}). Saved locally only.`;
|
|
2878
|
+
case "insecure-endpoint":
|
|
2879
|
+
return `Refusing a non-HTTPS ${FEEDBACK_URL_ENV}. Saved locally only.`;
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
var SECRET_RULES = [
|
|
2883
|
+
{ label: "an OpenAI/Anthropic-style key (sk-\u2026)", pattern: /\bsk-[A-Za-z0-9_-]{16,}/ },
|
|
2884
|
+
{ label: "a GitHub token (ghp_/gho_/ghu_/ghs_/ghr_\u2026)", pattern: /\bgh[pousr]_[A-Za-z0-9]{20,}/ },
|
|
2885
|
+
{ label: "an AWS access key id (AKIA\u2026/ASIA\u2026)", pattern: /\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\b/ },
|
|
2886
|
+
{ label: "a Google API key (AIza\u2026)", pattern: /\bAIza[0-9A-Za-z_-]{35}\b/ },
|
|
2887
|
+
{ label: "a Slack token (xox\u2026)", pattern: /\bxox[abprs]-[A-Za-z0-9-]{10,}/ },
|
|
2888
|
+
{
|
|
2889
|
+
label: "an Authorization header",
|
|
2890
|
+
pattern: /authorization\s*[:=]\s*(?:bearer|basic|token|digest)\s+\S+/i
|
|
2891
|
+
},
|
|
2892
|
+
{ label: "a JWT (eyJ\u2026)", pattern: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]+/ },
|
|
2893
|
+
{
|
|
2894
|
+
label: "a PEM private key block",
|
|
2895
|
+
pattern: /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY-----/
|
|
2896
|
+
},
|
|
2897
|
+
{
|
|
2898
|
+
label: "a credential-shaped assignment (password=/api_key=/secret=\u2026)",
|
|
2899
|
+
pattern: /\b(?:pass(?:word|wd)?|api[_-]?key|secret|token|credentials?)\s*[:=]\s*\S{6,}/i
|
|
2900
|
+
},
|
|
2901
|
+
{
|
|
2902
|
+
// Long opaque runs. Requires mixed case *and* a digit so ordinary prose,
|
|
2903
|
+
// file paths, and hyphenated identifiers do not trip it.
|
|
2904
|
+
label: "a long high-entropy string (base64-ish)",
|
|
2905
|
+
pattern: /(?=[A-Za-z0-9+/_-]{40,})(?=[A-Za-z0-9+/_-]*[a-z])(?=[A-Za-z0-9+/_-]*[A-Z])(?=[A-Za-z0-9+/_-]*[0-9])[A-Za-z0-9+/_-]{40,}={0,2}/
|
|
2906
|
+
}
|
|
2907
|
+
];
|
|
2908
|
+
function scanForSecrets(message) {
|
|
2909
|
+
const warnings = [];
|
|
2910
|
+
for (const rule of SECRET_RULES) {
|
|
2911
|
+
if (rule.pattern.test(message)) warnings.push(`Message appears to contain ${rule.label}.`);
|
|
2912
|
+
}
|
|
2913
|
+
return warnings;
|
|
2914
|
+
}
|
|
2915
|
+
function serializePayload(payload) {
|
|
2916
|
+
const body = {
|
|
2917
|
+
message: payload.message,
|
|
2918
|
+
timestamp: payload.timestamp
|
|
2919
|
+
};
|
|
2920
|
+
if (payload.version !== void 0) body.version = payload.version;
|
|
2921
|
+
if (payload.model !== void 0) body.model = payload.model;
|
|
2922
|
+
if (payload.mode !== void 0) body.mode = payload.mode;
|
|
2923
|
+
return JSON.stringify(body);
|
|
2924
|
+
}
|
|
2925
|
+
function requestHeaders(target, redactAuthorization = false) {
|
|
2926
|
+
const headers = { "content-type": "application/json" };
|
|
2927
|
+
if (target.authorization) {
|
|
2928
|
+
headers.authorization = redactAuthorization ? "Bearer <redacted>" : target.authorization;
|
|
2929
|
+
}
|
|
2930
|
+
return headers;
|
|
2931
|
+
}
|
|
2932
|
+
function buildSubmitPreview(payload, env = process.env, options = {}) {
|
|
2933
|
+
if (submissionBlockedReason(env, options) !== null) return null;
|
|
2934
|
+
const target = resolveFeedbackTarget(env, options);
|
|
2935
|
+
if (target === null) return null;
|
|
2936
|
+
return {
|
|
2937
|
+
url: target.url,
|
|
2938
|
+
body: serializePayload(payload),
|
|
2939
|
+
headers: requestHeaders(target, true),
|
|
2940
|
+
warnings: scanForSecrets(payload.message)
|
|
2941
|
+
};
|
|
2942
|
+
}
|
|
2943
|
+
async function submitFeedback(payload, env = process.env, opts = {}) {
|
|
2944
|
+
const blocked = submissionBlockedReason(env, opts);
|
|
2945
|
+
if (blocked !== null) return { ok: false, skipped: blocked, error: describeSkip(blocked) };
|
|
2946
|
+
const target = resolveFeedbackTarget(env, opts);
|
|
2947
|
+
const targetBlocked = targetBlockedReason(target);
|
|
2948
|
+
if (targetBlocked !== null) return { ok: false, skipped: targetBlocked, error: describeSkip(targetBlocked) };
|
|
2949
|
+
if (target === null) return { ok: false, skipped: "no-endpoint", error: describeSkip("no-endpoint") };
|
|
2950
|
+
const body = serializePayload(payload);
|
|
2951
|
+
if (Buffer.byteLength(body, "utf8") > MAX_BODY_BYTES) {
|
|
2952
|
+
return { ok: false, error: `Message too large to submit (limit ${MAX_BODY_BYTES} bytes).` };
|
|
2953
|
+
}
|
|
2954
|
+
const reviewed = opts.expectedPreview;
|
|
2955
|
+
if (reviewed !== void 0) {
|
|
2956
|
+
const currentHeaders = requestHeaders(target, true);
|
|
2957
|
+
if (target.url !== reviewed.url || body !== reviewed.body || Object.keys(currentHeaders).length !== Object.keys(reviewed.headers).length || !Object.keys(currentHeaders).every((k) => currentHeaders[k] === reviewed.headers[k])) {
|
|
2958
|
+
return {
|
|
2959
|
+
ok: false,
|
|
2960
|
+
error: "Reviewed feedback destination has changed. Preview the updated submission before sending again."
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
2965
|
+
const timeoutMs = opts.timeoutMs ?? FEEDBACK_TIMEOUT_MS;
|
|
2966
|
+
const controller = new AbortController();
|
|
2967
|
+
let timer;
|
|
2968
|
+
const timeout = new Promise((resolve) => {
|
|
2969
|
+
timer = setTimeout(() => {
|
|
2970
|
+
controller.abort();
|
|
2971
|
+
resolve({ ok: false, error: `Feedback submission timed out after ${timeoutMs}ms.` });
|
|
2972
|
+
}, timeoutMs);
|
|
2973
|
+
timer.unref?.();
|
|
2974
|
+
});
|
|
2975
|
+
const attempt = (async () => {
|
|
2976
|
+
try {
|
|
2977
|
+
const response = await doFetch(target.url, {
|
|
2978
|
+
method: "POST",
|
|
2979
|
+
headers: requestHeaders(target),
|
|
2980
|
+
body,
|
|
2981
|
+
signal: controller.signal,
|
|
2982
|
+
redirect: "error"
|
|
2983
|
+
});
|
|
2984
|
+
return response.ok ? { ok: true, status: response.status } : { ok: false, status: response.status, error: `Endpoint returned ${response.status}.` };
|
|
2985
|
+
} catch (error) {
|
|
2986
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
2987
|
+
}
|
|
2988
|
+
})();
|
|
2989
|
+
try {
|
|
2990
|
+
return await Promise.race([attempt, timeout]);
|
|
2991
|
+
} finally {
|
|
2992
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
var DIAGNOSTIC_PLATFORMS = /* @__PURE__ */ new Set(["darwin", "linux", "win32", "aix", "freebsd", "openbsd", "sunos"]);
|
|
2996
|
+
var DIAGNOSTIC_ARCHS = /* @__PURE__ */ new Set(["x64", "arm64", "arm", "ia32", "s390", "mips", "ppc64"]);
|
|
2997
|
+
var DIAGNOSTIC_RUNTIMES = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
|
|
2998
|
+
function classifyFailureText(text) {
|
|
2999
|
+
const t = text.toLowerCase();
|
|
3000
|
+
if (/exited?\s+-?\d+|exit code|non-?zero exit/.test(t)) return "nonzero-exit";
|
|
3001
|
+
if (/enoent|no such file|command not found|not found on path|cannot find (?:module|the )/.test(t)) return "not-found";
|
|
3002
|
+
if (/etimedout|timed out|\btimeout\b|deadline exceeded/.test(t)) return "timeout";
|
|
3003
|
+
if (/econnrefused|econnreset|epipe|network|fetch failed|socket hang up|getaddrinfo|dns|tls|certificate/.test(t)) return "network";
|
|
3004
|
+
if (/eacces|eperm|permission denied|forbidden|unauthor|401|403/.test(t)) return "permission";
|
|
3005
|
+
if (/rate.?limit|too many requests|\b429\b|quota|overloaded/.test(t)) return "rate-limit";
|
|
3006
|
+
if (/unexpected token|json|yaml|parse error|invalid json|malformed/.test(t)) return "parse";
|
|
3007
|
+
if (/out of memory|enomem|heap|maximum call stack/.test(t)) return "resource";
|
|
3008
|
+
if (/stream completed without final response|response stream failed|incomplete/.test(t)) return "stream-incomplete";
|
|
3009
|
+
return null;
|
|
3010
|
+
}
|
|
3011
|
+
function diagnosticError(error) {
|
|
3012
|
+
try {
|
|
3013
|
+
if (error instanceof TypeError) return "TypeError";
|
|
3014
|
+
if (error instanceof ReferenceError) return "ReferenceError";
|
|
3015
|
+
if (error instanceof SyntaxError) return "SyntaxError";
|
|
3016
|
+
if (error instanceof RangeError) return "RangeError";
|
|
3017
|
+
if (error instanceof URIError) return "URIError";
|
|
3018
|
+
if (error instanceof EvalError) return "EvalError";
|
|
3019
|
+
if (error instanceof Error) {
|
|
3020
|
+
const mode = classifyFailureText(error.message ?? "");
|
|
3021
|
+
return mode ? `Error:${mode}` : "Error";
|
|
3022
|
+
}
|
|
3023
|
+
if (typeof error === "string") {
|
|
3024
|
+
return classifyFailureText(error) ?? "tool-error";
|
|
3025
|
+
}
|
|
3026
|
+
} catch {
|
|
3027
|
+
}
|
|
3028
|
+
return "unknown";
|
|
3029
|
+
}
|
|
3030
|
+
function diagnosticVersion(raw) {
|
|
3031
|
+
if (typeof raw !== "string" || raw.length > 128) return "unknown";
|
|
3032
|
+
return /^v?(\d{1,6}\.\d{1,6}\.\d{1,6})(?:[-+][a-zA-Z0-9.+-]+)?$/.exec(raw)?.[1] ?? "unknown";
|
|
3033
|
+
}
|
|
3034
|
+
function buildDiagnosticFeedback(info, env = process.env) {
|
|
3035
|
+
const kind = info.kind === "tool" ? "tool" : info.kind === "runtime" ? "runtime" : "unknown";
|
|
3036
|
+
const platform = DIAGNOSTIC_PLATFORMS.has(info.platform) ? info.platform : "unknown";
|
|
3037
|
+
const arch = DIAGNOSTIC_ARCHS.has(info.arch) ? info.arch : "unknown";
|
|
3038
|
+
const runtime = DIAGNOSTIC_RUNTIMES.has(info.runtime) ? info.runtime : "unknown";
|
|
3039
|
+
const runtimeVersion = diagnosticVersion(info.runtimeVersion);
|
|
3040
|
+
const version = diagnosticVersion(info.version);
|
|
3041
|
+
const timestamp = typeof info.timestamp === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(info.timestamp) && Number.isFinite(Date.parse(info.timestamp)) ? info.timestamp : (/* @__PURE__ */ new Date()).toISOString();
|
|
3042
|
+
let message = `Diagnostic: ${kind} error \u2014 ${platform}/${arch} on ${runtime} ${runtimeVersion}
|
|
3043
|
+
Version: ${version}
|
|
3044
|
+
Error: ${diagnosticError(info.error)}`;
|
|
3045
|
+
if (diagnosticDetailAllowed(env)) {
|
|
3046
|
+
const detail = redactDiagnosticDetail(buildDiagnosticDetailText(info));
|
|
3047
|
+
if (detail) message = capUtf8Bytes(`${message}
|
|
3048
|
+
|
|
3049
|
+
${detail}`, MAX_DIAGNOSTIC_DETAIL_MESSAGE_BYTES);
|
|
3050
|
+
}
|
|
3051
|
+
return { message, timestamp, version };
|
|
3052
|
+
}
|
|
3053
|
+
var MAX_DIAGNOSTIC_DETAIL_MESSAGE_BYTES = 8192;
|
|
3054
|
+
var MAX_DIAGNOSTIC_DETAIL_CHARS = 6e3;
|
|
3055
|
+
function diagnosticDetailAllowed(env) {
|
|
3056
|
+
const level = (env["ZERO_ANALYTICS_LEVEL"] ?? "").trim().toLowerCase();
|
|
3057
|
+
return level === "commands" || level === "full";
|
|
3058
|
+
}
|
|
3059
|
+
function buildDiagnosticDetailText(info) {
|
|
3060
|
+
const parts = [];
|
|
3061
|
+
if (info.toolName) parts.push(`Tool: ${info.toolName}`);
|
|
3062
|
+
const err = info.error;
|
|
3063
|
+
if (err instanceof Error) {
|
|
3064
|
+
parts.push(`${err.name}: ${err.message || "(no message)"}`);
|
|
3065
|
+
if (typeof err.stack === "string" && err.stack.trim()) parts.push(err.stack.trim());
|
|
3066
|
+
} else {
|
|
3067
|
+
const text = diagnosticErrorText(err);
|
|
3068
|
+
if (text) parts.push(text);
|
|
3069
|
+
}
|
|
3070
|
+
const exitOutput = typeof info.exitOutput === "string" ? info.exitOutput.trim() : "";
|
|
3071
|
+
if (exitOutput && !parts.some((p) => p.includes(exitOutput))) parts.push(exitOutput);
|
|
3072
|
+
let detail = parts.join("\n").trim();
|
|
3073
|
+
if (detail.length > MAX_DIAGNOSTIC_DETAIL_CHARS) detail = detail.slice(0, MAX_DIAGNOSTIC_DETAIL_CHARS - 1) + "\u2026";
|
|
3074
|
+
return detail;
|
|
3075
|
+
}
|
|
3076
|
+
var DETAIL_REDACTIONS = [
|
|
3077
|
+
{ re: /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9 ]+ )?PRIVATE KEY-----/g, to: "\u2039redacted:private-key\u203A" },
|
|
3078
|
+
{ re: /\bsk-[A-Za-z0-9_-]{16,}/g, to: "\u2039redacted:key\u203A" },
|
|
3079
|
+
{ re: /\bgh[pousr]_[A-Za-z0-9]{20,}/g, to: "\u2039redacted:gh-token\u203A" },
|
|
3080
|
+
{ re: /\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\b/g, to: "\u2039redacted:aws-key\u203A" },
|
|
3081
|
+
{ re: /\bAIza[0-9A-Za-z_-]{35}\b/g, to: "\u2039redacted:gcp-key\u203A" },
|
|
3082
|
+
{ re: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g, to: "\u2039redacted:slack-token\u203A" },
|
|
3083
|
+
{ re: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]+/g, to: "\u2039redacted:jwt\u203A" },
|
|
3084
|
+
{ re: /authorization\s*[:=]\s*(?:bearer|basic|token|digest)\s+\S+/gi, to: "authorization: \u2039redacted\u203A" },
|
|
3085
|
+
{ re: /\b((?:pass(?:word|wd)?|api[_-]?key|secret|token|credentials?)\s*[:=]\s*)\S{6,}/gi, to: "$1\u2039redacted\u203A" },
|
|
3086
|
+
// Connection strings: keep the scheme/host shape, drop the inline credentials.
|
|
3087
|
+
// MUST run before the email rule so `user:pass@host` is not mis-read as an
|
|
3088
|
+
// email (which would swallow the host too).
|
|
3089
|
+
{ re: /([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/gi, to: "$1\u2039redacted\u203A@" },
|
|
3090
|
+
{ re: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, to: "\u2039redacted:email\u203A" },
|
|
3091
|
+
// Home-dir usernames in file paths — keep the path shape, anonymise the user.
|
|
3092
|
+
{ re: /(\/home\/)[^/\s]+/g, to: "$1\u2039user\u203A" },
|
|
3093
|
+
{ re: /(\/Users\/)[^/\s]+/g, to: "$1\u2039user\u203A" },
|
|
3094
|
+
{ re: /([A-Za-z]:\\Users\\)[^\\\s]+/g, to: "$1\u2039user\u203A" }
|
|
3095
|
+
];
|
|
3096
|
+
function redactDiagnosticDetail(text) {
|
|
3097
|
+
if (typeof text !== "string" || text.length === 0) return "";
|
|
3098
|
+
try {
|
|
3099
|
+
let out = text;
|
|
3100
|
+
for (const { re, to } of DETAIL_REDACTIONS) out = out.replace(re, to);
|
|
3101
|
+
return out;
|
|
3102
|
+
} catch {
|
|
3103
|
+
return "";
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
function capUtf8Bytes(text, maxBytes) {
|
|
3107
|
+
if (Buffer.byteLength(text, "utf8") <= maxBytes) return text;
|
|
3108
|
+
let lo = 0, hi = text.length;
|
|
3109
|
+
while (lo < hi) {
|
|
3110
|
+
const mid = Math.ceil((lo + hi) / 2);
|
|
3111
|
+
if (Buffer.byteLength(text.slice(0, mid), "utf8") <= maxBytes - 1) lo = mid;
|
|
3112
|
+
else hi = mid - 1;
|
|
3113
|
+
}
|
|
3114
|
+
return text.slice(0, lo) + "\u2026";
|
|
3115
|
+
}
|
|
3116
|
+
function diagnosticErrorText(error) {
|
|
3117
|
+
if (typeof error === "string") return error.trim();
|
|
3118
|
+
if (error instanceof Error) return (error.message || error.name || "Error").trim();
|
|
3119
|
+
if (error === null || error === void 0) return "";
|
|
3120
|
+
try {
|
|
3121
|
+
const text = String(error).trim();
|
|
3122
|
+
return text === "[object Object]" ? "" : text;
|
|
3123
|
+
} catch {
|
|
3124
|
+
return "";
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
|
|
2381
3128
|
export {
|
|
2382
3129
|
KEYBINDINGS,
|
|
2383
3130
|
keybindingsByCategory,
|
|
@@ -2419,6 +3166,7 @@ export {
|
|
|
2419
3166
|
readProjectOverrides,
|
|
2420
3167
|
saveProjectOverrides,
|
|
2421
3168
|
getSettings,
|
|
3169
|
+
getSettingSources,
|
|
2422
3170
|
updateSetting,
|
|
2423
3171
|
resetSettings,
|
|
2424
3172
|
previewSetting,
|
|
@@ -2435,5 +3183,24 @@ export {
|
|
|
2435
3183
|
suspendProcessPresentationStreamBridge,
|
|
2436
3184
|
resumeProcessPresentationStreamBridge,
|
|
2437
3185
|
processPresentationOutput,
|
|
2438
|
-
writePresentationLine
|
|
3186
|
+
writePresentationLine,
|
|
3187
|
+
PROVIDERS,
|
|
3188
|
+
providerStates,
|
|
3189
|
+
isProviderConfigured,
|
|
3190
|
+
loadCredentials,
|
|
3191
|
+
saveCredentials,
|
|
3192
|
+
credentialEnvPatch,
|
|
3193
|
+
redactSecret,
|
|
3194
|
+
loadAccountStore,
|
|
3195
|
+
saveAccountStore,
|
|
3196
|
+
addAccount,
|
|
3197
|
+
accountEnvPatch,
|
|
3198
|
+
appendFeedback,
|
|
3199
|
+
parseFeedbackCommand,
|
|
3200
|
+
FEEDBACK_OPT_OUT_ENV,
|
|
3201
|
+
submissionBlockedReason,
|
|
3202
|
+
describeSkip,
|
|
3203
|
+
buildSubmitPreview,
|
|
3204
|
+
submitFeedback,
|
|
3205
|
+
buildDiagnosticFeedback
|
|
2439
3206
|
};
|