@wrongstack/cli 1.1.4 → 1.1.6
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-MZ5MQBFN.js → acp-CZBSFQB7.js} +6 -5
- package/dist/acp-leader/acp-leader-confirm.d.ts +2 -0
- package/dist/acp-leader/acp-leader-host-binding.d.ts +1 -0
- package/dist/acp-leader/acp-leader-host.d.ts +9 -0
- package/dist/acp-leader/acp-leader-picker.d.ts +6 -3
- package/dist/acp-leader/acp-leader-provider.d.ts +10 -0
- package/dist/acp-leader/acp-leader-skills.d.ts +5 -0
- package/dist/acp-leader/acp-leader-stream.d.ts +9 -0
- package/dist/{auth-IHQS6AFW.js → auth-2ZG5DBHS.js} +4 -4
- package/dist/boot/launch-menu-arrow.d.ts +20 -0
- package/dist/boot/launch-menu-types.d.ts +36 -0
- package/dist/boot/launch-menu.d.ts +1 -57
- package/dist/boot/tui-session-resume-rollback.d.ts +31 -0
- package/dist/{boot-auto-thin-7D4OVEWA.js → boot-auto-thin-N2CIGTXE.js} +3 -3
- package/dist/{chunk-TZPKOS5G.js → chunk-2XQ6SRKG.js} +2 -2
- package/dist/{chunk-3EXRPTAU.js → chunk-5TQ65PXQ.js} +17 -17
- package/dist/{chunk-VAQXOZKP.js → chunk-AJM4EFCM.js} +139 -136
- package/dist/{chunk-C2BBH4S4.js → chunk-E3MJFAFR.js} +2 -2
- package/dist/{chunk-COGI7EQU.js → chunk-EAATL6FL.js} +713 -21
- package/dist/{chunk-AWRZWPJ3.js → chunk-FWUIKCCO.js} +152 -123
- package/dist/{chunk-TAE3EGBM.js → chunk-H5DCQFUI.js} +5 -5
- package/dist/{chunk-BIGSVBPI.js → chunk-KNYIEQAY.js} +64 -61
- package/dist/chunk-LYWOV4OX.js +252 -0
- package/dist/{chunk-75OLD563.js → chunk-M7PSENGF.js} +3 -1
- package/dist/{chunk-I5LJLKA3.js → chunk-MKMM5RIW.js} +12 -5
- package/dist/{chunk-ZF4HRR5F.js → chunk-P7MEEUGT.js} +275 -250
- package/dist/chunk-PGC44T7N.js +255 -0
- package/dist/chunk-QLYPIJ7X.js +258 -0
- package/dist/{chunk-JKTJSLUT.js → chunk-QRWXHGLB.js} +242 -18
- package/dist/chunk-RN5CFAVQ.js +292 -0
- package/dist/{chunk-WDXNBYGD.js → chunk-TDP7CZGE.js} +6 -1
- package/dist/{chunk-6VPWQ4TG.js → chunk-UROB7MK5.js} +2 -1
- package/dist/{chunk-IJLIAPCW.js → chunk-VPSAKGLK.js} +47 -47
- package/dist/{chunk-2LTHDKZX.js → chunk-W7X23HI6.js} +18 -18
- package/dist/{chunk-GKGWPFW2.js → chunk-X4DEQLAZ.js} +1 -1
- package/dist/{cli-context-E44HQ4ZO.js → cli-context-6KS3HV5R.js} +247 -236
- package/dist/{cli-main-OT63I2FX.js → cli-main-NF3YGDME.js} +1779 -1494
- package/dist/cli-main-boot.d.ts +1 -1
- package/dist/config-doctor-known-keys.d.ts +33 -0
- package/dist/config-doctor.d.ts +2 -47
- package/dist/config-history-support.d.ts +36 -0
- package/dist/config-history.d.ts +1 -1
- package/dist/{config-transfer-Y7XJMJRN.js → config-transfer-B4GOZTFC.js} +2 -2
- package/dist/{execution-OK34CJKJ.js → execution-RVDUXLNT.js} +6522 -6426
- package/dist/execution-tui.d.ts +30 -0
- package/dist/fleet/acp-wrongstack-bridge.d.ts +11 -3
- package/dist/fleet/host-acp.d.ts +3 -0
- package/dist/fleet/wrongforge-client.d.ts +116 -0
- package/dist/fleet/wrongforge-gateway.d.ts +20 -0
- package/dist/fleet/wrongforge-info.d.ts +32 -0
- package/dist/fleet/wrongforge-quota.d.ts +10 -0
- package/dist/fleet/wrongforge-runner.d.ts +37 -0
- package/dist/fleet/wrongforge-runtime.d.ts +10 -0
- package/dist/{hq-JD5AZVMG.js → hq-V2M4IYCI.js} +320 -306
- package/dist/hq-server/login-attempt-store.d.ts +2 -0
- package/dist/hq-server/sweeps.d.ts +28 -0
- package/dist/{hq-server-BI23EPM7.js → hq-server-QM7EIVGJ.js} +3 -3
- package/dist/index.js +5 -5
- package/dist/mcp-22CFBX4H.js +236 -0
- package/dist/mcp-serve.d.ts +0 -63
- package/dist/{modeldiag-HDIKG37X.js → modeldiag-5PBB777J.js} +4 -4
- package/dist/{per-subcommand-help-4GYSCMLU.js → per-subcommand-help-MQGERD4E.js} +2 -2
- package/dist/plugin-llm-command.d.ts +9 -0
- package/dist/{plugin-usage-TYF5HY4K.js → plugin-usage-UWKYMNQU.js} +3 -3
- package/dist/{plugins-XGAXDLPT.js → plugins-VP7JQH47.js} +4 -4
- package/dist/{sdd-runtime-6MFUXWJK.js → sdd-runtime-HIJ4Z5TF.js} +2 -2
- package/dist/services/sdd/state.d.ts +2 -0
- package/dist/session-event-wiring-types.d.ts +62 -0
- package/dist/session-event-wiring.d.ts +2 -52
- package/dist/{sessions-config-SJVQXCIE.js → sessions-config-7MK742OW.js} +2 -2
- package/dist/short-circuit-flags-276OLPIN.js +11 -0
- package/dist/slash-commands/command-context-fleet.d.ts +187 -0
- package/dist/slash-commands/command-context-sdd-goal.d.ts +102 -0
- package/dist/slash-commands/command-context.d.ts +3 -282
- package/dist/slash-commands/project-switch-interactive.d.ts +11 -0
- package/dist/slash-commands/setmodel-helpers.d.ts +36 -0
- package/dist/subcommands/handlers/hq-token.d.ts +3 -0
- package/dist/subcommands/handlers/hq.d.ts +0 -21
- package/dist/subcommands/handlers/per-subcommand-help-operations.d.ts +8 -0
- package/dist/subcommands/handlers/tools-cli-args.d.ts +57 -0
- package/dist/subcommands/handlers/tools-cli.d.ts +5 -0
- package/dist/subcommands/handlers/tools-serve.d.ts +4 -0
- package/dist/{subcommands-YADUV7IJ.js → subcommands-SKJPUJ7N.js} +2 -2
- package/dist/tool-gateway-http.d.ts +30 -0
- package/dist/tool-gateway.d.ts +185 -0
- package/dist/tools-cli-44MLIUQO.js +509 -0
- package/dist/tools-serve-TRCET2M7.js +102 -0
- package/dist/{tools-skills-55QVTMSK.js → tools-skills-WDTMCE46.js} +7 -2
- package/dist/tuneup-runtime-checks.d.ts +7 -0
- package/dist/tuneup-types.d.ts +118 -0
- package/dist/tuneup.d.ts +3 -122
- package/dist/{typesafe-YBEADNAH.js → typesafe-QLSMFU7J.js} +1 -1
- package/dist/{version-help-RBZC5GZZ.js → version-help-X6CC4CFY.js} +2 -2
- package/dist/{webui-server-CB64CFDG.js → webui-server-6I6QOECV.js} +44 -6
- package/dist/wiring/provider-runtime-setup.d.ts +3 -1
- package/dist/wiring/session-runtime.d.ts +1 -1
- package/dist/wrongforge-gateway-UOFBK56J.js +92 -0
- package/dist/wrongforge-leader/wrongforge-leader-gateway.d.ts +9 -0
- package/dist/wrongforge-leader/wrongforge-leader-picker.d.ts +12 -0
- package/dist/wrongforge-leader/wrongforge-leader-provider.d.ts +56 -0
- package/package.json +1 -1
- package/dist/mcp-W7A5HUXV.js +0 -560
- package/dist/short-circuit-flags-QEXUS3BY.js +0 -11
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
sanitizeApiError,
|
|
25
25
|
truncateHqSummary,
|
|
26
26
|
writeInvalidBody
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-5TQ65PXQ.js";
|
|
28
28
|
import {
|
|
29
29
|
createHqIpAllowlist
|
|
30
30
|
} from "./chunk-OGVSKS4Q.js";
|
|
@@ -201,6 +201,7 @@ var LoginAttemptStore = class _LoginAttemptStore {
|
|
|
201
201
|
store = /* @__PURE__ */ new Map();
|
|
202
202
|
writeTimer;
|
|
203
203
|
dirty = false;
|
|
204
|
+
writeTail = Promise.resolve();
|
|
204
205
|
filePath;
|
|
205
206
|
debounceMs;
|
|
206
207
|
retentionMs;
|
|
@@ -339,9 +340,7 @@ var LoginAttemptStore = class _LoginAttemptStore {
|
|
|
339
340
|
clearTimeout(this.writeTimer);
|
|
340
341
|
this.writeTimer = void 0;
|
|
341
342
|
}
|
|
342
|
-
|
|
343
|
-
await this.writeToDisk();
|
|
344
|
-
}
|
|
343
|
+
await this.writeToDisk();
|
|
345
344
|
}
|
|
346
345
|
scheduleWrite() {
|
|
347
346
|
this.dirty = true;
|
|
@@ -352,7 +351,14 @@ var LoginAttemptStore = class _LoginAttemptStore {
|
|
|
352
351
|
}, this.debounceMs);
|
|
353
352
|
this.writeTimer.unref?.();
|
|
354
353
|
}
|
|
355
|
-
|
|
354
|
+
writeToDisk() {
|
|
355
|
+
const pending = this.writeTail.then(() => {
|
|
356
|
+
return this.dirty ? this.writeSnapshotToDisk() : void 0;
|
|
357
|
+
});
|
|
358
|
+
this.writeTail = pending.catch(() => void 0);
|
|
359
|
+
return pending;
|
|
360
|
+
}
|
|
361
|
+
async writeSnapshotToDisk() {
|
|
356
362
|
this.dirty = false;
|
|
357
363
|
const now = Date.now();
|
|
358
364
|
const cutoff = now - this.retentionMs;
|
|
@@ -3733,52 +3739,51 @@ ${terminalText("Reachable from other machines on your network:", "blue", { bold:
|
|
|
3733
3739
|
|
|
3734
3740
|
// src/hq-server/server-lifecycle.ts
|
|
3735
3741
|
function createHqServerShutdown(params) {
|
|
3736
|
-
let
|
|
3737
|
-
return () =>
|
|
3738
|
-
if (
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
ws.terminate();
|
|
3756
|
-
} catch {
|
|
3742
|
+
let closing;
|
|
3743
|
+
return () => {
|
|
3744
|
+
if (closing) return closing;
|
|
3745
|
+
closing = new Promise((res) => {
|
|
3746
|
+
clearInterval(params.cleanupTimer);
|
|
3747
|
+
clearInterval(params.browserHeartbeatTimer);
|
|
3748
|
+
clearInterval(params.timeseriesFlushTimer);
|
|
3749
|
+
clearInterval(params.sessionCleanupTimer);
|
|
3750
|
+
params.stopAlertEngine();
|
|
3751
|
+
params.authWatcher.close();
|
|
3752
|
+
params.snapshotBroadcaster.close();
|
|
3753
|
+
params.bootstrapStore.clear();
|
|
3754
|
+
params.persistence.timeseries.flush();
|
|
3755
|
+
for (const ws of [...params.browsers, ...params.clients.keys()]) {
|
|
3756
|
+
try {
|
|
3757
|
+
ws.close(1001, "HQ shutting down");
|
|
3758
|
+
ws.terminate();
|
|
3759
|
+
} catch {
|
|
3760
|
+
}
|
|
3757
3761
|
}
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
params.
|
|
3779
|
-
|
|
3762
|
+
params.wss.close();
|
|
3763
|
+
params.mailboxManager.close();
|
|
3764
|
+
params.httpServer.close(() => {
|
|
3765
|
+
const mailboxCloses = [...params.mailboxManager.mailboxGateways.values()].map(
|
|
3766
|
+
({ mailbox }) => mailbox.close().catch(() => void 0)
|
|
3767
|
+
);
|
|
3768
|
+
params.mailboxManager.mailboxGateways.clear();
|
|
3769
|
+
params.mailboxManager.mailboxGatewayLastUsed.clear();
|
|
3770
|
+
void Promise.allSettled([
|
|
3771
|
+
...mailboxCloses,
|
|
3772
|
+
// Chimera/race: flush the login-attempt store INSIDE the awaited
|
|
3773
|
+
// shutdown set — the old fire-and-forget `void flush()` let close()
|
|
3774
|
+
// resolve before the debounced write landed, so persistence tests
|
|
3775
|
+
// had to sleep past it (and restarts could miss lockout state).
|
|
3776
|
+
params.loginAttempts.flush(),
|
|
3777
|
+
params.persistence.eventLog.drain(),
|
|
3778
|
+
params.persistence.snapshotStore.drain(),
|
|
3779
|
+
params.persistence.timeseries.drain(),
|
|
3780
|
+
params.persistence.kanban.drain(),
|
|
3781
|
+
params.persistence.sage?.drain()
|
|
3782
|
+
]).then(() => clearHqRuntimeMarker(params.dataDir, params.hqUrl)).catch(() => void 0).finally(() => res());
|
|
3783
|
+
});
|
|
3780
3784
|
});
|
|
3781
|
-
|
|
3785
|
+
return closing;
|
|
3786
|
+
};
|
|
3782
3787
|
}
|
|
3783
3788
|
|
|
3784
3789
|
// src/hq-server/socket-credentials.ts
|
|
@@ -3839,9 +3844,6 @@ function createHqSocketCredentialEnforcer(state) {
|
|
|
3839
3844
|
};
|
|
3840
3845
|
}
|
|
3841
3846
|
|
|
3842
|
-
// src/hq-server/upgrade-handler.ts
|
|
3843
|
-
import { hqTokenVerifier as hqTokenVerifier2, isLoopbackHost as isLoopbackHost3, isTokenExpired as isTokenExpired5 } from "@wrongstack/core/hq";
|
|
3844
|
-
|
|
3845
3847
|
// src/hq-server/ws.ts
|
|
3846
3848
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
3847
3849
|
import {
|
|
@@ -4706,7 +4708,91 @@ function handleClient(ws, clients, browsers, eventLog, auth, snapshotBroadcaster
|
|
|
4706
4708
|
});
|
|
4707
4709
|
}
|
|
4708
4710
|
|
|
4711
|
+
// src/hq-server/sweeps.ts
|
|
4712
|
+
var HQ_SESSION_MAX_AGE_MS2 = 7 * 24 * 60 * 60 * 1e3;
|
|
4713
|
+
var HQ_SESSION_IDLE_TIMEOUT_MS2 = 30 * 6e4;
|
|
4714
|
+
var PENDING_2FA_TTL_MS2 = 5 * 6e4;
|
|
4715
|
+
var HQ_SESSION_CLEANUP_INTERVAL_MS = 6e4;
|
|
4716
|
+
function sweepHqBrowserSessions(sessions, loginAttempts, socketCredentials) {
|
|
4717
|
+
const now = Date.now();
|
|
4718
|
+
const maxAgeCutoff = now - HQ_SESSION_MAX_AGE_MS2;
|
|
4719
|
+
const idleCutoff = now - HQ_SESSION_IDLE_TIMEOUT_MS2;
|
|
4720
|
+
const pending2faCutoff = now - PENDING_2FA_TTL_MS2;
|
|
4721
|
+
for (const [id, session] of sessions) {
|
|
4722
|
+
if (session.createdAt < maxAgeCutoff) {
|
|
4723
|
+
sessions.delete(id);
|
|
4724
|
+
socketCredentials.closeSessionSockets(id, "Browser session expired");
|
|
4725
|
+
continue;
|
|
4726
|
+
}
|
|
4727
|
+
if (session.pending2fa) {
|
|
4728
|
+
if (session.createdAt < pending2faCutoff) sessions.delete(id);
|
|
4729
|
+
continue;
|
|
4730
|
+
}
|
|
4731
|
+
if (session.lastSeenAt < idleCutoff) {
|
|
4732
|
+
sessions.delete(id);
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4735
|
+
for (const [ip, entry] of loginAttempts.entries()) {
|
|
4736
|
+
if (now - entry.lastAttempt > 15 * 6e4) loginAttempts.delete(ip);
|
|
4737
|
+
}
|
|
4738
|
+
}
|
|
4739
|
+
function sweepStaleHqClients({
|
|
4740
|
+
clients,
|
|
4741
|
+
browsers,
|
|
4742
|
+
auditLog,
|
|
4743
|
+
eventLog,
|
|
4744
|
+
persistence,
|
|
4745
|
+
clientTtlMs,
|
|
4746
|
+
sessionSnapshotTtlMs
|
|
4747
|
+
}) {
|
|
4748
|
+
const cutoff = Date.now() - clientTtlMs;
|
|
4749
|
+
const sessionCutoff = Date.now() - sessionSnapshotTtlMs;
|
|
4750
|
+
let changed = false;
|
|
4751
|
+
for (const [ws, client] of clients.entries()) {
|
|
4752
|
+
if (new Date(client.lastSeenAt).getTime() < cutoff) {
|
|
4753
|
+
const lostClient = client;
|
|
4754
|
+
ws.terminate();
|
|
4755
|
+
clients.delete(ws);
|
|
4756
|
+
failUndeliveredCommands(lostClient, browsers, auditLog);
|
|
4757
|
+
detectLeaderLoss(lostClient, clients, browsers, "heartbeat-timeout", {
|
|
4758
|
+
eventLog,
|
|
4759
|
+
persistence
|
|
4760
|
+
});
|
|
4761
|
+
changed = true;
|
|
4762
|
+
continue;
|
|
4763
|
+
}
|
|
4764
|
+
const expiredSessions = /* @__PURE__ */ new Set();
|
|
4765
|
+
for (const [sessionId, tracked] of client.sessions.entries()) {
|
|
4766
|
+
if (tracked.receivedAt < sessionCutoff) {
|
|
4767
|
+
client.sessions.delete(sessionId);
|
|
4768
|
+
client.mcpSnapshots.delete(sessionId);
|
|
4769
|
+
expiredSessions.add(sessionId);
|
|
4770
|
+
changed = true;
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
for (const [runId, fleet] of client.fleets) {
|
|
4774
|
+
if (fleet.sessionId !== void 0 && expiredSessions.has(fleet.sessionId)) {
|
|
4775
|
+
client.fleets.delete(runId);
|
|
4776
|
+
changed = true;
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4779
|
+
if (client.capabilities.includes("session.summary") && client.sessions.size === 0 && Date.now() - Date.parse(client.connectedAt) > sessionSnapshotTtlMs) {
|
|
4780
|
+
const lostClient = client;
|
|
4781
|
+
ws.terminate();
|
|
4782
|
+
clients.delete(ws);
|
|
4783
|
+
failUndeliveredCommands(lostClient, browsers, auditLog);
|
|
4784
|
+
detectLeaderLoss(lostClient, clients, browsers, "crash", {
|
|
4785
|
+
eventLog,
|
|
4786
|
+
persistence
|
|
4787
|
+
});
|
|
4788
|
+
changed = true;
|
|
4789
|
+
}
|
|
4790
|
+
}
|
|
4791
|
+
return changed;
|
|
4792
|
+
}
|
|
4793
|
+
|
|
4709
4794
|
// src/hq-server/upgrade-handler.ts
|
|
4795
|
+
import { hqTokenVerifier as hqTokenVerifier2, isLoopbackHost as isLoopbackHost3, isTokenExpired as isTokenExpired5 } from "@wrongstack/core/hq";
|
|
4710
4796
|
function handleHqUpgrade(req, socket, head, deps) {
|
|
4711
4797
|
const url = new URL(req.url ?? "/", `http://${deps.host}:${deps.port}`);
|
|
4712
4798
|
const pathname = url.pathname;
|
|
@@ -4964,33 +5050,10 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
|
|
|
4964
5050
|
mutableAuth,
|
|
4965
5051
|
sessions
|
|
4966
5052
|
});
|
|
4967
|
-
const
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
const sessionCleanupTimer = setInterval(() => {
|
|
4972
|
-
const now = Date.now();
|
|
4973
|
-
const maxAgeCutoff = now - HQ_SESSION_MAX_AGE_MS2;
|
|
4974
|
-
const idleCutoff = now - HQ_SESSION_IDLE_TIMEOUT_MS2;
|
|
4975
|
-
const pending2faCutoff = now - PENDING_2FA_TTL_MS2;
|
|
4976
|
-
for (const [id, session] of sessions) {
|
|
4977
|
-
if (session.createdAt < maxAgeCutoff) {
|
|
4978
|
-
sessions.delete(id);
|
|
4979
|
-
socketCredentials.closeSessionSockets(id, "Browser session expired");
|
|
4980
|
-
continue;
|
|
4981
|
-
}
|
|
4982
|
-
if (session.pending2fa) {
|
|
4983
|
-
if (session.createdAt < pending2faCutoff) sessions.delete(id);
|
|
4984
|
-
continue;
|
|
4985
|
-
}
|
|
4986
|
-
if (session.lastSeenAt < idleCutoff) {
|
|
4987
|
-
sessions.delete(id);
|
|
4988
|
-
}
|
|
4989
|
-
}
|
|
4990
|
-
for (const [ip, entry] of loginAttempts.entries()) {
|
|
4991
|
-
if (now - entry.lastAttempt > 15 * 6e4) loginAttempts.delete(ip);
|
|
4992
|
-
}
|
|
4993
|
-
}, SESSION_CLEANUP_INTERVAL_MS);
|
|
5053
|
+
const sessionCleanupTimer = setInterval(
|
|
5054
|
+
() => sweepHqBrowserSessions(sessions, loginAttempts, socketCredentials),
|
|
5055
|
+
HQ_SESSION_CLEANUP_INTERVAL_MS
|
|
5056
|
+
);
|
|
4994
5057
|
sessionCleanupTimer.unref?.();
|
|
4995
5058
|
const persistence = createHqPersistence(dataDir);
|
|
4996
5059
|
const auditLog = new HqCommandAuditLog(1e3, (entry) => persistence.commandLog.append(entry));
|
|
@@ -5065,49 +5128,15 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
|
|
|
5065
5128
|
const sessionSnapshotTtlMs = options.sessionSnapshotTtlMs ?? SESSION_SNAPSHOT_TTL_MS;
|
|
5066
5129
|
const cleanupTimer = setInterval(() => {
|
|
5067
5130
|
socketCredentials.sweepExpiredSocketCredentials();
|
|
5068
|
-
const
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
detectLeaderLoss(lostClient, clients, browsers, "heartbeat-timeout", {
|
|
5078
|
-
eventLog,
|
|
5079
|
-
persistence
|
|
5080
|
-
});
|
|
5081
|
-
changed = true;
|
|
5082
|
-
continue;
|
|
5083
|
-
}
|
|
5084
|
-
const expiredSessions = /* @__PURE__ */ new Set();
|
|
5085
|
-
for (const [sessionId, tracked] of client.sessions.entries()) {
|
|
5086
|
-
if (tracked.receivedAt < sessionCutoff) {
|
|
5087
|
-
client.sessions.delete(sessionId);
|
|
5088
|
-
client.mcpSnapshots.delete(sessionId);
|
|
5089
|
-
expiredSessions.add(sessionId);
|
|
5090
|
-
changed = true;
|
|
5091
|
-
}
|
|
5092
|
-
}
|
|
5093
|
-
for (const [runId, fleet] of client.fleets) {
|
|
5094
|
-
if (fleet.sessionId !== void 0 && expiredSessions.has(fleet.sessionId)) {
|
|
5095
|
-
client.fleets.delete(runId);
|
|
5096
|
-
changed = true;
|
|
5097
|
-
}
|
|
5098
|
-
}
|
|
5099
|
-
if (client.capabilities.includes("session.summary") && client.sessions.size === 0 && Date.now() - Date.parse(client.connectedAt) > sessionSnapshotTtlMs) {
|
|
5100
|
-
const lostClient = client;
|
|
5101
|
-
ws.terminate();
|
|
5102
|
-
clients.delete(ws);
|
|
5103
|
-
failUndeliveredCommands(lostClient, browsers, auditLog);
|
|
5104
|
-
detectLeaderLoss(lostClient, clients, browsers, "crash", {
|
|
5105
|
-
eventLog,
|
|
5106
|
-
persistence
|
|
5107
|
-
});
|
|
5108
|
-
changed = true;
|
|
5109
|
-
}
|
|
5110
|
-
}
|
|
5131
|
+
const changed = sweepStaleHqClients({
|
|
5132
|
+
clients,
|
|
5133
|
+
browsers,
|
|
5134
|
+
auditLog,
|
|
5135
|
+
eventLog,
|
|
5136
|
+
persistence,
|
|
5137
|
+
clientTtlMs,
|
|
5138
|
+
sessionSnapshotTtlMs
|
|
5139
|
+
});
|
|
5111
5140
|
if (changed) snapshotBroadcaster.broadcast();
|
|
5112
5141
|
}, options.clientCleanupIntervalMs ?? CLIENT_CLEANUP_INTERVAL_MS);
|
|
5113
5142
|
cleanupTimer.unref?.();
|
|
@@ -5361,4 +5390,4 @@ export {
|
|
|
5361
5390
|
startHqServer,
|
|
5362
5391
|
HqInsecureExposureError2 as HqInsecureExposureError
|
|
5363
5392
|
};
|
|
5364
|
-
//# sourceMappingURL=chunk-
|
|
5393
|
+
//# sourceMappingURL=chunk-FWUIKCCO.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseArgs
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-M7PSENGF.js";
|
|
4
4
|
import {
|
|
5
5
|
helpCmd,
|
|
6
6
|
versionCmd
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UROB7MK5.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-SKJPUJ7N.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-MQGERD4E.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-H5DCQFUI.js.map
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
|
|
2
2
|
// src/config-history.ts
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
|
+
import * as fs2 from "node:fs/promises";
|
|
5
|
+
import * as path2 from "node:path";
|
|
6
|
+
import { isSecretField as isSecretField2 } from "@wrongstack/core/security";
|
|
7
|
+
import { ERROR_CODES as ERROR_CODES2, FsError as FsError2 } from "@wrongstack/core/types";
|
|
8
|
+
import { atomicWrite, toErrorMessage, withFileLock, writeErr } from "@wrongstack/core/utils";
|
|
9
|
+
|
|
10
|
+
// src/config-history-support.ts
|
|
4
11
|
import * as fs from "node:fs/promises";
|
|
5
12
|
import os from "node:os";
|
|
6
13
|
import * as path from "node:path";
|
|
7
14
|
import { isSecretField } from "@wrongstack/core/security";
|
|
8
15
|
import { ERROR_CODES, FsError } from "@wrongstack/core/types";
|
|
9
|
-
import {
|
|
10
|
-
atomicWrite,
|
|
11
|
-
resolveWstackPaths,
|
|
12
|
-
toErrorMessage,
|
|
13
|
-
withFileLock,
|
|
14
|
-
writeErr
|
|
15
|
-
} from "@wrongstack/core/utils";
|
|
16
|
+
import { resolveWstackPaths } from "@wrongstack/core/utils";
|
|
16
17
|
var defaultUidFn = () => os.userInfo().uid;
|
|
17
18
|
async function getFileUid(filePath) {
|
|
18
19
|
try {
|
|
@@ -32,8 +33,6 @@ async function checkConfigOwnership(homeFn, uidFn = defaultUidFn, targetConfigPa
|
|
|
32
33
|
return fileUid === callerUid;
|
|
33
34
|
}
|
|
34
35
|
var PROTECTED_BASENAMES = /* @__PURE__ */ new Set(["config.json", ".key", "index.json"]);
|
|
35
|
-
var MAX_CONFIG_BACKUPS = 10;
|
|
36
|
-
var MAX_CONFIG_HISTORY_ENTRIES = 50;
|
|
37
36
|
function assertSafeToDelete(filename, parentDir) {
|
|
38
37
|
if (PROTECTED_BASENAMES.has(filename)) {
|
|
39
38
|
throw new FsError({
|
|
@@ -72,19 +71,6 @@ function assertSafeToDelete(filename, parentDir) {
|
|
|
72
71
|
});
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
|
-
async function safeDelete(filePath) {
|
|
76
|
-
const dir = path.dirname(filePath);
|
|
77
|
-
const filename = path.basename(filePath);
|
|
78
|
-
try {
|
|
79
|
-
assertSafeToDelete(filename, dir);
|
|
80
|
-
await fs.unlink(filePath);
|
|
81
|
-
} catch (err) {
|
|
82
|
-
if (err instanceof Error && err.message.startsWith("Refusing")) {
|
|
83
|
-
writeErr(`[config-history] SAFETY: ${err.message}
|
|
84
|
-
`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
74
|
var REDACTED = "[REDACTED]";
|
|
89
75
|
function maskValue(v) {
|
|
90
76
|
if (Array.isArray(v)) return v.map(maskValue);
|
|
@@ -130,6 +116,31 @@ function reviveSecrets(masked, current) {
|
|
|
130
116
|
}
|
|
131
117
|
return out;
|
|
132
118
|
}
|
|
119
|
+
var defaultHomeDir = () => os.homedir();
|
|
120
|
+
function activeProfileConfigPath(homeFn) {
|
|
121
|
+
const paths = homeFn === defaultHomeDir ? resolveWstackPaths({ projectRoot: process.cwd() }) : resolveWstackPaths({ projectRoot: process.cwd(), userHome: homeFn() });
|
|
122
|
+
return paths.profileConfig(paths.profileName);
|
|
123
|
+
}
|
|
124
|
+
function configPath(homeFn = defaultHomeDir, targetConfigPath) {
|
|
125
|
+
return targetConfigPath ?? activeProfileConfigPath(homeFn);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/config-history.ts
|
|
129
|
+
var MAX_CONFIG_BACKUPS = 10;
|
|
130
|
+
var MAX_CONFIG_HISTORY_ENTRIES = 50;
|
|
131
|
+
async function safeDelete(filePath) {
|
|
132
|
+
const dir = path2.dirname(filePath);
|
|
133
|
+
const filename = path2.basename(filePath);
|
|
134
|
+
try {
|
|
135
|
+
assertSafeToDelete(filename, dir);
|
|
136
|
+
await fs2.unlink(filePath);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
if (err instanceof Error && err.message.startsWith("Refusing")) {
|
|
139
|
+
writeErr(`[config-history] SAFETY: ${err.message}
|
|
140
|
+
`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
133
144
|
function diffSummary(oldCfg, newCfg) {
|
|
134
145
|
const changes = [];
|
|
135
146
|
const allKeys = /* @__PURE__ */ new Set([...Object.keys(oldCfg), ...Object.keys(newCfg)]);
|
|
@@ -137,7 +148,7 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
137
148
|
const o = JSON.stringify(oldCfg[k]);
|
|
138
149
|
const n = JSON.stringify(newCfg[k]);
|
|
139
150
|
if (o !== n) {
|
|
140
|
-
if (
|
|
151
|
+
if (isSecretField2(k)) {
|
|
141
152
|
changes.push(`${k}: [CHANGED]`);
|
|
142
153
|
} else if (typeof newCfg[k] !== "object") {
|
|
143
154
|
changes.push(`${k}: ${oldCfg[k] ?? "(unset)"} \u2192 ${newCfg[k]}`);
|
|
@@ -148,24 +159,16 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
148
159
|
}
|
|
149
160
|
return changes.length > 0 ? changes.slice(0, 5).join(", ") : "no changes";
|
|
150
161
|
}
|
|
151
|
-
var defaultHomeDir = () => os.homedir();
|
|
152
|
-
function activeProfileConfigPath(homeFn) {
|
|
153
|
-
const paths = homeFn === defaultHomeDir ? resolveWstackPaths({ projectRoot: process.cwd() }) : resolveWstackPaths({ projectRoot: process.cwd(), userHome: homeFn() });
|
|
154
|
-
return paths.profileConfig(paths.profileName);
|
|
155
|
-
}
|
|
156
162
|
function historyDir(homeFn = defaultHomeDir, targetConfigPath) {
|
|
157
|
-
return
|
|
163
|
+
return path2.join(path2.dirname(configPath(homeFn, targetConfigPath)), "config.history", "entries");
|
|
158
164
|
}
|
|
159
165
|
function historyIndexPath(homeFn = defaultHomeDir, targetConfigPath) {
|
|
160
|
-
return
|
|
161
|
-
|
|
166
|
+
return path2.join(
|
|
167
|
+
path2.dirname(configPath(homeFn, targetConfigPath)),
|
|
162
168
|
"config.history",
|
|
163
169
|
"index.json"
|
|
164
170
|
);
|
|
165
171
|
}
|
|
166
|
-
function configPath(homeFn = defaultHomeDir, targetConfigPath) {
|
|
167
|
-
return targetConfigPath ?? activeProfileConfigPath(homeFn);
|
|
168
|
-
}
|
|
169
172
|
function backupLastPath(homeFn = defaultHomeDir, targetConfigPath) {
|
|
170
173
|
return `${configPath(homeFn, targetConfigPath)}.last`;
|
|
171
174
|
}
|
|
@@ -174,11 +177,11 @@ function entryId(ts) {
|
|
|
174
177
|
}
|
|
175
178
|
async function ensureHistoryDir(homeFn = defaultHomeDir, targetConfigPath) {
|
|
176
179
|
try {
|
|
177
|
-
await
|
|
180
|
+
await fs2.mkdir(historyDir(homeFn, targetConfigPath), { recursive: true });
|
|
178
181
|
} catch (err) {
|
|
179
|
-
throw new
|
|
182
|
+
throw new FsError2({
|
|
180
183
|
message: toErrorMessage(err),
|
|
181
|
-
code:
|
|
184
|
+
code: ERROR_CODES2.FS_MKDIR_FAILED,
|
|
182
185
|
path: historyDir(homeFn, targetConfigPath),
|
|
183
186
|
cause: err
|
|
184
187
|
});
|
|
@@ -187,7 +190,7 @@ async function ensureHistoryDir(homeFn = defaultHomeDir, targetConfigPath) {
|
|
|
187
190
|
async function readIndex(homeFn = defaultHomeDir, targetConfigPath) {
|
|
188
191
|
let raw;
|
|
189
192
|
try {
|
|
190
|
-
raw = await
|
|
193
|
+
raw = await fs2.readFile(historyIndexPath(homeFn, targetConfigPath), "utf8");
|
|
191
194
|
} catch (err) {
|
|
192
195
|
if (err.code !== "ENOENT") throw err;
|
|
193
196
|
return rebuildIndexFromEntries(homeFn, targetConfigPath);
|
|
@@ -208,7 +211,7 @@ async function rebuildIndexFromEntries(homeFn = defaultHomeDir, targetConfigPath
|
|
|
208
211
|
const dir = historyDir(homeFn, targetConfigPath);
|
|
209
212
|
let files;
|
|
210
213
|
try {
|
|
211
|
-
files = (await
|
|
214
|
+
files = (await fs2.readdir(dir)).filter((file) => file.endsWith(".json"));
|
|
212
215
|
} catch (err) {
|
|
213
216
|
if (err.code === "ENOENT") return { version: 1, entries: [] };
|
|
214
217
|
throw err;
|
|
@@ -218,7 +221,7 @@ async function rebuildIndexFromEntries(homeFn = defaultHomeDir, targetConfigPath
|
|
|
218
221
|
const id = file.slice(0, -".json".length);
|
|
219
222
|
let raw;
|
|
220
223
|
try {
|
|
221
|
-
raw = await
|
|
224
|
+
raw = await fs2.readFile(path2.join(dir, file), "utf8");
|
|
222
225
|
} catch (err) {
|
|
223
226
|
if (err.code === "ENOENT") continue;
|
|
224
227
|
entries.push({ id, timestamp: "", description: "" });
|
|
@@ -244,9 +247,9 @@ async function writeIndex(idx, homeFn = defaultHomeDir, targetConfigPath) {
|
|
|
244
247
|
try {
|
|
245
248
|
await atomicWrite(historyIndexPath(homeFn, targetConfigPath), JSON.stringify(idx, null, 2));
|
|
246
249
|
} catch (err) {
|
|
247
|
-
throw new
|
|
250
|
+
throw new FsError2({
|
|
248
251
|
message: toErrorMessage(err),
|
|
249
|
-
code:
|
|
252
|
+
code: ERROR_CODES2.FS_ATOMIC_WRITE_FAILED,
|
|
250
253
|
path: historyIndexPath(homeFn, targetConfigPath),
|
|
251
254
|
cause: err
|
|
252
255
|
});
|
|
@@ -258,16 +261,16 @@ async function pruneHistoryEntries(idx, homeFn = defaultHomeDir, targetConfigPat
|
|
|
258
261
|
const dir = historyDir(homeFn, targetConfigPath);
|
|
259
262
|
for (const entry of removed) {
|
|
260
263
|
try {
|
|
261
|
-
await
|
|
264
|
+
await fs2.unlink(path2.join(dir, `${entry.id}.json`));
|
|
262
265
|
} catch {
|
|
263
266
|
}
|
|
264
267
|
}
|
|
265
268
|
try {
|
|
266
|
-
const files = await
|
|
269
|
+
const files = await fs2.readdir(dir);
|
|
267
270
|
await Promise.all(
|
|
268
271
|
files.filter((file) => file.endsWith(".json") && !keep.has(file)).map(async (file) => {
|
|
269
272
|
try {
|
|
270
|
-
await
|
|
273
|
+
await fs2.unlink(path2.join(dir, file));
|
|
271
274
|
} catch {
|
|
272
275
|
}
|
|
273
276
|
})
|
|
@@ -281,7 +284,7 @@ async function backupCurrent(homeFn = defaultHomeDir, targetConfigPath) {
|
|
|
281
284
|
const ts = Date.now();
|
|
282
285
|
let content;
|
|
283
286
|
try {
|
|
284
|
-
content = await
|
|
287
|
+
content = await fs2.readFile(cfg, "utf8");
|
|
285
288
|
} catch {
|
|
286
289
|
}
|
|
287
290
|
if (content !== void 0) {
|
|
@@ -300,11 +303,11 @@ async function backupCurrent(homeFn = defaultHomeDir, targetConfigPath) {
|
|
|
300
303
|
}
|
|
301
304
|
}
|
|
302
305
|
try {
|
|
303
|
-
const dir =
|
|
304
|
-
const files = await
|
|
306
|
+
const dir = path2.dirname(cfg);
|
|
307
|
+
const files = await fs2.readdir(dir);
|
|
305
308
|
const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
|
|
306
309
|
for (const f of baks.slice(MAX_CONFIG_BACKUPS)) {
|
|
307
|
-
await safeDelete(
|
|
310
|
+
await safeDelete(path2.join(dir, f));
|
|
308
311
|
}
|
|
309
312
|
} catch (err) {
|
|
310
313
|
writeErr(`[config-history] backup prune failed: ${toErrorMessage(err)}`);
|
|
@@ -322,16 +325,16 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
322
325
|
diffSummary: diffSummary(oldCfg, newCfg)
|
|
323
326
|
};
|
|
324
327
|
try {
|
|
325
|
-
await
|
|
326
|
-
|
|
328
|
+
await fs2.writeFile(
|
|
329
|
+
path2.join(historyDir(homeFn, targetConfigPath), `${id}.json`),
|
|
327
330
|
JSON.stringify(entry, null, 2),
|
|
328
331
|
{ encoding: "utf8", mode: 384 }
|
|
329
332
|
);
|
|
330
333
|
} catch (err) {
|
|
331
|
-
throw new
|
|
334
|
+
throw new FsError2({
|
|
332
335
|
message: toErrorMessage(err),
|
|
333
|
-
code:
|
|
334
|
-
path:
|
|
336
|
+
code: ERROR_CODES2.FS_WRITE_FAILED,
|
|
337
|
+
path: path2.join(historyDir(homeFn, targetConfigPath), `${id}.json`),
|
|
335
338
|
cause: err
|
|
336
339
|
});
|
|
337
340
|
}
|
|
@@ -350,10 +353,10 @@ async function listHistory(homeFn = defaultHomeDir, targetConfigPath) {
|
|
|
350
353
|
}
|
|
351
354
|
async function getHistoryEntry(id, homeFn = defaultHomeDir, targetConfigPath) {
|
|
352
355
|
const dir = historyDir(homeFn, targetConfigPath);
|
|
353
|
-
const filePath =
|
|
354
|
-
if (
|
|
356
|
+
const filePath = path2.join(dir, `${id}.json`);
|
|
357
|
+
if (path2.dirname(path2.resolve(filePath)) !== path2.resolve(dir)) return null;
|
|
355
358
|
try {
|
|
356
|
-
const raw = await
|
|
359
|
+
const raw = await fs2.readFile(filePath, "utf8");
|
|
357
360
|
return JSON.parse(raw);
|
|
358
361
|
} catch {
|
|
359
362
|
return null;
|
|
@@ -374,7 +377,7 @@ async function restoreFromHistory(id, homeFn = defaultHomeDir, targetConfigPath)
|
|
|
374
377
|
await backupCurrent(homeFn, targetConfigPath);
|
|
375
378
|
let oldCfg = {};
|
|
376
379
|
try {
|
|
377
|
-
const raw = await
|
|
380
|
+
const raw = await fs2.readFile(cfg, "utf8");
|
|
378
381
|
oldCfg = JSON.parse(raw);
|
|
379
382
|
} catch {
|
|
380
383
|
}
|
|
@@ -399,7 +402,7 @@ async function restoreLast(homeFn = defaultHomeDir, targetConfigPath) {
|
|
|
399
402
|
const cfg = configPath(homeFn, targetConfigPath);
|
|
400
403
|
let lastCfg = {};
|
|
401
404
|
try {
|
|
402
|
-
const raw = await
|
|
405
|
+
const raw = await fs2.readFile(last, "utf8");
|
|
403
406
|
lastCfg = JSON.parse(raw);
|
|
404
407
|
} catch {
|
|
405
408
|
return { ok: false, error: "No prior backup found" };
|
|
@@ -410,7 +413,7 @@ async function restoreLast(homeFn = defaultHomeDir, targetConfigPath) {
|
|
|
410
413
|
return withFileLock(cfg, async () => {
|
|
411
414
|
let oldCfg = {};
|
|
412
415
|
try {
|
|
413
|
-
const raw = await
|
|
416
|
+
const raw = await fs2.readFile(cfg, "utf8");
|
|
414
417
|
oldCfg = JSON.parse(raw);
|
|
415
418
|
} catch {
|
|
416
419
|
}
|
|
@@ -439,4 +442,4 @@ export {
|
|
|
439
442
|
restoreFromHistory,
|
|
440
443
|
restoreLast
|
|
441
444
|
};
|
|
442
|
-
//# sourceMappingURL=chunk-
|
|
445
|
+
//# sourceMappingURL=chunk-KNYIEQAY.js.map
|