@wrongstack/cli 1.0.24 → 1.0.26

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.
Files changed (124) hide show
  1. package/data/providers.json +44 -0
  2. package/dist/{acp-AJWSKWE5.js → acp-LI665LO2.js} +5 -5
  3. package/dist/ascii-mode-SG4PRNLP.js +7 -0
  4. package/dist/{auth-GRBZ7Y3D.js → auth-TE55JF4Z.js} +14 -14
  5. package/dist/{bench-N5K7Z5O3.js → bench-GTLPM22B.js} +2 -2
  6. package/dist/boot/append-system-prompt.d.ts +10 -0
  7. package/dist/boot/ascii-mode.d.ts +12 -0
  8. package/dist/boot/container-wiring.d.ts +4 -2
  9. package/dist/boot/dispatch-singleshot.d.ts +4 -1
  10. package/dist/boot/mcp-config-flag.d.ts +22 -0
  11. package/dist/boot/piped-stdin.d.ts +40 -0
  12. package/dist/boot/restricted-mode.d.ts +15 -0
  13. package/dist/boot/safe-mode.d.ts +30 -0
  14. package/dist/boot/single-shot-budget.d.ts +30 -0
  15. package/dist/boot/stream-json.d.ts +36 -0
  16. package/dist/boot/structured-output.d.ts +16 -0
  17. package/dist/boot/system-prompt-builder.d.ts +2 -0
  18. package/dist/boot/tool-restriction-flags.d.ts +36 -0
  19. package/dist/boot/worktree-sessions.d.ts +21 -0
  20. package/dist/boot-provider-selection.d.ts +67 -0
  21. package/dist/boot.d.ts +1 -38
  22. package/dist/{chunk-LFMVHT3R.js → chunk-2ERX3RPN.js} +11 -13
  23. package/dist/chunk-4TVYGG6G.js +62 -0
  24. package/dist/chunk-5JYDKG5B.js +154 -0
  25. package/dist/{chunk-3VXYY5JC.js → chunk-5UZBUV2H.js} +3 -19
  26. package/dist/chunk-BEHGRNOQ.js +58 -0
  27. package/dist/chunk-CZTXF6HY.js +140 -0
  28. package/dist/{chunk-SKPDE62W.js → chunk-EPR42SWM.js} +36 -6
  29. package/dist/{chunk-YBUCXXYZ.js → chunk-F5DCFAKI.js} +1 -1
  30. package/dist/chunk-HLSHFXTO.js +97 -0
  31. package/dist/{chunk-BLD5DSVA.js → chunk-KJS5543T.js} +2 -2
  32. package/dist/{chunk-QFPQXKCG.js → chunk-KOEO4CJG.js} +21 -6
  33. package/dist/chunk-LBPLTTZ6.js +105 -0
  34. package/dist/{chunk-3OCUS3VN.js → chunk-LDCGZJTC.js} +4 -4
  35. package/dist/chunk-LKPEBZBE.js +143 -0
  36. package/dist/{chunk-DRM7PSW7.js → chunk-OAVBM55Q.js} +3 -3
  37. package/dist/chunk-PZQBGUVZ.js +18 -0
  38. package/dist/{chunk-MHEBWVXL.js → chunk-Q4INH2DG.js} +46 -2
  39. package/dist/{chunk-ZZRSG7VK.js → chunk-RZ4ZDRYA.js} +3 -3
  40. package/dist/{chunk-U46QR4WR.js → chunk-TI7KLHYX.js} +132 -3
  41. package/dist/{chunk-PGKJ2OQP.js → chunk-WQV2NP57.js} +5 -5
  42. package/dist/chunk-X756ZS72.js +329 -0
  43. package/dist/{chunk-JF2MULWF.js → chunk-YHYINL47.js} +47 -15
  44. package/dist/{cli-context-34Q5IJRA.js → cli-context-UVGAN3NO.js} +407 -327
  45. package/dist/{cli-main-AELJDBGU.js → cli-main-TN3KZ2P6.js} +2652 -1978
  46. package/dist/cli-update-notice.d.ts +2 -1
  47. package/dist/config-doctor.d.ts +1 -1
  48. package/dist/{diag-doctor-WIFJH3XH.js → diag-doctor-BIZAFIAT.js} +2 -2
  49. package/dist/{execution-PYC5FCS4.js → execution-7QIP6GRY.js} +1948 -1666
  50. package/dist/fleet/host-types.d.ts +3 -1
  51. package/dist/goal-commands.d.ts +47 -0
  52. package/dist/goal-host.d.ts +1 -14
  53. package/dist/{hq-JTUTFI4L.js → hq-GLZ7QESM.js} +6 -6
  54. package/dist/{hq-server-PC7GZMPT.js → hq-server-YTKDVQPY.js} +2 -2
  55. package/dist/{hq-service-NW56MUP2.js → hq-service-BMMJAHRT.js} +4 -4
  56. package/dist/import-claude-code-32PCSIQI.js +236 -0
  57. package/dist/index.js +12 -8
  58. package/dist/{mailbox-serve-WDOCH6KR.js → mailbox-serve-NW5VZG7R.js} +3 -3
  59. package/dist/{modeldiag-36OGAL5C.js → modeldiag-36VODVIH.js} +8 -8
  60. package/dist/{per-subcommand-help-XE77MTGJ.js → per-subcommand-help-QDAQASPZ.js} +2 -2
  61. package/dist/permission-prompt.d.ts +14 -0
  62. package/dist/permission-rules-view.d.ts +18 -0
  63. package/dist/{permissions-CFDLXETM.js → permissions-PL27UHYD.js} +67 -45
  64. package/dist/{plugin-usage-S5LVGMBW.js → plugin-usage-CIGOOTX3.js} +2 -2
  65. package/dist/{plugins-RHBIVLI7.js → plugins-HQTXUD6A.js} +4 -2
  66. package/dist/preflight.d.ts +1 -1
  67. package/dist/{projects-LMNAGGEU.js → projects-T4CZCA25.js} +3 -6
  68. package/dist/provider-helpers.d.ts +7 -11
  69. package/dist/{providers-models-2CFG46RD.js → providers-models-5Z2PJTEW.js} +5 -5
  70. package/dist/release-asset-download.d.ts +10 -0
  71. package/dist/release-attestation.d.ts +28 -0
  72. package/dist/remote/remote-session.d.ts +64 -0
  73. package/dist/remote/remote-target.d.ts +69 -0
  74. package/dist/remote-O5WXHLWV.js +561 -0
  75. package/dist/{replay-Z3FTJEMM.js → replay-DEI566CW.js} +2 -2
  76. package/dist/services/commit-message.d.ts +2 -1
  77. package/dist/session-move-P2EC2COA.js +44 -0
  78. package/dist/session-move.d.ts +20 -0
  79. package/dist/session-stats.d.ts +10 -0
  80. package/dist/{sessions-config-IQUH2GXT.js → sessions-config-SXTTLNXA.js} +35 -5
  81. package/dist/short-circuit-flags-74Y5N6CF.js +10 -0
  82. package/dist/slash-commands/command-context.d.ts +15 -5
  83. package/dist/slash-commands/goal-refiner.d.ts +4 -49
  84. package/dist/slash-commands/mcp.d.ts +2 -1
  85. package/dist/slash-commands/permissions.d.ts +4 -0
  86. package/dist/slash-commands/settings-context-mutations.d.ts +5 -0
  87. package/dist/slash-commands/settings-limits-mutations.d.ts +12 -0
  88. package/dist/standalone-auto-update-TOTRGCSA.js +249 -0
  89. package/dist/standalone-auto-update.d.ts +65 -0
  90. package/dist/standalone-update-LZO3MNLH.js +24 -0
  91. package/dist/standalone-update.d.ts +37 -1
  92. package/dist/subcommands/handlers/import-claude-code.d.ts +42 -0
  93. package/dist/subcommands/handlers/permissions.d.ts +9 -3
  94. package/dist/subcommands/handlers/remote.d.ts +6 -0
  95. package/dist/subcommands/handlers/version-help.d.ts +5 -0
  96. package/dist/{subcommands-ZTSIN7LG.js → subcommands-Q6ORSB2E.js} +2 -2
  97. package/dist/tui-resume-callback.d.ts +18 -0
  98. package/dist/{typesafe-HVEVPA6R.js → typesafe-U7YB2RRV.js} +5 -5
  99. package/dist/{typesafe-topic-replay-4W73BOLV.js → typesafe-topic-replay-ZILIU3WS.js} +18 -2
  100. package/dist/{update-7XIOEXTD.js → update-TDZX4IL2.js} +4 -4
  101. package/dist/{update-check-2POMBHFB.js → update-check-J5ZVKS73.js} +3 -3
  102. package/dist/version-JB2UAV2A.js +11 -0
  103. package/dist/version-help-XJJQ7YSH.js +10 -0
  104. package/dist/webui-server/route-contexts.d.ts +1 -1
  105. package/dist/webui-server/session-start-payload.d.ts +5 -1
  106. package/dist/{webui-server-L2SICPTB.js → webui-server-YTHLRFD2.js} +509 -366
  107. package/dist/webui-server-lifecycle.d.ts +57 -0
  108. package/dist/webui-server.d.ts +0 -14
  109. package/dist/wiring/brain-and-orchestration.d.ts +3 -1
  110. package/dist/wiring/cli-prompt-and-tools-setup.d.ts +9 -0
  111. package/dist/wiring/cli-slash-commands-setup.d.ts +1 -0
  112. package/dist/wiring/lifecycle-plugins.d.ts +4 -2
  113. package/dist/wiring/metrics.d.ts +9 -1
  114. package/dist/wiring/pipeline.d.ts +2 -7
  115. package/dist/wiring/plugins.d.ts +3 -0
  116. package/dist/wiring/provider-status.d.ts +1 -0
  117. package/dist/wiring/provider-utility-tools.d.ts +3 -1
  118. package/package.json +28 -28
  119. package/dist/chunk-2BKFWIER.js +0 -98
  120. package/dist/chunk-424ORSE7.js +0 -56
  121. package/dist/chunk-QBBF7GIK.js +0 -65
  122. package/dist/short-circuit-flags-SDT53OML.js +0 -10
  123. package/dist/standalone-update-MW47J3AK.js +0 -164
  124. package/dist/version-help-WZ623NWM.js +0 -10
