@wrongstack/cli 1.0.19 → 1.0.24

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/dist/{acp-INCNOH42.js → acp-AJWSKWE5.js} +18 -7
  2. package/dist/arg-parser.d.ts +1 -0
  3. package/dist/{auth-YXK3EDUL.js → auth-GRBZ7Y3D.js} +61 -15
  4. package/dist/auth-menu/direct.d.ts +1 -0
  5. package/dist/auth-menu/local.d.ts +2 -0
  6. package/dist/auth-menu/oauth-menu.d.ts +3 -1
  7. package/dist/auth-menu/panel-service.d.ts +20 -0
  8. package/dist/{bench-YBU6BVQP.js → bench-N5K7Z5O3.js} +2 -2
  9. package/dist/boot/config-backup-recovery.d.ts +2 -1
  10. package/dist/boot/dispatch-webui.d.ts +11 -0
  11. package/dist/boot/short-circuit-desktop.d.ts +1 -0
  12. package/dist/boot/tui-project-picker-callback.d.ts +1 -1
  13. package/dist/boot/tui-settings-adapter.d.ts +15 -2
  14. package/dist/{boot-auto-thin-MANHOYHE.js → boot-auto-thin-6Q56FY3F.js} +3 -2
  15. package/dist/{chunk-6WRKAACA.js → chunk-2BKFWIER.js} +4 -2
  16. package/dist/{chunk-JY52HWL6.js → chunk-2EBVTYJR.js} +2 -2
  17. package/dist/chunk-3OCUS3VN.js +46 -0
  18. package/dist/chunk-424ORSE7.js +56 -0
  19. package/dist/{chunk-KEPABQYT.js → chunk-5SEYGOCH.js} +8 -82
  20. package/dist/chunk-BBHWW6LR.js +549 -0
  21. package/dist/{chunk-CJTBLTS4.js → chunk-BLD5DSVA.js} +4 -3
  22. package/dist/{chunk-KK2LB3ZS.js → chunk-DRM7PSW7.js} +9 -5
  23. package/dist/{chunk-XJ7K67PP.js → chunk-JF2MULWF.js} +17 -10
  24. package/dist/{chunk-SIS37L3W.js → chunk-L2RVEQWO.js} +211 -193
  25. package/dist/{chunk-RDHFXOJM.js → chunk-LFMVHT3R.js} +106 -57
  26. package/dist/chunk-LUMETJFO.js +171 -0
  27. package/dist/{chunk-C3Z4N6A6.js → chunk-MHEBWVXL.js} +6 -2
  28. package/dist/{chunk-2ENZU66X.js → chunk-PGKJ2OQP.js} +5 -5
  29. package/dist/{chunk-672UXYL3.js → chunk-QBBF7GIK.js} +2 -2
  30. package/dist/{chunk-DE7BZDNK.js → chunk-QFPQXKCG.js} +11 -3
  31. package/dist/chunk-R4QGL6GB.js +10 -0
  32. package/dist/chunk-S4FFHDJG.js +93 -0
  33. package/dist/chunk-SFBFYIOW.js +271 -0
  34. package/dist/{chunk-JX6KL7OF.js → chunk-SKPDE62W.js} +4 -3
  35. package/dist/{chunk-RMQNCHGO.js → chunk-U46QR4WR.js} +5 -9
  36. package/dist/{chunk-B4JLSXZB.js → chunk-WSTVGC2C.js} +73 -45
  37. package/dist/chunk-YBUCXXYZ.js +36 -0
  38. package/dist/{chunk-2VMJ3E5Z.js → chunk-YTEOAWOJ.js} +6 -2
  39. package/dist/{chunk-GPPRNAKW.js → chunk-ZMX5HRLI.js} +2 -2
  40. package/dist/{chunk-YLQEGYV4.js → chunk-ZZRSG7VK.js} +14 -4
  41. package/dist/{cli-context-33F74CSZ.js → cli-context-34Q5IJRA.js} +121 -89
  42. package/dist/cli-entry-point.d.ts +2 -0
  43. package/dist/{cli-main-FNGMKPBA.js → cli-main-AELJDBGU.js} +1439 -1276
  44. package/dist/config-doctor.d.ts +10 -2
  45. package/dist/{config-transfer-2YF3CRTN.js → config-transfer-RUWLDCNK.js} +34 -13
  46. package/dist/{diag-doctor-ANPRUYFM.js → diag-doctor-WIFJH3XH.js} +2 -2
  47. package/dist/execute-deps.d.ts +2 -1
  48. package/dist/{execution-A6WJLSOC.js → execution-PYC5FCS4.js} +303 -256
  49. package/dist/{hq-XQWQTRDA.js → hq-JTUTFI4L.js} +274 -258
  50. package/dist/hq-command-controller.d.ts +1 -1
  51. package/dist/{hq-server-IIBZQZU4.js → hq-server-PC7GZMPT.js} +2 -2
  52. package/dist/{hq-service-L5INQFM4.js → hq-service-NW56MUP2.js} +11 -4
  53. package/dist/index.d.ts +1 -1
  54. package/dist/index.js +17 -12
  55. package/dist/kanban-supervisor-67ECABTD.js +7 -0
  56. package/dist/live-settings-input.d.ts +7 -0
  57. package/dist/{mailbox-serve-3GFOK2I7.js → mailbox-serve-WDOCH6KR.js} +3 -3
  58. package/dist/{mcp-DC5WSBOO.js → mcp-LBS45HWZ.js} +4 -4
  59. package/dist/modeldiag-36OGAL5C.js +828 -0
  60. package/dist/{per-subcommand-help-DHTYU524.js → per-subcommand-help-XE77MTGJ.js} +3 -3
  61. package/dist/permission-prompt-mirror.d.ts +1 -1
  62. package/dist/permission-prompt.d.ts +2 -2
  63. package/dist/plugin-config.d.ts +12 -0
  64. package/dist/plugin-management-types.d.ts +31 -0
  65. package/dist/plugin-management.d.ts +2 -30
  66. package/dist/plugin-package-install.d.ts +3 -0
  67. package/dist/{plugin-usage-M6KET4OZ.js → plugin-usage-S5LVGMBW.js} +6 -5
  68. package/dist/{plugins-WJLW6ULV.js → plugins-RHBIVLI7.js} +4 -3
  69. package/dist/provider-config-utils.d.ts +1 -6
  70. package/dist/{providers-models-LFNZ5WI7.js → providers-models-2CFG46RD.js} +41 -31
  71. package/dist/release-asset-download.d.ts +3 -0
  72. package/dist/services/dispatch-classifier.d.ts +39 -5
  73. package/dist/{sessions-config-UL45XKX7.js → sessions-config-IQUH2GXT.js} +13 -5
  74. package/dist/settings-menu.d.ts +2 -0
  75. package/dist/short-circuit-flags-SDT53OML.js +10 -0
  76. package/dist/{simpleui-dist-CXSIDAXZ.js → simpleui-dist-7BQISSEP.js} +7 -3
  77. package/dist/skill-suggest-L2EVWB6R.js +290 -0
  78. package/dist/slash-commands/effort.d.ts +1 -1
  79. package/dist/slash-commands/fallback.d.ts +1 -1
  80. package/dist/slash-commands/gitid.d.ts +1 -1
  81. package/dist/slash-commands/jev.d.ts +4 -0
  82. package/dist/slash-commands/openai-quota.d.ts +15 -0
  83. package/dist/slash-commands/sdd-task-mutations.d.ts +4 -0
  84. package/dist/slash-commands/settings-keys.d.ts +2 -0
  85. package/dist/slash-commands/settings-reset.d.ts +6 -0
  86. package/dist/standalone-update-MW47J3AK.js +164 -0
  87. package/dist/standalone-update.d.ts +19 -0
  88. package/dist/subcommands/handlers/hq-alerts.d.ts +3 -0
  89. package/dist/subcommands/handlers/hq-audit.d.ts +46 -0
  90. package/dist/subcommands/handlers/hq-data-dir.d.ts +13 -0
  91. package/dist/subcommands/handlers/skill-suggest.d.ts +21 -0
  92. package/dist/subcommands/handlers/typesafe-brain-replay.d.ts +26 -0
  93. package/dist/subcommands/handlers/typesafe-judgment-check.d.ts +3 -0
  94. package/dist/subcommands/handlers/typesafe-topic-replay.d.ts +19 -0
  95. package/dist/subcommands/handlers/typesafe-triage-replay.d.ts +18 -0
  96. package/dist/subcommands/handlers/typesafe.d.ts +23 -0
  97. package/dist/{subcommands-XSLXMIDP.js → subcommands-ZTSIN7LG.js} +2 -2
  98. package/dist/typesafe-HVEVPA6R.js +316 -0
  99. package/dist/typesafe-brain-replay-C5276FVR.js +222 -0
  100. package/dist/typesafe-judgment-check-GGYYV5UF.js +29 -0
  101. package/dist/typesafe-topic-replay-4W73BOLV.js +218 -0
  102. package/dist/typesafe-triage-replay-FGAW7QM7.js +215 -0
  103. package/dist/{update-5DPB4YD4.js → update-7XIOEXTD.js} +4 -3
  104. package/dist/{update-check-BS3DHTSI.js → update-check-2POMBHFB.js} +3 -2
  105. package/dist/update-check.d.ts +1 -1
  106. package/dist/version-help-WZ623NWM.js +10 -0
  107. package/dist/version.d.ts +3 -2
  108. package/dist/webui-client-observability.d.ts +7 -0
  109. package/dist/{webui-server-MRPGTTKL.js → webui-server-L2SICPTB.js} +158 -94
  110. package/dist/webui-server-options.d.ts +11 -0
  111. package/dist/webui-server.d.ts +15 -1
  112. package/dist/wiring/domain-glossary.d.ts +5 -4
  113. package/dist/wiring/plugins.d.ts +1 -1
  114. package/dist/wiring/provider-persisters.d.ts +24 -0
  115. package/dist/wiring/provider-runtime.d.ts +9 -1
  116. package/dist/wiring/runtime-dispatch-state.d.ts +3 -1
  117. package/package.json +28 -28
  118. package/dist/chunk-2CZEORLV.js +0 -54
  119. package/dist/chunk-DR2OXU46.js +0 -174
  120. package/dist/chunk-JACOMTIH.js +0 -275
  121. package/dist/chunk-XJXDOF63.js +0 -28
  122. package/dist/modeldiag-CJSFHZ3J.js +0 -1356
  123. package/dist/short-circuit-flags-7GQIGMVD.js +0 -10
  124. package/dist/version-help-WXI42PZ3.js +0 -10
