@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.
- package/dist/{acp-INCNOH42.js → acp-AJWSKWE5.js} +18 -7
- package/dist/arg-parser.d.ts +1 -0
- package/dist/{auth-YXK3EDUL.js → auth-GRBZ7Y3D.js} +61 -15
- package/dist/auth-menu/direct.d.ts +1 -0
- package/dist/auth-menu/local.d.ts +2 -0
- package/dist/auth-menu/oauth-menu.d.ts +3 -1
- package/dist/auth-menu/panel-service.d.ts +20 -0
- package/dist/{bench-YBU6BVQP.js → bench-N5K7Z5O3.js} +2 -2
- package/dist/boot/config-backup-recovery.d.ts +2 -1
- package/dist/boot/dispatch-webui.d.ts +11 -0
- package/dist/boot/short-circuit-desktop.d.ts +1 -0
- package/dist/boot/tui-project-picker-callback.d.ts +1 -1
- package/dist/boot/tui-settings-adapter.d.ts +15 -2
- package/dist/{boot-auto-thin-MANHOYHE.js → boot-auto-thin-6Q56FY3F.js} +3 -2
- package/dist/{chunk-6WRKAACA.js → chunk-2BKFWIER.js} +4 -2
- package/dist/{chunk-JY52HWL6.js → chunk-2EBVTYJR.js} +2 -2
- package/dist/chunk-3OCUS3VN.js +46 -0
- package/dist/chunk-424ORSE7.js +56 -0
- package/dist/{chunk-KEPABQYT.js → chunk-5SEYGOCH.js} +8 -82
- package/dist/chunk-BBHWW6LR.js +549 -0
- package/dist/{chunk-CJTBLTS4.js → chunk-BLD5DSVA.js} +4 -3
- package/dist/{chunk-KK2LB3ZS.js → chunk-DRM7PSW7.js} +9 -5
- package/dist/{chunk-XJ7K67PP.js → chunk-JF2MULWF.js} +17 -10
- package/dist/{chunk-SIS37L3W.js → chunk-L2RVEQWO.js} +211 -193
- package/dist/{chunk-RDHFXOJM.js → chunk-LFMVHT3R.js} +106 -57
- package/dist/chunk-LUMETJFO.js +171 -0
- package/dist/{chunk-C3Z4N6A6.js → chunk-MHEBWVXL.js} +6 -2
- package/dist/{chunk-2ENZU66X.js → chunk-PGKJ2OQP.js} +5 -5
- package/dist/{chunk-672UXYL3.js → chunk-QBBF7GIK.js} +2 -2
- package/dist/{chunk-DE7BZDNK.js → chunk-QFPQXKCG.js} +11 -3
- package/dist/chunk-R4QGL6GB.js +10 -0
- package/dist/chunk-S4FFHDJG.js +93 -0
- package/dist/chunk-SFBFYIOW.js +271 -0
- package/dist/{chunk-JX6KL7OF.js → chunk-SKPDE62W.js} +4 -3
- package/dist/{chunk-RMQNCHGO.js → chunk-U46QR4WR.js} +5 -9
- package/dist/{chunk-B4JLSXZB.js → chunk-WSTVGC2C.js} +73 -45
- package/dist/chunk-YBUCXXYZ.js +36 -0
- package/dist/{chunk-2VMJ3E5Z.js → chunk-YTEOAWOJ.js} +6 -2
- package/dist/{chunk-GPPRNAKW.js → chunk-ZMX5HRLI.js} +2 -2
- package/dist/{chunk-YLQEGYV4.js → chunk-ZZRSG7VK.js} +14 -4
- package/dist/{cli-context-33F74CSZ.js → cli-context-34Q5IJRA.js} +121 -89
- package/dist/cli-entry-point.d.ts +2 -0
- package/dist/{cli-main-FNGMKPBA.js → cli-main-AELJDBGU.js} +1439 -1276
- package/dist/config-doctor.d.ts +10 -2
- package/dist/{config-transfer-2YF3CRTN.js → config-transfer-RUWLDCNK.js} +34 -13
- package/dist/{diag-doctor-ANPRUYFM.js → diag-doctor-WIFJH3XH.js} +2 -2
- package/dist/execute-deps.d.ts +2 -1
- package/dist/{execution-A6WJLSOC.js → execution-PYC5FCS4.js} +303 -256
- package/dist/{hq-XQWQTRDA.js → hq-JTUTFI4L.js} +274 -258
- package/dist/hq-command-controller.d.ts +1 -1
- package/dist/{hq-server-IIBZQZU4.js → hq-server-PC7GZMPT.js} +2 -2
- package/dist/{hq-service-L5INQFM4.js → hq-service-NW56MUP2.js} +11 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +17 -12
- package/dist/kanban-supervisor-67ECABTD.js +7 -0
- package/dist/live-settings-input.d.ts +7 -0
- package/dist/{mailbox-serve-3GFOK2I7.js → mailbox-serve-WDOCH6KR.js} +3 -3
- package/dist/{mcp-DC5WSBOO.js → mcp-LBS45HWZ.js} +4 -4
- package/dist/modeldiag-36OGAL5C.js +828 -0
- package/dist/{per-subcommand-help-DHTYU524.js → per-subcommand-help-XE77MTGJ.js} +3 -3
- package/dist/permission-prompt-mirror.d.ts +1 -1
- package/dist/permission-prompt.d.ts +2 -2
- package/dist/plugin-config.d.ts +12 -0
- package/dist/plugin-management-types.d.ts +31 -0
- package/dist/plugin-management.d.ts +2 -30
- package/dist/plugin-package-install.d.ts +3 -0
- package/dist/{plugin-usage-M6KET4OZ.js → plugin-usage-S5LVGMBW.js} +6 -5
- package/dist/{plugins-WJLW6ULV.js → plugins-RHBIVLI7.js} +4 -3
- package/dist/provider-config-utils.d.ts +1 -6
- package/dist/{providers-models-LFNZ5WI7.js → providers-models-2CFG46RD.js} +41 -31
- package/dist/release-asset-download.d.ts +3 -0
- package/dist/services/dispatch-classifier.d.ts +39 -5
- package/dist/{sessions-config-UL45XKX7.js → sessions-config-IQUH2GXT.js} +13 -5
- package/dist/settings-menu.d.ts +2 -0
- package/dist/short-circuit-flags-SDT53OML.js +10 -0
- package/dist/{simpleui-dist-CXSIDAXZ.js → simpleui-dist-7BQISSEP.js} +7 -3
- package/dist/skill-suggest-L2EVWB6R.js +290 -0
- package/dist/slash-commands/effort.d.ts +1 -1
- package/dist/slash-commands/fallback.d.ts +1 -1
- package/dist/slash-commands/gitid.d.ts +1 -1
- package/dist/slash-commands/jev.d.ts +4 -0
- package/dist/slash-commands/openai-quota.d.ts +15 -0
- package/dist/slash-commands/sdd-task-mutations.d.ts +4 -0
- package/dist/slash-commands/settings-keys.d.ts +2 -0
- package/dist/slash-commands/settings-reset.d.ts +6 -0
- package/dist/standalone-update-MW47J3AK.js +164 -0
- package/dist/standalone-update.d.ts +19 -0
- package/dist/subcommands/handlers/hq-alerts.d.ts +3 -0
- package/dist/subcommands/handlers/hq-audit.d.ts +46 -0
- package/dist/subcommands/handlers/hq-data-dir.d.ts +13 -0
- package/dist/subcommands/handlers/skill-suggest.d.ts +21 -0
- package/dist/subcommands/handlers/typesafe-brain-replay.d.ts +26 -0
- package/dist/subcommands/handlers/typesafe-judgment-check.d.ts +3 -0
- package/dist/subcommands/handlers/typesafe-topic-replay.d.ts +19 -0
- package/dist/subcommands/handlers/typesafe-triage-replay.d.ts +18 -0
- package/dist/subcommands/handlers/typesafe.d.ts +23 -0
- package/dist/{subcommands-XSLXMIDP.js → subcommands-ZTSIN7LG.js} +2 -2
- package/dist/typesafe-HVEVPA6R.js +316 -0
- package/dist/typesafe-brain-replay-C5276FVR.js +222 -0
- package/dist/typesafe-judgment-check-GGYYV5UF.js +29 -0
- package/dist/typesafe-topic-replay-4W73BOLV.js +218 -0
- package/dist/typesafe-triage-replay-FGAW7QM7.js +215 -0
- package/dist/{update-5DPB4YD4.js → update-7XIOEXTD.js} +4 -3
- package/dist/{update-check-BS3DHTSI.js → update-check-2POMBHFB.js} +3 -2
- package/dist/update-check.d.ts +1 -1
- package/dist/version-help-WZ623NWM.js +10 -0
- package/dist/version.d.ts +3 -2
- package/dist/webui-client-observability.d.ts +7 -0
- package/dist/{webui-server-MRPGTTKL.js → webui-server-L2SICPTB.js} +158 -94
- package/dist/webui-server-options.d.ts +11 -0
- package/dist/webui-server.d.ts +15 -1
- package/dist/wiring/domain-glossary.d.ts +5 -4
- package/dist/wiring/plugins.d.ts +1 -1
- package/dist/wiring/provider-persisters.d.ts +24 -0
- package/dist/wiring/provider-runtime.d.ts +9 -1
- package/dist/wiring/runtime-dispatch-state.d.ts +3 -1
- package/package.json +28 -28
- package/dist/chunk-2CZEORLV.js +0 -54
- package/dist/chunk-DR2OXU46.js +0 -174
- package/dist/chunk-JACOMTIH.js +0 -275
- package/dist/chunk-XJXDOF63.js +0 -28
- package/dist/modeldiag-CJSFHZ3J.js +0 -1356
- package/dist/short-circuit-flags-7GQIGMVD.js +0 -10
- 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-
|
|
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-
|
|
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
|
-
|
|
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 =
|
|
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-
|
|
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 {
|
|
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 (
|
|
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) =>
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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-
|
|
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-
|
|
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(
|
|
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-
|
|
128
|
+
//# sourceMappingURL=chunk-ZMX5HRLI.js.map
|