@@ -2,6 +2,115 @@ import {
2
2
  theme
3
3
  } from "./chunk-QD544J2B.js";
4
4
 
5
+ // src/boot/tool-restriction-flags.ts
6
+ function parseToolList(flag, value) {
7
+ if (value === void 0) return void 0;
8
+ if (value === true || typeof value === "string" && !value.trim()) {
9
+ throw new Error(`--${flag} needs a comma-separated list of tool names`);
10
+ }
11
+ const names = String(value).split(/[\s,]+/).map((name) => name.trim()).filter(Boolean);
12
+ const scoped = names.find((name) => name.includes("("));
13
+ if (scoped) {
14
+ throw new Error(
15
+ `--${flag}: scoped rules like "${scoped}" are not supported; list whole tool names (exec policy lives in config tools.exec)`
16
+ );
17
+ }
18
+ return names;
19
+ }
20
+ function resolveLaunchAllowedTools(flags) {
21
+ return parseToolList("allowed-tools", flags["allowed-tools"]);
22
+ }
23
+ function resolveToolRestriction(flags) {
24
+ const only = parseToolList("only-tools", flags["only-tools"]);
25
+ const deny = parseToolList("disallowed-tools", flags["disallowed-tools"]);
26
+ if (only === void 0 && deny === void 0) return void 0;
27
+ return { ...only ? { only } : {}, deny: deny ?? [] };
28
+ }
29
+ function unknownRestrictedToolNames(restriction, isRegistered) {
30
+ return [...restriction.only ?? [], ...restriction.deny].filter(
31
+ (name) => !name.endsWith("*") && !name.includes("__") && !isRegistered(name)
32
+ );
33
+ }
34
+
35
+ // src/boot/restricted-mode.ts
36
+ import { lockToProjectRoot, lockYoloOff, ToolCapabilities } from "@wrongstack/core/security";
37
+ var RESTRICTED_ENV = "WRONGSTACK_RESTRICTED";
38
+ var RESTRICTED_DENY_CAPABILITIES = [
39
+ ToolCapabilities.SHELL_ARBITRARY,
40
+ ToolCapabilities.SHELL_RESTRICTED,
41
+ ToolCapabilities.SHELL_EXEC,
42
+ ToolCapabilities.NET_OUTBOUND,
43
+ ToolCapabilities.PACKAGE_INSTALL,
44
+ ToolCapabilities.CONFIG_MUTATE,
45
+ ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
46
+ ToolCapabilities.TOOL_MUTATE_ANY,
47
+ ToolCapabilities.MCP_PROXY
48
+ ];
49
+ var CONFLICTING_FLAGS = [
50
+ "yolo",
51
+ "yolo-destructive",
52
+ "full-auto",
53
+ "mcp-config",
54
+ // Pre-approving a tool would let writes skip the prompt this mode promises.
55
+ "allowed-tools"
56
+ ];
57
+ function isRestrictedMode(flags) {
58
+ return flags["restricted"] === true || process.env[RESTRICTED_ENV] === "1";
59
+ }
60
+ function validateRestrictedMode(flags) {
61
+ if (!isRestrictedMode(flags)) return false;
62
+ const conflict = CONFLICTING_FLAGS.find(
63
+ (name) => flags[name] !== void 0 && flags[name] !== false
64
+ );
65
+ if (conflict) throw new Error(`--${conflict} cannot be combined with --restricted`);
66
+ return true;
67
+ }
68
+ function withRestrictedTools(restriction, restricted) {
69
+ if (!restricted) return restriction;
70
+ return {
71
+ ...restriction?.only ? { only: restriction.only } : {},
72
+ deny: [...restriction?.deny ?? [], "mcp__*"],
73
+ denyCapabilities: RESTRICTED_DENY_CAPABILITIES,
74
+ requireDeclaredCapabilities: true
75
+ };
76
+ }
77
+ var RESTRICTED_NOTICE = "Restricted mode: no shell, network, installs or MCP tools; files stay inside the project; YOLO is off.";
78
+ var announced = false;
79
+ function activateRestrictedMode(flags, write) {
80
+ if (!validateRestrictedMode(flags)) return false;
81
+ lockToProjectRoot();
82
+ lockYoloOff();
83
+ process.env[RESTRICTED_ENV] = "1";
84
+ if (!announced) {
85
+ announced = true;
86
+ write(`${RESTRICTED_NOTICE}
87
+ `);
88
+ }
89
+ return true;
90
+ }
91
+
92
+ // src/boot/safe-mode.ts
93
+ var SAFE_MODE_ENV = "WRONGSTACK_SAFE_MODE";
94
+ function isSafeMode(flags) {
95
+ return flags["safe-mode"] === true || process.env[SAFE_MODE_ENV] === "1";
96
+ }
97
+ function propagateSafeMode(flags) {
98
+ const on = isSafeMode(flags);
99
+ if (on) process.env[SAFE_MODE_ENV] = "1";
100
+ return on;
101
+ }
102
+ var SAFE_MODE_NOTICE = "Safe mode: third-party plugins, hooks, configured MCP servers, skills and instruction overrides are off.";
103
+ var announced2 = false;
104
+ function announceSafeMode(flags, write) {
105
+ const on = propagateSafeMode(flags);
106
+ if (on && !announced2) {
107
+ announced2 = true;
108
+ write(`${SAFE_MODE_NOTICE}
109
+ `);
110
+ }
111
+ return on;
112
+ }
113
+
5
114
  // src/diff-renderer.ts