@@ -0,0 +1,93 @@
1
+
2
+ // src/wiring/provider-runtime.ts
3
+ import {
4
+ getProxyConfig,
5
+ isProxyEligible,
6
+ rewriteBaseUrl,
7
+ sanitizeUrlForLog,
8
+ shouldRewriteFor
9
+ } from "@wrongstack/core/wiring/proxy-rewrite";
10
+ import { makeProviderFromConfig } from "@wrongstack/providers";
11
+ function resolveRawProviderConnection(config, providerId) {
12
+ const savedCfg = config.providers?.[providerId];
13
+ const isAccountAlias = savedCfg?.type !== void 0 && savedCfg.type !== providerId;
14
+ const inheritsPrimary = !isAccountAlias && (config.provider === void 0 || config.provider === providerId);
15
+ return {
16
+ apiKey: savedCfg?.apiKey ?? (inheritsPrimary ? config.apiKey : void 0),
17
+ baseUrl: savedCfg?.baseUrl ?? (inheritsPrimary ? config.baseUrl : void 0)
18
+ };
19
+ }
20
+ function resolveProviderCfg(config, providerId, opts) {
21
+ const savedCfg = config.providers?.[providerId];
22
+ const proxyProviderType = savedCfg?.family === "openai-codex" ? "openai-codex" : savedCfg?.type ?? providerId;
23
+ const connection = resolveRawProviderConnection(config, providerId);
24
+ const rawBaseUrl = connection.baseUrl;
25
+ let rewriteReason = "no-base-url";
26
+ let baseUrl = rawBaseUrl;
27
+ if (rawBaseUrl && shouldRewriteFor(proxyProviderType)) {
28
+ const rewritten = rewriteBaseUrl(rawBaseUrl, currentProxyBaseUrl());
29
+ if (rewritten !== rawBaseUrl) {
30
+ rewriteReason = "applied";
31
+ baseUrl = rewritten;
32
+ } else {
33
+ rewriteReason = passthroughReason(rawBaseUrl);
34
+ }
35
+ } else if (rawBaseUrl) {
36
+ rewriteReason = proxySkipReason(proxyProviderType);
37
+ }
38
+ opts?.logger?.info(
39
+ `WrongProxy rewrite ${rewriteReason} for provider '${providerId}': ${sanitizeUrlForLog(rawBaseUrl) || "<none>"} -> ${sanitizeUrlForLog(baseUrl) || "<none>"}`
40
+ );
41
+ const cfg = {
42
+ ...savedCfg,
43
+ apiKey: connection.apiKey,
44
+ baseUrl,
45
+ type: providerId
46
+ };
47
+ const factoryType = savedCfg?.type ?? providerId;
48
+ return { cfg, factoryType };
49
+ }
50
+ function passthroughReason(rawBaseUrl) {
51
+ if (!rawBaseUrl.includes("://")) return "no-scheme";
52
+ const proxyRoot = currentProxyBaseUrl().replace(/\/+$/, "");
53
+ if (proxyRoot && rawBaseUrl.startsWith(`${proxyRoot}/proxy/`)) {
54
+ return "already-rewritten";
55
+ }
56
+ try {
57
+ const parsed = new URL(rawBaseUrl);
58
+ const host = parsed.hostname;
59
+ const isLoopback = host === "localhost" || host === "127.0.0.1" || host === "[::1]";
60
+ if (isLoopback && parsed.port !== "") return "loopback";
61
+ } catch {
62
+ }
63
+ return "ineligible";
64
+ }
65
+ function proxySkipReason(providerId) {
66
+ const cfg = getProxyConfig();
67
+ if (!cfg.enabled) return "disabled";
68
+ if (!cfg.url) return "no-proxy-url";
69
+ if (!cfg.active) return "inactive";
70
+ if (!isProxyEligible(providerId)) return "excluded";
71
+ return "unknown";
72
+ }
73
+ function currentProxyBaseUrl() {
74
+ return getProxyConfig().url;
75
+ }
76
+ function resolveProviderCfgWithProxy(config, providerId, opts) {
77
+ return resolveProviderCfg(config, providerId, opts);
78
+ }
79
+ function buildProviderForId(args, providerId) {
80
+ const { cfg, factoryType } = resolveProviderCfg(args.config, providerId, {
81
+ logger: args.logger
82
+ });
83
+ const useRegistry = !!args.config.features.modelsRegistry && args.providerRegistry.has(factoryType);
84
+ return useRegistry ? args.providerRegistry.create(cfg, factoryType) : makeProviderFromConfig(providerId, { ...cfg, type: factoryType });
85
+ }
86
+
87
+ export {
88
+ resolveRawProviderConnection,
89
+ resolveProviderCfg,
90
+ resolveProviderCfgWithProxy,
91
+ buildProviderForId
92
+ };
93
+ //# sourceMappingURL=chunk-S4FFHDJG.js.map
@@ -0,0 +1,271 @@
1
+ import {
2
+ appendHistory,
3
+ backupCurrent
4
+ } from "./chunk-WSTVGC2C.js";
5
+
6
+ // src/settings-menu.ts
7
+ import * as fs from "node:fs/promises";
8
+ import * as path from "node:path";
9
+ import { decryptConfigSecretsForRewrite, encryptConfigSecrets } from "@wrongstack/core/security";
10
+ import { ConfigError, ERROR_CODES, FsError } from "@wrongstack/core/types";
11
+ import { atomicWrite, deepMerge, withFileLock } from "@wrongstack/core/utils";
12
+ function resolvePersistPath(deps) {
13
+ if (deps.forceGlobal) return deps.profileConfigPath;
14
+ const scope = deps.configStore.get().configScope;
15
+ if (scope === "project" && deps.inProjectConfigPath) {
16
+ return deps.inProjectConfigPath;
17
+ }
18
+ return deps.profileConfigPath;
19
+ }
20
+ function resolveActualTarget(deps, decrypted, fallbackPath) {
21
+ if (deps.forceGlobal) return deps.profileConfigPath;
22
+ const newScope = decrypted.configScope;
23
+ const newProfileName = decrypted.activeProfile;
24
+ const effectiveProfilePath = newProfileName && deps.resolveProfilePath ? deps.resolveProfilePath(newProfileName) : deps.profileConfigPath;
25
+ if (newScope === "project" && deps.inProjectConfigPath) {
26
+ return deps.inProjectConfigPath;
27
+ }
28
+ if (newScope === "global") {
29
+ return effectiveProfilePath;
30
+ }
31
+ return fallbackPath;
32
+ }
33
+ function isProfileOrGlobalTarget(actualTarget, deps) {
34
+ return !deps.inProjectConfigPath || path.resolve(actualTarget) !== path.resolve(deps.inProjectConfigPath);
35
+ }
36
+ async function ensureProjectDir(filePath) {
37
+ const dir = path.dirname(filePath);
38
+ try {
39
+ await fs.mkdir(dir, { recursive: true });
40
+ } catch {
41
+ }
42
+ }
43
+ var PROJECT_SAFE_FIELDS = /* @__PURE__ */ new Set([
44
+ "version",
45
+ "model",
46
+ "cwd",
47
+ "fallbackModels",
48
+ "fallbackBridge",
49
+ // Kept in sync with core's IN_PROJECT_ALLOWED_KEYS (config-loader.ts): these
50
+ // model-routing prefs are explicitly safe to READ from a per-project config,
51
+ // so the writer must not strip them — otherwise a scope=project /fallback
52
+ // profile/fav write (or a TUI full-config write under project scope) would be
53
+ // silently dropped while the loader would happily accept them on next boot.
54
+ "fallbackProfiles",
55
+ "fallbackProfile",
56
+ "favoriteModels",
57
+ "disabledModels",
58
+ "favoriteModelsOnly",
59
+ "modelAvailabilitySchedule",
60
+ "fallbackAuto",
61
+ "fallbackStickiness",
62
+ "fallbackGateSeconds",
63
+ "models",
64
+ "modelMatrix",
65
+ "modelTiers",
66
+ "maxConcurrent",
67
+ "autonomy",
68
+ "hints",
69
+ "nextPrediction",
70
+ "debugStream",
71
+ "configScope",
72
+ "features",
73
+ "context",
74
+ "log",
75
+ "session",
76
+ "indexing",
77
+ "tools",
78
+ "launch",
79
+ "circuitBreaker",
80
+ "adaptiveConcurrency",
81
+ "modelRuntime",
82
+ "Sage",
83
+ "skills",
84
+ "chronicle",
85
+ "uiLocale",
86
+ "themePreset"
87
+ ]);
88
+ var PROJECT_DENIED_PATHS = [
89
+ "tools.exec.allow",
90
+ "tools.exec.danger",
91
+ "tools.council",
92
+ "skills.extraDirs",
93
+ "skills.registryUrl",
94
+ "skills.suggest",
95
+ "Sage.storage.directory",
96
+ "autonomy.yolo",
97
+ "autonomy.defaultMode",
98
+ "autonomy.yoloConfirm",
99
+ "launch.autonomy",
100
+ "features.allowOutsideProjectRoot",
101
+ "tools.restrictToProjectRoot",
102
+ "tools.loopDetection",
103
+ "tools.maxIterations",
104
+ "tools.autoExtendLimit",
105
+ "tools.maxAutoExtensions",
106
+ "tools.kanbanGovernance",
107
+ "tools.autoThin",
108
+ "tools.disabledToolMeta",
109
+ "tools.wrongProxy",
110
+ "features.mailboxBridge",
111
+ "features.pluginsTrust"
112
+ ];
113
+ function deleteProjectDeniedPath(target, dotted) {
114
+ const parts = dotted.split(".");
115
+ const leaf = parts.pop();
116
+ if (!leaf) return;
117
+ let cursor = target;
118
+ for (const part of parts) {
119
+ const next = cursor[part];
120
+ if (!next || typeof next !== "object" || Array.isArray(next)) return;
121
+ cursor = next;
122
+ }
123
+ delete cursor[leaf];
124
+ }
125
+ function filterSafeForProject(cfg) {
126
+ const out = {};
127
+ for (const [key, value] of Object.entries(cfg)) {
128
+ if (PROJECT_SAFE_FIELDS.has(key)) {
129
+ out[key] = structuredClone(value);
130
+ }
131
+ }
132
+ for (const denied of PROJECT_DENIED_PATHS) deleteProjectDeniedPath(out, denied);
133
+ return out;
134
+ }
135
+ async function readConfigForRewrite(filePath, vault) {
136
+ let raw;
137
+ let fileExists = true;
138
+ try {
139
+ raw = await fs.readFile(filePath, "utf8");
140
+ } catch (err) {
141
+ if (err.code !== "ENOENT") {
142
+ throw new FsError({
143
+ message: `Could not read ${filePath}`,
144
+ code: ERROR_CODES.FS_READ_FAILED,
145
+ path: filePath,
146
+ cause: err
147
+ });
148
+ }
149
+ fileExists = false;
150
+ raw = "{}";
151
+ }
152
+ try {
153
+ const parsed = JSON.parse(raw);
154
+ return decryptConfigSecretsForRewrite(parsed, vault);
155
+ } catch (err) {
156
+ if (fileExists) {
157
+ throw new ConfigError({
158
+ message: `Config at ${filePath} is not valid JSON`,
159
+ code: ERROR_CODES.CONFIG_PARSE_FAILED,
160
+ context: { path: filePath },
161
+ cause: err
162
+ });
163
+ }
164
+ return {};
165
+ }
166
+ }
167
+ async function writeConfigMutation(deps, mutator) {
168
+ const sourcePath = resolvePersistPath(deps);
169
+ await ensureProjectDir(sourcePath);
170
+ let mutated = {};
171
+ let sourceBefore = {};
172
+ let actualTarget = sourcePath;
173
+ let wroteSource = false;
174
+ await withFileLock(sourcePath, async () => {
175
+ mutated = await readConfigForRewrite(sourcePath, deps.vault);
176
+ sourceBefore = structuredClone(mutated);
177
+ mutator(mutated);
178
+ actualTarget = resolveActualTarget(deps, mutated, sourcePath);
179
+ if (actualTarget !== sourcePath) return;
180
+ const toWrite = isProfileOrGlobalTarget(actualTarget, deps) ? mutated : filterSafeForProject(mutated);
181
+ await backupCurrent(void 0, actualTarget);
182
+ await atomicWrite(
183
+ actualTarget,
184
+ JSON.stringify(encryptConfigSecrets(toWrite, deps.vault), null, 2),
185
+ { mode: 384 }
186
+ );
187
+ try {
188
+ await appendHistory(sourceBefore, toWrite, "Settings updated", void 0, actualTarget);
189
+ } catch {
190
+ }
191
+ wroteSource = true;
192
+ });
193
+ if (!wroteSource) {
194
+ await ensureProjectDir(actualTarget);
195
+ await withFileLock(actualTarget, async () => {
196
+ const destinationBefore = await readConfigForRewrite(actualTarget, deps.vault);
197
+ const effectiveConfig = deepMerge(destinationBefore, mutated);
198
+ const toWrite = isProfileOrGlobalTarget(actualTarget, deps) ? effectiveConfig : filterSafeForProject(effectiveConfig);
199
+ await backupCurrent(void 0, actualTarget);
200
+ await atomicWrite(
201
+ actualTarget,
202
+ JSON.stringify(encryptConfigSecrets(toWrite, deps.vault), null, 2),
203
+ { mode: 384 }
204
+ );
205
+ try {
206
+ await appendHistory(
207
+ destinationBefore,
208
+ toWrite,
209
+ "Settings scope changed and settings were updated",
210
+ void 0,
211
+ actualTarget
212
+ );
213
+ } catch {
214
+ }
215
+ });
216
+ }
217
+ return mutated;
218
+ }
219
+ async function persistAutonomySetting(deps, mutator) {
220
+ const decrypted = await writeConfigMutation(deps, (config) => {
221
+ const autonomy = config.autonomy ?? {};
222
+ mutator(
223
+ autonomy
224
+ );
225
+ config.autonomy = autonomy;
226
+ });
227
+ if (deps.updateStore !== false) {
228
+ deps.configStore.update({
229
+ autonomy: decrypted.autonomy
230
+ });
231
+ }
232
+ }
233
+ async function persistConfigSetting(deps, mutator) {
234
+ const decrypted = await writeConfigMutation(deps, mutator);
235
+ if (deps.updateStore !== false) {
236
+ deps.configStore.update(decrypted);
237
+ }
238
+ }
239
+ async function persistTelegramConfig(deps, mutator) {
240
+ const decrypted = await writeConfigMutation({ ...deps, forceGlobal: true }, (config) => {
241
+ const extensions = config.extensions ?? {};
242
+ const telegram = extensions.telegram ?? {};
243
+ mutator(telegram);
244
+ extensions.telegram = telegram;
245
+ config.extensions = extensions;
246
+ });
247
+ await deps.vault.flushHardening?.();
248
+ deps.configStore.update({
249
+ extensions: decrypted.extensions
250
+ });
251
+ }
252
+ function deriveFsAccessPair(input) {
253
+ if (input.allowOutsideProjectRoot !== void 0) {
254
+ const allow = input.allowOutsideProjectRoot;
255
+ return { allowOutsideProjectRoot: allow, restrictToProjectRoot: !allow };
256
+ }
257
+ if (input.restrictFsToRoot !== void 0) {
258
+ const restrict = input.restrictFsToRoot;
259
+ return { allowOutsideProjectRoot: !restrict, restrictToProjectRoot: restrict };
260
+ }
261
+ return void 0;
262
+ }
263
+
264
+ export {
265
+ filterSafeForProject,
266
+ persistAutonomySetting,
267
+ persistConfigSetting,
268
+ persistTelegramConfig,
269
+ deriveFsAccessPair
270
+ };
271
+ //# sourceMappingURL=chunk-SFBFYIOW.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  renderAuthLocalHelpToString
3
- } from "./chunk-2VMJ3E5Z.js";
3
+ } from "./chunk-YTEOAWOJ.js";
4
4
 
