@wrongstack/cli 1.0.19 → 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.
- package/data/providers.json +44 -0
- package/dist/{acp-INCNOH42.js → acp-LI665LO2.js} +18 -7
- package/dist/arg-parser.d.ts +1 -0
- package/dist/ascii-mode-SG4PRNLP.js +7 -0
- package/dist/{auth-YXK3EDUL.js → auth-TE55JF4Z.js} +68 -22
- 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-GTLPM22B.js} +2 -2
- package/dist/boot/append-system-prompt.d.ts +10 -0
- package/dist/boot/ascii-mode.d.ts +12 -0
- package/dist/boot/config-backup-recovery.d.ts +2 -1
- package/dist/boot/container-wiring.d.ts +4 -2
- package/dist/boot/dispatch-singleshot.d.ts +4 -1
- package/dist/boot/dispatch-webui.d.ts +11 -0
- package/dist/boot/mcp-config-flag.d.ts +22 -0
- package/dist/boot/piped-stdin.d.ts +40 -0
- package/dist/boot/restricted-mode.d.ts +15 -0
- package/dist/boot/safe-mode.d.ts +30 -0
- package/dist/boot/short-circuit-desktop.d.ts +1 -0
- package/dist/boot/single-shot-budget.d.ts +30 -0
- package/dist/boot/stream-json.d.ts +36 -0
- package/dist/boot/structured-output.d.ts +16 -0
- package/dist/boot/system-prompt-builder.d.ts +2 -0
- package/dist/boot/tool-restriction-flags.d.ts +36 -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/worktree-sessions.d.ts +21 -0
- package/dist/{boot-auto-thin-MANHOYHE.js → boot-auto-thin-6Q56FY3F.js} +3 -2
- package/dist/boot-provider-selection.d.ts +67 -0
- package/dist/boot.d.ts +1 -38
- package/dist/{chunk-JY52HWL6.js → chunk-2EBVTYJR.js} +2 -2
- package/dist/{chunk-RDHFXOJM.js → chunk-2ERX3RPN.js} +112 -65
- package/dist/chunk-4TVYGG6G.js +62 -0
- package/dist/chunk-5JYDKG5B.js +154 -0
- package/dist/{chunk-KEPABQYT.js → chunk-5SEYGOCH.js} +8 -82
- package/dist/{chunk-3VXYY5JC.js → chunk-5UZBUV2H.js} +3 -19
- package/dist/chunk-BBHWW6LR.js +549 -0
- package/dist/chunk-BEHGRNOQ.js +58 -0
- package/dist/chunk-CZTXF6HY.js +140 -0
- package/dist/{chunk-JX6KL7OF.js → chunk-EPR42SWM.js} +39 -8
- package/dist/chunk-F5DCFAKI.js +36 -0
- package/dist/chunk-HLSHFXTO.js +97 -0
- package/dist/{chunk-CJTBLTS4.js → chunk-KJS5543T.js} +4 -3
- package/dist/{chunk-DE7BZDNK.js → chunk-KOEO4CJG.js} +27 -4
- package/dist/{chunk-SIS37L3W.js → chunk-L2RVEQWO.js} +211 -193
- package/dist/chunk-LBPLTTZ6.js +105 -0
- package/dist/chunk-LDCGZJTC.js +46 -0
- package/dist/chunk-LKPEBZBE.js +143 -0
- package/dist/chunk-LUMETJFO.js +171 -0
- package/dist/{chunk-KK2LB3ZS.js → chunk-OAVBM55Q.js} +9 -5
- package/dist/chunk-PZQBGUVZ.js +18 -0
- package/dist/{chunk-C3Z4N6A6.js → chunk-Q4INH2DG.js} +51 -3
- package/dist/chunk-R4QGL6GB.js +10 -0
- package/dist/{chunk-YLQEGYV4.js → chunk-RZ4ZDRYA.js} +14 -4
- package/dist/chunk-S4FFHDJG.js +93 -0
- package/dist/chunk-SFBFYIOW.js +271 -0
- package/dist/{chunk-RMQNCHGO.js → chunk-TI7KLHYX.js} +136 -11
- package/dist/{chunk-2ENZU66X.js → chunk-WQV2NP57.js} +5 -5
- package/dist/{chunk-B4JLSXZB.js → chunk-WSTVGC2C.js} +73 -45
- package/dist/chunk-X756ZS72.js +329 -0
- package/dist/{chunk-XJ7K67PP.js → chunk-YHYINL47.js} +63 -24
- package/dist/{chunk-2VMJ3E5Z.js → chunk-YTEOAWOJ.js} +6 -2
- package/dist/{chunk-GPPRNAKW.js → chunk-ZMX5HRLI.js} +2 -2
- package/dist/{cli-context-33F74CSZ.js → cli-context-UVGAN3NO.js} +496 -384
- package/dist/cli-entry-point.d.ts +2 -0
- package/dist/{cli-main-FNGMKPBA.js → cli-main-TN3KZ2P6.js} +3561 -2724
- package/dist/cli-update-notice.d.ts +2 -1
- 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-BIZAFIAT.js} +2 -2
- package/dist/execute-deps.d.ts +2 -1
- package/dist/{execution-A6WJLSOC.js → execution-7QIP6GRY.js} +2384 -2055
- package/dist/fleet/host-types.d.ts +3 -1
- package/dist/goal-commands.d.ts +47 -0
- package/dist/goal-host.d.ts +1 -14
- package/dist/{hq-XQWQTRDA.js → hq-GLZ7QESM.js} +277 -261
- package/dist/hq-command-controller.d.ts +1 -1
- package/dist/{hq-server-IIBZQZU4.js → hq-server-YTKDVQPY.js} +2 -2
- package/dist/{hq-service-L5INQFM4.js → hq-service-BMMJAHRT.js} +11 -4
- package/dist/import-claude-code-32PCSIQI.js +236 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +19 -10
- 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-NW5VZG7R.js} +3 -3
- package/dist/{mcp-DC5WSBOO.js → mcp-LBS45HWZ.js} +4 -4
- package/dist/modeldiag-36VODVIH.js +828 -0
- package/dist/{per-subcommand-help-DHTYU524.js → per-subcommand-help-QDAQASPZ.js} +3 -3
- package/dist/permission-prompt-mirror.d.ts +1 -1
- package/dist/permission-prompt.d.ts +16 -2
- package/dist/permission-rules-view.d.ts +18 -0
- package/dist/{permissions-CFDLXETM.js → permissions-PL27UHYD.js} +67 -45
- 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-CIGOOTX3.js} +6 -5
- package/dist/{plugins-WJLW6ULV.js → plugins-HQTXUD6A.js} +6 -3
- package/dist/preflight.d.ts +1 -1
- package/dist/{projects-LMNAGGEU.js → projects-T4CZCA25.js} +3 -6
- package/dist/provider-config-utils.d.ts +1 -6
- package/dist/provider-helpers.d.ts +7 -11
- package/dist/{providers-models-LFNZ5WI7.js → providers-models-5Z2PJTEW.js} +44 -34
- package/dist/release-asset-download.d.ts +13 -0
- package/dist/release-attestation.d.ts +28 -0
- package/dist/remote/remote-session.d.ts +64 -0
- package/dist/remote/remote-target.d.ts +69 -0
- package/dist/remote-O5WXHLWV.js +561 -0
- package/dist/{replay-Z3FTJEMM.js → replay-DEI566CW.js} +2 -2
- package/dist/services/commit-message.d.ts +2 -1
- package/dist/services/dispatch-classifier.d.ts +39 -5
- package/dist/session-move-P2EC2COA.js +44 -0
- package/dist/session-move.d.ts +20 -0
- package/dist/session-stats.d.ts +10 -0
- package/dist/{sessions-config-UL45XKX7.js → sessions-config-SXTTLNXA.js} +47 -9
- package/dist/settings-menu.d.ts +2 -0
- package/dist/short-circuit-flags-74Y5N6CF.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/command-context.d.ts +15 -5
- 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/goal-refiner.d.ts +4 -49
- package/dist/slash-commands/jev.d.ts +4 -0
- package/dist/slash-commands/mcp.d.ts +2 -1
- package/dist/slash-commands/openai-quota.d.ts +15 -0
- package/dist/slash-commands/permissions.d.ts +4 -0
- package/dist/slash-commands/sdd-task-mutations.d.ts +4 -0
- package/dist/slash-commands/settings-context-mutations.d.ts +5 -0
- package/dist/slash-commands/settings-keys.d.ts +2 -0
- package/dist/slash-commands/settings-limits-mutations.d.ts +12 -0
- package/dist/slash-commands/settings-reset.d.ts +6 -0
- package/dist/standalone-auto-update-TOTRGCSA.js +249 -0
- package/dist/standalone-auto-update.d.ts +65 -0
- package/dist/standalone-update-LZO3MNLH.js +24 -0
- package/dist/standalone-update.d.ts +55 -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/import-claude-code.d.ts +42 -0
- package/dist/subcommands/handlers/permissions.d.ts +9 -3
- package/dist/subcommands/handlers/remote.d.ts +6 -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/handlers/version-help.d.ts +5 -0
- package/dist/{subcommands-XSLXMIDP.js → subcommands-Q6ORSB2E.js} +2 -2
- package/dist/tui-resume-callback.d.ts +18 -0
- package/dist/typesafe-U7YB2RRV.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-ZILIU3WS.js +234 -0
- package/dist/typesafe-triage-replay-FGAW7QM7.js +215 -0
- package/dist/{update-5DPB4YD4.js → update-TDZX4IL2.js} +4 -3
- package/dist/{update-check-BS3DHTSI.js → update-check-J5ZVKS73.js} +3 -2
- package/dist/update-check.d.ts +1 -1
- package/dist/version-JB2UAV2A.js +11 -0
- package/dist/version-help-XJJQ7YSH.js +10 -0
- package/dist/version.d.ts +3 -2
- package/dist/webui-client-observability.d.ts +7 -0
- package/dist/webui-server/route-contexts.d.ts +1 -1
- package/dist/webui-server/session-start-payload.d.ts +5 -1
- package/dist/{webui-server-MRPGTTKL.js → webui-server-YTHLRFD2.js} +577 -370
- package/dist/webui-server-lifecycle.d.ts +57 -0
- package/dist/webui-server-options.d.ts +11 -0
- package/dist/webui-server.d.ts +1 -1
- package/dist/wiring/brain-and-orchestration.d.ts +3 -1
- package/dist/wiring/cli-prompt-and-tools-setup.d.ts +9 -0
- package/dist/wiring/cli-slash-commands-setup.d.ts +1 -0
- package/dist/wiring/domain-glossary.d.ts +5 -4
- package/dist/wiring/lifecycle-plugins.d.ts +4 -2
- package/dist/wiring/metrics.d.ts +9 -1
- package/dist/wiring/pipeline.d.ts +2 -7
- package/dist/wiring/plugins.d.ts +4 -1
- package/dist/wiring/provider-persisters.d.ts +24 -0
- package/dist/wiring/provider-runtime.d.ts +9 -1
- package/dist/wiring/provider-status.d.ts +1 -0
- package/dist/wiring/provider-utility-tools.d.ts +3 -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-672UXYL3.js +0 -65
- package/dist/chunk-6WRKAACA.js +0 -96
- 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,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
|
|
@@ -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) {
|
|
@@ -17,7 +126,6 @@ function renderDiff(diff) {
|
|
|
17
126
|
}
|
|
18
127
|
|
|
19
128
|
// src/permission-prompt.ts
|
|
20
|
-
import { alwaysAllowUnavailableReason } from "@wrongstack/core/security";
|
|
21
129
|
import {
|
|
22
130
|
color as color2,
|
|
23
131
|
sanitizeTerminalPreview,
|
|
@@ -26,6 +134,15 @@ import {
|
|
|
26
134
|
unifiedDiff,
|
|
27
135
|
writeOut
|
|
28
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
|
+
}
|
|
29
146
|
function makePromptDelegate(reader) {
|
|
30
147
|
return async (tool, input, suggestedPattern, signal) => {
|
|
31
148
|
if (signal?.aborted) return "no";
|
|
@@ -41,18 +158,15 @@ ${theme.warn("\u26A0 APPROVAL REQUIRED")} ${theme.primary("\u2502")} ${theme.bol
|
|
|
41
158
|
if (preview) writeOut(`${preview}
|
|
42
159
|
`);
|
|
43
160
|
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
161
|
const options = [
|
|
50
162
|
{ key: "y", label: "yes", value: "yes" },
|
|
51
163
|
{ key: "n", label: "no", value: "no" },
|
|
52
|
-
|
|
164
|
+
{ key: "a", label: "remember same input/args", value: "always-exact" },
|
|
165
|
+
...tool.name === "exec" ? [{ key: "c", label: "remember executable with any args", value: "always-command" }] : [],
|
|
166
|
+
{ key: "t", label: "remember tool with any input", value: "always-tool" },
|
|
53
167
|
{ key: "d", label: "deny", value: "deny" }
|
|
54
168
|
];
|
|
55
|
-
const alwaysHint =
|
|
169
|
+
const alwaysHint = ` ${theme.bold("[a]")} same input (${sanitizeTerminalText2(suggestedPattern)})${tool.name === "exec" ? " [c] command, any args" : ""} [t] tool, any input`;
|
|
56
170
|
const answer = await reader.readKey(
|
|
57
171
|
`${theme.bold("[y]")}es ${theme.bold("[n]")}o${alwaysHint} ${theme.bold("[d]")}eny: `,
|
|
58
172
|
options,
|
|
@@ -61,8 +175,11 @@ ${theme.warn("\u26A0 APPROVAL REQUIRED")} ${theme.primary("\u2502")} ${theme.bol
|
|
|
61
175
|
return answer;
|
|
62
176
|
};
|
|
63
177
|
}
|
|
178
|
+
function makeStdinPromptDelegate(reader, stdinIsTTY = process.stdin.isTTY === true) {
|
|
179
|
+
return stdinIsTTY ? makePromptDelegate(reader) : makeHeadlessPromptDelegate();
|
|
180
|
+
}
|
|
64
181
|
function makeConfirmAwaiter(reader) {
|
|
65
|
-
const delegate =
|
|
182
|
+
const delegate = makeStdinPromptDelegate(reader);
|
|
66
183
|
return async (tool, input, _toolUseId, suggestedPattern) => {
|
|
67
184
|
const result = await delegate(tool, input, suggestedPattern);
|
|
68
185
|
return result;
|
|
@@ -474,12 +591,20 @@ on disconnect with exponential backoff, up to 3 attempts"._
|
|
|
474
591
|
|
|
475
592
|
export {
|
|
476
593
|
renderDiff,
|
|
477
|
-
|
|
594
|
+
makeStdinPromptDelegate,
|
|
478
595
|
makeConfirmAwaiter,
|
|
596
|
+
resolveLaunchAllowedTools,
|
|
597
|
+
resolveToolRestriction,
|
|
598
|
+
unknownRestrictedToolNames,
|
|
599
|
+
isRestrictedMode,
|
|
600
|
+
withRestrictedTools,
|
|
601
|
+
activateRestrictedMode,
|
|
602
|
+
isSafeMode,
|
|
603
|
+
announceSafeMode,
|
|
479
604
|
isSimpleUiFullAuto,
|
|
480
605
|
applySimpleUiFullAutoProfile,
|
|
481
606
|
configureSimpleUiRuntimeContext,
|
|
482
607
|
detectProjectFacts,
|
|
483
608
|
renderAgentsTemplate
|
|
484
609
|
};
|
|
485
|
-
//# sourceMappingURL=chunk-
|
|
610
|
+
//# sourceMappingURL=chunk-TI7KLHYX.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
helpCmd,
|
|
3
3
|
versionCmd
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5JYDKG5B.js";
|
|
5
5
|
import {
|
|
6
6
|
parseArgs
|
|
7
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
46
|
+
//# sourceMappingURL=chunk-WQV2NP57.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
|