6
115
  import { color, sanitizeTerminalText } from "@wrongstack/core/utils";
7
116
  function diffLineStyle(line) {
@@ -25,6 +134,15 @@ import {
25
134
  unifiedDiff,
26
135
  writeOut
27
136
  } from "@wrongstack/core/utils";
137
+ function makeHeadlessPromptDelegate(write = (text) => process.stderr.write(text)) {
138
+ return async (tool) => {
139
+ write(
140
+ `${theme.warn("\u26A0")} ${tool.name} needs approval but no terminal is attached to ask \u2014 denied. Grant it up front with --allowed-tools ${tool.name} or --yolo.
141
+ `
142
+ );
143
+ return "no";
144
+ };
145
+ }
28
146
  function makePromptDelegate(reader) {
29
147
  return async (tool, input, suggestedPattern, signal) => {
30
148
  if (signal?.aborted) return "no";
@@ -57,8 +175,11 @@ ${theme.warn("\u26A0 APPROVAL REQUIRED")} ${theme.primary("\u2502")} ${theme.bol
57
175
  return answer;
58
176
  };
59
177
  }
178
+ function makeStdinPromptDelegate(reader, stdinIsTTY = process.stdin.isTTY === true) {
179
+ return stdinIsTTY ? makePromptDelegate(reader) : makeHeadlessPromptDelegate();
180
+ }
60
181
  function makeConfirmAwaiter(reader) {
61
- const delegate = makePromptDelegate(reader);
182
+ const delegate = makeStdinPromptDelegate(reader);
62
183
  return async (tool, input, _toolUseId, suggestedPattern) => {
63
184
  const result = await delegate(tool, input, suggestedPattern);
64
185
  return result;
@@ -470,12 +591,20 @@ on disconnect with exponential backoff, up to 3 attempts"._
470
591
 
471
592
  export {
472
593
  renderDiff,
473
- makePromptDelegate,
594
+ makeStdinPromptDelegate,
474
595
  makeConfirmAwaiter,
596
+ resolveLaunchAllowedTools,
597
+ resolveToolRestriction,
598
+ unknownRestrictedToolNames,
599
+ isRestrictedMode,
600
+ withRestrictedTools,
601
+ activateRestrictedMode,
602
+ isSafeMode,
603
+ announceSafeMode,
475
604
  isSimpleUiFullAuto,
476
605
  applySimpleUiFullAutoProfile,
477
606
  configureSimpleUiRuntimeContext,
478
607
  detectProjectFacts,
479
608
  renderAgentsTemplate
480
609
  };
481
- //# sourceMappingURL=chunk-U46QR4WR.js.map
610
+ //# sourceMappingURL=chunk-TI7KLHYX.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  helpCmd,
3
3
  versionCmd
4
- } from "./chunk-2BKFWIER.js";
4
+ } from "./chunk-5JYDKG5B.js";
5
5
  import {
6
6
  parseArgs
7
- } from "./chunk-MHEBWVXL.js";
7
+ } from "./chunk-Q4INH2DG.js";
8
8
 
9
9
  // src/boot/short-circuit-flags.ts
10
10
  async function handleHelpVersionShortCircuit(argv) {
@@ -22,11 +22,11 @@ async function handleHelpVersionShortCircuit(argv) {
22
22
  }
23
23
  if (earlyFlags["help"] === true) {
24
24
  if (positional.length > 0) {
25
- const { subcommandNames } = await import("./subcommands-ZTSIN7LG.js");
25
+ const { subcommandNames } = await import("./subcommands-Q6ORSB2E.js");
26
26
  if (subcommandNames.includes(positional[0])) {
27
27
  const first = positional[0];
28
28
  const deep = positional[1];
29
- const { renderDeepHelp, renderFocusedHelp } = await import("./per-subcommand-help-XE77MTGJ.js");
29
+ const { renderDeepHelp, renderFocusedHelp } = await import("./per-subcommand-help-QDAQASPZ.js");
30
30
  if (deep && renderDeepHelp(`${first}:${deep}`, stubRenderer)) {
31
31
  return 0;
32
32
  }
@@ -43,4 +43,4 @@ async function handleHelpVersionShortCircuit(argv) {
43
43
  export {
44
44
  handleHelpVersionShortCircuit
45
45
  };
46
- //# sourceMappingURL=chunk-PGKJ2OQP.js.map
46
+ //# sourceMappingURL=chunk-WQV2NP57.js.map
@@ -0,0 +1,329 @@
1
+ import {
2
+ STANDALONE_TARGET
3
+ } from "./chunk-F5DCFAKI.js";
4
+
5
+ // src/standalone-update.ts
6
+ import { createHash as createHash2, randomBytes } from "node:crypto";
7
+ import * as fs from "node:fs";
8
+ import * as path from "node:path";
9
+ import { FetchError } from "@wrongstack/core/types";
10
+
11
+ // src/release-asset-download.ts
12
+ import { createHash } from "node:crypto";
13
+ import * as fsp from "node:fs/promises";
14
+ async function downloadReleaseAsset(url, timeoutMs, maxBytes) {
15
+ const res = await fetch(url, {
16
+ signal: AbortSignal.timeout(timeoutMs),
17
+ headers: { "User-Agent": "wrongstack-cli" },
18
+ redirect: "follow"
19
+ });
20
+ if (!res.ok) throw new Error(`download ${url} responded ${res.status}`);
21
+ const declared = Number(res.headers.get("content-length") ?? "0");
22
+ if (declared > maxBytes) throw new Error(`download ${url} is too large`);
23
+ if (!res.body) return Buffer.alloc(0);
24
+ const chunks = [];
25
+ let received = 0;
26
+ const reader = res.body.getReader();
27
+ try {
28
+ while (true) {
29
+ const { done, value } = await reader.read();
30
+ if (done) break;
31
+ received += value.byteLength;
32
+ if (received > maxBytes) {
33
+ await reader.cancel();
34
+ throw new Error(`download ${url} is too large`);
35
+ }
36
+ chunks.push(Buffer.from(value.buffer, value.byteOffset, value.byteLength));
37
+ }
38
+ } finally {
39
+ reader.releaseLock();
40
+ }
41
+ return Buffer.concat(chunks, received);
42
+ }
43
+ async function downloadReleaseAssetToFile(url, file, options) {
44
+ const timeout = AbortSignal.timeout(options.timeoutMs);
45
+ const res = await fetch(url, {
46
+ signal: options.signal ? AbortSignal.any([options.signal, timeout]) : timeout,
47
+ headers: { "User-Agent": "wrongstack-cli" },
48
+ redirect: "follow"
49
+ });
50
+ if (!res.ok) throw new Error(`download ${url} responded ${res.status}`);
51
+ const declared = Number(res.headers.get("content-length") ?? "0");
52
+ if (declared > options.maxBytes) throw new Error(`download ${url} is too large`);
53
+ const hash = createHash("sha256");
54
+ const out = await fsp.open(file, "wx", 384);
55
+ let received = 0;
56
+ try {
57
+ if (res.body) {
58
+ const reader = res.body.getReader();
59
+ try {
60
+ while (true) {
61
+ const { done, value } = await reader.read();
62
+ if (done) break;
63
+ received += value.byteLength;
64
+ if (received > options.maxBytes) {
65
+ await reader.cancel();
66
+ throw new Error(`download ${url} is too large`);
67
+ }
68
+ hash.update(value);
69
+ await out.write(value);
70
+ }
71
+ } finally {
72
+ reader.releaseLock();
73
+ }
74
+ }
75
+ await out.sync();
76
+ } catch (err) {
77
+ await out.close().catch(() => void 0);
78
+ await fsp.rm(file, { force: true });
79
+ throw err;
80
+ }
81
+ await out.close();
82
+ return hash.digest("hex");
83
+ }
84
+
85
+ // src/release-attestation.ts
86
+ var RELEASES_REPO = "WrongStack/WrongStack";
87
+ var RELEASES_REPO_ID = 1237163046;
88
+ var REPO_URL = `https://github.com/${RELEASES_REPO}`;
89
+ var IN_TOTO_PAYLOAD = "application/vnd.in-toto+json";
90
+ var IN_TOTO_STATEMENT = "https://in-toto.io/Statement/v1";
91
+ var SLSA_PROVENANCE = "https://slsa.dev/provenance/v1";
92
+ var obj = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
93
+ function statementOf(attestation) {
94
+ const envelope = obj(obj(attestation["bundle"])?.["dsseEnvelope"]);
95
+ if (envelope?.["payloadType"] !== IN_TOTO_PAYLOAD) return void 0;
96
+ const payload = envelope["payload"];
97
+ if (typeof payload !== "string") return void 0;
98
+ try {
99
+ return obj(JSON.parse(Buffer.from(payload, "base64").toString("utf8")));
100
+ } catch {
101
+ return void 0;
102
+ }
103
+ }
104
+ function vouches(attestation, subject) {
105
+ if (attestation["repository_id"] !== RELEASES_REPO_ID) return false;
106
+ const statement = statementOf(attestation);
107
+ if (statement?.["_type"] !== IN_TOTO_STATEMENT) return false;
108
+ if (statement["predicateType"] !== SLSA_PROVENANCE) return false;
109
+ const subjects = Array.isArray(statement["subject"]) ? statement["subject"] : [];
110
+ const named = subjects.some((entry) => {
111
+ const s = obj(entry);
112
+ return s?.["name"] === subject.assetName && obj(s["digest"])?.["sha256"] === subject.sha256;
113
+ });
114
+ if (!named) return false;
115
+ const tag = `refs/tags/v${subject.version}`;
116
+ const predicate = obj(statement["predicate"]);
117
+ const build = obj(predicate?.["buildDefinition"]);
118
+ const workflow = obj(obj(build?.["externalParameters"])?.["workflow"]);
119
+ const github = obj(obj(build?.["internalParameters"])?.["github"]);
120
+ const builder = obj(obj(predicate?.["runDetails"])?.["builder"]);
121
+ return workflow?.["repository"] === REPO_URL && workflow["path"] === ".github/workflows/release.yml" && workflow["ref"] === tag && builder?.["id"] === `${REPO_URL}/.github/workflows/binaries.yml@${tag}` && github?.["event_name"] === "push" && github["runner_environment"] === "github-hosted";
122
+ }
123
+ function attestationsVouchFor(response, subject) {
124
+ const list = obj(response)?.["attestations"];
125
+ if (!Array.isArray(list)) return false;
126
+ return list.some((entry) => {
127
+ const attestation = obj(entry);
128
+ return attestation !== void 0 && vouches(attestation, subject);
129
+ });
130
+ }
131
+ async function verifyReleaseAttestation(subject, signal) {
132
+ const url = `https://api.github.com/repos/${RELEASES_REPO}/attestations/sha256:${subject.sha256}`;
133
+ const timeout = AbortSignal.timeout(3e4);
134
+ const res = await fetch(url, {
135
+ signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
136
+ headers: { Accept: "application/vnd.github+json", "User-Agent": "wrongstack-cli" }
137
+ });
138
+ if (res.status === 404) {
139
+ throw new Error(`no build attestation exists for ${subject.assetName}`);
140
+ }
141
+ if (!res.ok) {
142
+ const limited = res.status === 403 || res.status === 429;
143
+ throw new Error(
144
+ limited ? "GitHub API rate limit reached while checking the build attestation; try again later" : `GitHub attestations API responded ${res.status}`
145
+ );
146
+ }
147
+ if (!attestationsVouchFor(await res.json(), subject)) {
148
+ throw new Error(
149
+ `${subject.assetName} has no attestation from the v${subject.version} release workflow`
150
+ );
151
+ }
152
+ }
153
+
154
+ // src/standalone-update.ts
155
+ var API_LATEST = `https://api.github.com/repos/${RELEASES_REPO}/releases/latest`;
156
+ var MAX_BINARY_BYTES = 512 * 1024 * 1024;
157
+ var MAX_CHECKSUM_BYTES = 1024 * 1024;
158
+ function standaloneAssetName(target) {
159
+ const name = `wstack-${target.replace(/^bun-/, "")}`;
160
+ return target.startsWith("bun-windows") ? `${name}.exe` : name;
161
+ }
162
+ async function fetchLatestRelease(timeoutMs, signal) {
163
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
164
+ const res = await fetch(API_LATEST, {
165
+ signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal,
166
+ headers: { Accept: "application/vnd.github+json", "User-Agent": "wrongstack-cli" }
167
+ });
168
+ if (!res.ok) {
169
+ throw new FetchError({
170
+ message: `GitHub releases responded ${res.status}`,
171
+ status: res.status,
172
+ context: { op: "checkForUpdate", registry: "github-releases", url: API_LATEST }
173
+ });
174
+ }
175
+ const data = await res.json();
176
+ if (typeof data.tag_name !== "string") throw new Error("Latest release has no tag");
177
+ const assets = Array.isArray(data.assets) ? data.assets.filter(
178
+ (a) => typeof a?.name === "string" && typeof a?.browser_download_url === "string" && (a.digest === void 0 || a.digest === null || typeof a.digest === "string")
179
+ ) : [];
180
+ return { version: data.tag_name.replace(/^v/, ""), assets };
181
+ }
182
+ async function findStandaloneRelease(target, signal) {
183
+ const release = await fetchLatestRelease(15e3, signal);
184
+ const assetName = standaloneAssetName(target);
185
+ const asset = release.assets.find((a) => a.name === assetName);
186
+ const sums = release.assets.find((a) => a.name === "SHA256SUMS");
187
+ if (!asset || !sums) throw new Error(`release v${release.version} has no ${assetName} build`);
188
+ return { version: release.version, assetName, asset, sums };
189
+ }
190
+ async function verifyStandaloneDigest(release, sha256, signal) {
191
+ const sumsText = (await downloadReleaseAsset(release.sums.browser_download_url, 3e4, MAX_CHECKSUM_BYTES)).toString("utf8");
192
+ if (parseSha256Sums(sumsText).get(release.assetName) !== sha256) {
193
+ throw new Error("checksum does not match the release SHA256SUMS");
194
+ }
195
+ const digest = release.asset.digest;
196
+ if (typeof digest === "string" && digest !== `sha256:${sha256}`) {
197
+ throw new Error("checksum does not match the digest GitHub recorded for the asset");
198
+ }
199
+ await verifyReleaseAttestation(
200
+ { assetName: release.assetName, sha256, version: release.version },
201
+ signal
202
+ );
203
+ }
204
+ async function fetchLatestStandaloneVersion(timeoutMs = 3e3, signal) {
205
+ return (await fetchLatestRelease(timeoutMs, signal)).version;
206
+ }
207
+ function parseSha256Sums(text) {
208
+ const sums = /* @__PURE__ */ new Map();
209
+ for (const line of text.split(/\r?\n/)) {
210
+ const match = line.trim().match(/^([0-9a-f]{64})\s+\*?(.+)$/i);
211
+ if (match?.[1] && match[2]) {
212
+ const name = match[2].trim();
213
+ if (sums.has(name)) throw new Error(`Duplicate SHA256SUMS entry: ${name}`);
214
+ sums.set(name, match[1].toLowerCase());
215
+ }
216
+ }
217
+ return sums;
218
+ }
219
+ function replaceExecutable(target, bytes, platform = process.platform) {
220
+ const staged = stagingPath(target);
221
+ fs.writeFileSync(staged, bytes, { mode: 493, flag: "wx" });
222
+ swapIntoPlace(target, staged, platform);
223
+ }
224
+ function installExecutableFrom(target, source, sha256, platform = process.platform) {
225
+ const staged = stagingPath(target);
226
+ fs.copyFileSync(source, staged, fs.constants.COPYFILE_EXCL);
227
+ try {
228
+ if (sha256File(staged) !== sha256) throw new Error("staged update does not match its digest");
229
+ fs.chmodSync(staged, 493);
230
+ } catch (error) {
231
+ fs.rmSync(staged, { force: true });
232
+ throw error;
233
+ }
234
+ swapIntoPlace(target, staged, platform);
235
+ }
236
+ function stagingPath(target) {
237
+ return `${target}.new-${randomBytes(9).toString("hex")}`;
238
+ }
239
+ function sha256File(file) {
240
+ const hash = createHash2("sha256");
241
+ const fd = fs.openSync(file, "r");
242
+ try {
243
+ const chunk = Buffer.allocUnsafe(1024 * 1024);
244
+ let read = fs.readSync(fd, chunk);
245
+ while (read > 0) {
246
+ hash.update(chunk.subarray(0, read));
247
+ read = fs.readSync(fd, chunk);
248
+ }
249
+ } finally {
250
+ fs.closeSync(fd);
251
+ }
252
+ return hash.digest("hex");
253
+ }
254
+ function swapIntoPlace(target, staged, platform) {
255
+ try {
256
+ if (platform === "win32") {
257
+ const aside = `${target}.old`;
258
+ fs.rmSync(aside, { force: true });
259
+ fs.renameSync(target, aside);
260
+ try {
261
+ fs.renameSync(staged, target);
262
+ } catch (error) {
263
+ fs.renameSync(aside, target);
264
+ throw error;
265
+ }
266
+ } else {
267
+ fs.renameSync(staged, target);
268
+ }
269
+ } finally {
270
+ fs.rmSync(staged, { force: true });
271
+ }
272
+ }
273
+ async function updateStandaloneBinary(options) {
274
+ const { renderer } = options;
275
+ if (!STANDALONE_TARGET) {
276
+ renderer.write("Update failed: this executable does not record its build target.\n");
277
+ return 1;
278
+ }
279
+ try {
280
+ const release = await findStandaloneRelease(STANDALONE_TARGET);
281
+ renderer.write(`Updating wrongstack from v${options.current} to v${release.version}...
282
+ `);
283
+ const executable = fs.realpathSync(process.execPath);
284
+ const staged = options.staged;
285
+ if (staged && staged.version === release.version) {
286
+ renderer.write(`Installing the v${staged.version} build already downloaded
287
+ `);
288
+ installExecutableFrom(executable, staged.file, staged.sha256);
289
+ } else {
290
+ renderer.write(`Downloading ${release.assetName}
291
+ `);
292
+ const bytes = await downloadReleaseAsset(
293
+ release.asset.browser_download_url,
294
+ 6e5,
295
+ MAX_BINARY_BYTES
296
+ );
297
+ renderer.write("Verifying the checksum and the build attestation\n");
298
+ await verifyStandaloneDigest(release, createHash2("sha256").update(bytes).digest("hex"));
299
+ replaceExecutable(executable, bytes);
300
+ }
301
+ renderer.write(
302
+ `
303
+ Updated ${path.basename(executable)} to v${release.version}. Restart wrongstack to use it.
304
+ `
305
+ );
306
+ return 0;
307
+ } catch (err) {
308
+ const msg = err instanceof Error ? err.message : String(err);
309
+ const permission = /EACCES|EPERM/.test(msg) ? "\nThe executable is not writable by this user; rerun with the permissions it was installed with." : "";
310
+ renderer.write(`
311
+ Update failed: ${msg}${permission}
312
+ `);
313
+ return 1;
314
+ }
315
+ }
316
+
317
+ export {
318
+ downloadReleaseAssetToFile,
319
+ MAX_BINARY_BYTES,
320
+ standaloneAssetName,
321
+ findStandaloneRelease,
322
+ verifyStandaloneDigest,
323
+ fetchLatestStandaloneVersion,
324
+ parseSha256Sums,
325
+ replaceExecutable,
326
+ installExecutableFrom,
327
+ updateStandaloneBinary
328
+ };
329
+ //# sourceMappingURL=chunk-X756ZS72.js.map
@@ -9,7 +9,10 @@ import {
9
9
  // src/wiring/plugins.ts
10
10
  import { join } from "node:path";
11
11
  import { loadPlugins, resolvePluginConfig, resolvePluginEnablement } from "@wrongstack/core/plugin";
12
- import { OFFICIAL_PLUGIN_FACTORIES } from "@wrongstack/plugins/factories";
12
+ import {
13
+ OFFICIAL_PLUGIN_FACTORIES,
14
+ OFFICIAL_PLUGIN_SPECIFIERS
15
+ } from "@wrongstack/plugins/factories";
13
16
 
14
17
  // src/plugin-api-factory.ts
15
18
  import { DefaultPluginAPI } from "@wrongstack/core/plugin";
@@ -109,6 +112,28 @@ var BUILTIN_PLUGIN_FACTORIES = [
109
112
  // ── Telegram plugin ─────────────────────────────────────────────────
110
113
  async () => (await import("@wrongstack/telegram")).default
111
114
  ];
115
+ var BUILTIN_PLUGIN_NAME_HINTS = [
116
+ "wstack-prompts",
117
+ "wstack-sync",
118
+ "wstack-cloud-config-sync",
119
+ "wstack-chimera",
120
+ "wstack-auto-review",
121
+ "wstack-specialist-triggers",
122
+ "wstack-skills",
123
+ ...OFFICIAL_PLUGIN_SPECIFIERS.map((specifier) => specifier.slice("@wrongstack/plugins/".length)),
124
+ "@wrongstack/plug-lsp",
125
+ "telegram"
126
+ ];
127
+ function builtinEnablement(name, config, aliases) {
128
+ const auditEntry = PLUGIN_AUDIT_ENTRIES.find((entry) => entry.name === name);
129
+ return resolvePluginEnablement({
130
+ name,
131
+ aliases,
132
+ defaultState: auditEntry?.defaultState ?? "active",
133
+ config,
134
+ matches: (spec) => (builtinPluginNameFromSpec(spec) ?? spec) === name
135
+ });
136
+ }
112
137
  async function setupPlugins(params) {
113
138
  const {
114
139
  config,
@@ -133,24 +158,30 @@ async function setupPlugins(params) {
133
158
  vault,
134
159
  pipelines,
135
160
  paths,
136
- hookRegistry
161
+ hookRegistry,
162
+ safeMode
137
163
  } = params;
138
164
  const builtinPlugins = [];
139
165
  if (paths && config.features?.plugins !== false) {
140
- for (const factory of BUILTIN_PLUGIN_FACTORIES) {
166
+ for (const [index, factory] of BUILTIN_PLUGIN_FACTORIES.entries()) {
167
+ const nameHint = BUILTIN_PLUGIN_NAME_HINTS[index];
168
+ if (nameHint && BUILTIN_PLUGIN_NAME_HINTS.length === BUILTIN_PLUGIN_FACTORIES.length) {
169
+ const { enabled, source } = builtinEnablement(
170
+ nameHint,
171
+ config,
172
+ nameHint === "telegram" ? ["@wrongstack/telegram"] : void 0
173
+ );
174
+ if (!enabled) {
175
+ if (source !== "default") {
176
+ log.info(`[setupPlugins] built-in plugin "${nameHint}" disabled by ${source}`);
177
+ }
178
+ continue;
179
+ }
180
+ }
141
181
  try {
142
182
  const plugin = await factory();
143
183
  if (!plugin) continue;
144
- const auditEntry = PLUGIN_AUDIT_ENTRIES.find((entry) => entry.name === plugin.name);
145
- const { enabled, source } = resolvePluginEnablement({
146
- name: plugin.name,
147
- aliases: plugin.configAliases,
148
- // Built-ins outside the audit catalog (prompts, sync, skills, …)
149
- // are infrastructure — they run unless explicitly turned off.
150
- defaultState: auditEntry?.defaultState ?? "active",
151
- config,
152
- matches: (spec) => (builtinPluginNameFromSpec(spec) ?? spec) === plugin.name
153
- });
184
+ const { enabled, source } = builtinEnablement(plugin.name, config, plugin.configAliases);
154
185
  if (!enabled) {
155
186
  if (source !== "default") {
156
187
  log.info(`[setupPlugins] built-in plugin "${plugin.name}" disabled by ${source}`);
@@ -164,7 +195,7 @@ async function setupPlugins(params) {
164
195
  }
165
196
  }
166
197
  }
167
- const userPlugins = config.features?.plugins === false ? [] : await loadExternalPlugins(
198
+ const userPlugins = config.features?.plugins === false || safeMode === true ? [] : await loadExternalPlugins(
168
199
  {
169
200
  config,
170
201
  log,
@@ -272,6 +303,7 @@ export {
272
303
  pluginNameFromSpec,
273
304
  builtinPluginNameFromSpec,
274
305
  BUILTIN_PLUGIN_FACTORIES,
306
+ BUILTIN_PLUGIN_NAME_HINTS,
275
307
  setupPlugins
276
308
  };
277
- //# sourceMappingURL=chunk-JF2MULWF.js.map
309
+ //# sourceMappingURL=chunk-YHYINL47.js.map