5
5
  // src/subcommands/handlers/per-subcommand-help.ts
6
6
  import { color as color3 } from "@wrongstack/core/utils";
@@ -780,10 +780,11 @@ var helpTable = {
780
780
  name: "config",
781
781
  title: "wstack config \u2014 show or edit effective config",
782
782
  description: "Print the resolved config (with the on-disk overrides merged on top), or open the active profile config in $EDITOR for interactive edits. Also exposes a small audit log of recent config-history changes for diagnostics.",
783
- usage: "wstack config [show|edit|history|restore] [...]",
783
+ usage: "wstack config [show|edit|backup|history|restore] [...]",
784
784
  subcommands: [
785
785
  { name: "show", description: "Print the resolved config to stdout (default)." },
786
786
  { name: "edit", description: "Open the active profile config in $EDITOR." },
787
+ { name: "backup", description: "Create a recoverable snapshot of the active profile." },
787
788
  { name: "history", description: "List recent config-history entries." },
788
789
  { name: "restore <id>", description: "Restore a previous config-history entry." }
789
790
  ],
@@ -1212,4 +1213,4 @@ export {
1212
1213
  subcommandsWithFocusedHelp,
1213
1214
  renderGenericHelp
1214
1215
  };
1215
- //# sourceMappingURL=chunk-JX6KL7OF.js.map
1216
+ //# sourceMappingURL=chunk-SKPDE62W.js.map
@@ -17,7 +17,6 @@ function renderDiff(diff) {
17
17
  }
18
18
 
19
19
  // src/permission-prompt.ts
20
- import { alwaysAllowUnavailableReason } from "@wrongstack/core/security";
21
20
  import {
22
21
  color as color2,
23
22
  sanitizeTerminalPreview,
@@ -41,18 +40,15 @@ ${theme.warn("\u26A0 APPROVAL REQUIRED")} ${theme.primary("\u2502")} ${theme.bol
41
40
  if (preview) writeOut(`${preview}
42
41
  `);
43
42
  writeOut(color2.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
44
- const noAlwaysReason = alwaysAllowUnavailableReason(tool, input);
45
- if (noAlwaysReason) {
46
- writeOut(`${color2.dim(`(no "always" for this call \u2014 ${noAlwaysReason})`)}
47
- `);
48
- }
49
43
  const options = [
50
44
  { key: "y", label: "yes", value: "yes" },
51
45
  { key: "n", label: "no", value: "no" },
52
- ...noAlwaysReason ? [] : [{ key: "a", label: "always", value: "always" }],
46
+ { key: "a", label: "remember same input/args", value: "always-exact" },
47
+ ...tool.name === "exec" ? [{ key: "c", label: "remember executable with any args", value: "always-command" }] : [],
48
+ { key: "t", label: "remember tool with any input", value: "always-tool" },
53
49
  { key: "d", label: "deny", value: "deny" }
54
50
  ];
55
- const alwaysHint = noAlwaysReason ? "" : ` ${theme.bold("[a]")}lways allow (${sanitizeTerminalText2(suggestedPattern)})`;
51
+ const alwaysHint = ` ${theme.bold("[a]")} same input (${sanitizeTerminalText2(suggestedPattern)})${tool.name === "exec" ? " [c] command, any args" : ""} [t] tool, any input`;
56
52
  const answer = await reader.readKey(
57
53
  `${theme.bold("[y]")}es ${theme.bold("[n]")}o${alwaysHint} ${theme.bold("[d]")}eny: `,
58
54
  options,
@@ -482,4 +478,4 @@ export {
482
478
  detectProjectFacts,
483
479
  renderAgentsTemplate
484
480
  };
485
- //# sourceMappingURL=chunk-RMQNCHGO.js.map
481
+ //# sourceMappingURL=chunk-U46QR4WR.js.map
@@ -6,7 +6,13 @@ import os from "node:os";
6
6
  import * as path from "node:path";
7
7
  import { isSecretField } from "@wrongstack/core/security";
8
8
  import { ERROR_CODES, FsError } from "@wrongstack/core/types";
9
- import { atomicWrite, resolveWstackPaths, toErrorMessage, writeErr } from "@wrongstack/core/utils";
9
+ import {
10
+ atomicWrite,
11
+ resolveWstackPaths,
12
+ toErrorMessage,
13
+ withFileLock,
14
+ writeErr
15
+ } from "@wrongstack/core/utils";
10
16
  var defaultUidFn = () => os.userInfo().uid;
11
17
  async function getFileUid(filePath) {
12
18
  try {
@@ -89,7 +95,11 @@ function maskConfigSecrets(cfg) {
89
95
  if (typeof cfg !== "object" || cfg === null) return {};
90
96
  const out = {};
91
97
  for (const [k, v] of Object.entries(cfg)) {
92
- if (isSecretField(k)) {
98
+ if (k.toLowerCase() === "apikeys" && Array.isArray(v) && v.every(
99
+ (entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry) && ("label" in entry || "id" in entry) && "apiKey" in entry
100
+ )) {
101
+ out[k] = maskValue(v);
102
+ } else if (isSecretField(k)) {
93
103
  out[k] = REDACTED;
94
104
  } else {
95
105
  out[k] = maskValue(v);
@@ -101,7 +111,14 @@ function reviveSecrets(masked, current) {
101
111
  if (masked === REDACTED) return current;
102
112
  if (Array.isArray(masked)) {
103
113
  const cur = Array.isArray(current) ? current : [];
104
- return masked.map((item, i) => reviveSecrets(item, cur[i]));
114
+ return masked.map((item, i) => {
115
+ const record = item && typeof item === "object" && !Array.isArray(item) ? item : void 0;
116
+ const identityKey = typeof record?.["label"] === "string" ? "label" : typeof record?.["id"] === "string" ? "id" : void 0;
117
+ const matched = identityKey ? cur.find(
118
+ (candidate) => candidate && typeof candidate === "object" && !Array.isArray(candidate) && candidate[identityKey] === record?.[identityKey]
119
+ ) : cur[i];
120
+ return reviveSecrets(item, matched);
121
+ });
105
122
  }
106
123
  if (typeof masked !== "object" || masked === null) return masked;
107
124
  const curObj = typeof current === "object" && current !== null && !Array.isArray(current) ? current : {};
@@ -271,10 +288,12 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
271
288
  cause: err
272
289
  });
273
290
  }
274
- const idx = await readIndex(homeFn, targetConfigPath);
275
- idx.entries.unshift({ id, timestamp, description });
276
- await pruneHistoryEntries(idx, homeFn, targetConfigPath);
277
- await writeIndex(idx, homeFn, targetConfigPath);
291
+ await withFileLock(historyIndexPath(homeFn, targetConfigPath), async () => {
292
+ const idx = await readIndex(homeFn, targetConfigPath);
293
+ idx.entries.unshift({ id, timestamp, description });
294
+ await pruneHistoryEntries(idx, homeFn, targetConfigPath);
295
+ await writeIndex(idx, homeFn, targetConfigPath);
296
+ });
278
297
  return id;
279
298
  }
280
299
  async function listHistory(homeFn = defaultHomeDir, targetConfigPath) {
@@ -302,39 +321,34 @@ async function restoreFromHistory(id, homeFn = defaultHomeDir, targetConfigPath)
302
321
  error: "Operation denied: config file is not owned by current user"
303
322
  };
304
323
  }
305
- await backupCurrent(homeFn, targetConfigPath);
306
- let oldCfg = {};
307
- try {
308
- const raw = await fs.readFile(configPath(homeFn, targetConfigPath), "utf8");
309
- oldCfg = JSON.parse(raw);
310
- } catch {
311
- }
312
- const restored = reviveSecrets(entry.snapshotMasked, oldCfg);
313
- try {
314
- await atomicWrite(configPath(homeFn, targetConfigPath), JSON.stringify(restored, null, 2), {
315
- mode: 384
316
- });
317
- } catch (err) {
318
- return { ok: false, backupId: null, error: String(err) };
319
- }
320
- const backupId = await appendHistory(
321
- oldCfg,
322
- restored,
323
- `Restored from history ${id}`,
324
- homeFn,
325
- targetConfigPath
326
- );
327
- return { ok: true, backupId };
324
+ const cfg = configPath(homeFn, targetConfigPath);
325
+ return withFileLock(cfg, async () => {
326
+ await backupCurrent(homeFn, targetConfigPath);
327
+ let oldCfg = {};
328
+ try {
329
+ const raw = await fs.readFile(cfg, "utf8");
330
+ oldCfg = JSON.parse(raw);
331
+ } catch {
332
+ }
333
+ const restored = reviveSecrets(entry.snapshotMasked, oldCfg);
334
+ try {
335
+ await atomicWrite(cfg, JSON.stringify(restored, null, 2), { mode: 384 });
336
+ } catch (err) {
337
+ return { ok: false, backupId: null, error: String(err) };
338
+ }
339
+ const backupId = await appendHistory(
340
+ oldCfg,
341
+ restored,
342
+ `Restored from history ${id}`,
343
+ homeFn,
344
+ targetConfigPath
345
+ );
346
+ return { ok: true, backupId };
347
+ });
328
348
  }
329
349
  async function restoreLast(homeFn = defaultHomeDir, targetConfigPath) {
330
350
  const last = backupLastPath(homeFn, targetConfigPath);
331
351
  const cfg = configPath(homeFn, targetConfigPath);
332
- let oldCfg = {};
333
- try {
334
- const raw = await fs.readFile(cfg, "utf8");
335
- oldCfg = JSON.parse(raw);
336
- } catch {
337
- }
338
352
  let lastCfg = {};
339
353
  try {
340
354
  const raw = await fs.readFile(last, "utf8");
@@ -345,14 +359,28 @@ async function restoreLast(homeFn = defaultHomeDir, targetConfigPath) {
345
359
  if (!await checkConfigOwnership(homeFn, defaultUidFn, targetConfigPath)) {
346
360
  return { ok: false, error: "Operation denied: config file is not owned by current user" };
347
361
  }
348
- await backupCurrent(homeFn, targetConfigPath);
349
- try {
350
- await atomicWrite(cfg, JSON.stringify(lastCfg, null, 2));
351
- } catch (err) {
352
- return { ok: false, error: String(err) };
353
- }
354
- await appendHistory(oldCfg, lastCfg, "Restored from config.json.last", homeFn, targetConfigPath);
355
- return { ok: true };
362
+ return withFileLock(cfg, async () => {
363
+ let oldCfg = {};
364
+ try {
365
+ const raw = await fs.readFile(cfg, "utf8");
366
+ oldCfg = JSON.parse(raw);
367
+ } catch {
368
+ }
369
+ await backupCurrent(homeFn, targetConfigPath);
370
+ try {
371
+ await atomicWrite(cfg, JSON.stringify(lastCfg, null, 2), { mode: 384 });
372
+ } catch (err) {
373
+ return { ok: false, error: String(err) };
374
+ }
375
+ await appendHistory(
376
+ oldCfg,
377
+ lastCfg,
378
+ "Restored from config.json.last",
379
+ homeFn,
380
+ targetConfigPath
381
+ );
382
+ return { ok: true };
383
+ });
356
384
  }
357
385
 
358
386
  export {
@@ -363,4 +391,4 @@ export {
363
391
  restoreFromHistory,
364
392
  restoreLast
365
393
  };
366
- //# sourceMappingURL=chunk-B4JLSXZB.js.map
394
+ //# sourceMappingURL=chunk-WSTVGC2C.js.map
@@ -0,0 +1,36 @@
1
+
2
+ // src/version.ts
3
+ import { createRequire } from "node:module";
4
+ var req = createRequire(import.meta.url);
5
+ var standaloneBuild = globalThis[/* @__PURE__ */ Symbol.for("wrongstack.standalone-build")];
6
+ function readOwnVersion() {
7
+ if (standaloneBuild?.version) return standaloneBuild.version;
8
+ const candidates = ["../package.json", "../../package.json"];
9
+ for (const rel of candidates) {
10
+ try {
11
+ const pkg = req(rel);
12
+ if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
13
+ } catch {
14
+ }
15
+ }
16
+ return "dev";
17
+ }
18
+ var CLI_VERSION = readOwnVersion();
19
+ function readApiVersion() {
20
+ if (standaloneBuild?.apiVersion) return standaloneBuild.apiVersion;
21
+ try {
22
+ const corePkg = req("@wrongstack/core/package.json");
23
+ if (corePkg.wrongstackApiVersion) return corePkg.wrongstackApiVersion;
24
+ } catch {
25
+ }
26
+ return "0.0.0";
27
+ }
28
+ var API_VERSION = readApiVersion();
29
+ var STANDALONE_TARGET = standaloneBuild?.target;
30
+
31
+ export {
32
+ CLI_VERSION,
33
+ API_VERSION,
34
+ STANDALONE_TARGET
35
+ };
36
+ //# sourceMappingURL=chunk-YBUCXXYZ.js.map
@@ -2,6 +2,10 @@
2
2
  // src/subcommands/handlers/auth-local-help.ts
3
3
  import { color } from "@wrongstack/core/utils";
4
4
  var LOCAL_AUTH_FLAGS = [
5
+ {
6
+ flag: "--alias <account>",
7
+ description: "Create a separate auth profile for this preset; the alias selects the endpoint, credential, and models in fallback references."
8
+ },
5
9
  {
6
10
  flag: "--name <ollama|vllm|lmstudio>",
7
11
  description: "Pick a preset (skip the interactive picker). Defaults to the first run; set this for scripting."
@@ -32,7 +36,7 @@ var LOCAL_AUTH_FLAGS = [
32
36
  }
33
37
  ];
34
38
  var LOCAL_FLAG_COLUMN_WIDTH = 38;
35
- var LOCAL_AUTH_HELP_USAGE = "wstack auth local [--name <id>] [--base-url <url>] [--no-key] [--no-probe|--probe-only] [--model <spec>] [--audit [target]]";
39
+ var LOCAL_AUTH_HELP_USAGE = "wstack auth local [--name <id>] [--alias <account>] [--base-url <url>] [--no-key] [--no-probe|--probe-only] [--model <spec>] [--audit [target]]";
36
40
  function wantsLocalHelp(args) {
37
41
  return args.includes("--help") || args.includes("-h");
38
42
  }
@@ -68,4 +72,4 @@ export {
68
72
  wantsLocalHelp,
69
73
  renderAuthLocalHelpToString
70
74
  };
71
- //# sourceMappingURL=chunk-2VMJ3E5Z.js.map
75
+ //# sourceMappingURL=chunk-YTEOAWOJ.js.map
@@ -60,7 +60,7 @@ function parseWebuiSessionChildOptions(flags) {
60
60
  const projectRoot = requireAbsolutePath(flagValue(flags, ["project-root"]), "--project-root");
61
61
  const workingDir = requireAbsolutePath(flagValue(flags, ["working-dir"]), "--working-dir");
62
62
  const relativeWorkingDir = path.relative(projectRoot, workingDir);
63
- if (relativeWorkingDir.startsWith("..") || path.isAbsolute(relativeWorkingDir)) {
63
+ if (relativeWorkingDir === ".." || relativeWorkingDir.startsWith(`..${path.sep}`) || path.isAbsolute(relativeWorkingDir)) {
64
64
  throw new Error("--working-dir must be inside --project-root");
65
65
  }
66
66
  const sessionId = flagValue(flags, ["session-id"]);
@@ -125,4 +125,4 @@ export {
125
125
  writeWebuiSessionChildReady,
126
126
  writeWebuiSessionChildError
127
127
  };
128
- //# sourceMappingURL=chunk-GPPRNAKW.js.map
128
+ //# sourceMappingURL=chunk-ZMX5HRLI.js.map