stim 1.15.0 → 1.16.0
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/{activity-B2-kb7k0.mjs → activity-BxwdD81S.mjs} +29 -16
- package/dist/agent-device-usage-output-Dpih1Tfa.mjs +411 -0
- package/dist/{android-Db2eEMaf.mjs → android-B_NumVyQ.mjs} +21 -20
- package/dist/{android-Cu_L4RpL.mjs → android-Ift4kVvt.mjs} +56 -47
- package/dist/{android-cas-PnKfAdB2.mjs → android-cas-8U4DPqeR.mjs} +1 -1
- package/dist/{android-CYmq0jNi.mjs → android-r2rBL8kV.mjs} +1 -1
- package/dist/{app-install-CAAplXAt.mjs → app-install-CKWdbXIX.mjs} +48 -5
- package/dist/attempt-Bk2ouQVL.mjs +39 -0
- package/dist/{budget-CMiqghig.mjs → budget-DjrURfQn.mjs} +356 -366
- package/dist/{build-plan-aeGMFX7e.mjs → build-plan-DjeSb2Xl.mjs} +19 -20
- package/dist/{build-slots-Df1ApSuH.mjs → build-slots-rUk77xqh.mjs} +7 -2
- package/dist/{cli-0cX5s4NM.mjs → cli-BkYZ_6hy.mjs} +23 -17
- package/dist/cli.mjs +1 -1
- package/dist/{client-C2IS36Qf.mjs → client-BTY686R6.mjs} +57 -22
- package/dist/collector-run.mjs +5 -192
- package/dist/{command-output-CliHHn7a.mjs → command-output-DnJMdEY-.mjs} +1 -1
- package/dist/{created-devices-BwVb3liH.mjs → created-devices-D5owjN4C.mjs} +1 -1
- package/dist/{dependency-state-BmsjKZ2H.mjs → dependency-state-B6cMXZab.mjs} +1 -1
- package/dist/{deps-BFlKbOIp.mjs → deps-urbmfXig.mjs} +2 -2
- package/dist/{dev-client-CjzLoNs0.mjs → dev-client-CroAIjjj.mjs} +3 -40
- package/dist/{device-BZ1IyS5A.mjs → device-DZQjF7H5.mjs} +7 -6
- package/dist/{device-capacity-DMuKC7nx.mjs → device-capacity-BHwxNBlu.mjs} +2 -2
- package/dist/device-host-worker.mjs +223 -28
- package/dist/{device-ios-DMQBUPus.mjs → device-ios-BadmeL2V.mjs} +7 -7
- package/dist/{device-lease-W5Pi1q38.mjs → device-lease-D-hLqx1b.mjs} +2 -2
- package/dist/{device-pool-B2r38YjS.mjs → device-lease-run-CqjShqWW.mjs} +5 -125
- package/dist/device-pool-CUaEWMMm.mjs +125 -0
- package/dist/{device-remote-9ulPsdhY.mjs → device-remote-D5WVWqOq.mjs} +12 -12
- package/dist/{doctor-BcE5xsBn.mjs → doctor-Cimf-gGB.mjs} +80 -34
- package/dist/{error-diagnostics-Cb0P1WLS.mjs → error-diagnostics-B9L4Ae0x.mjs} +11 -15
- package/dist/{gc-BuL1Genr.mjs → gc-Bqy4bjEu.mjs} +194 -31
- package/dist/{gradle-CmdOBq1d.mjs → gradle-BsO1MdsH.mjs} +4 -4
- package/dist/{guide-cCSGRFd3.mjs → guide-B_uyKTxW.mjs} +588 -53
- package/dist/{guide-status-CIoWK6mG.mjs → guide-status-CjEZMAD4.mjs} +1 -1
- package/dist/hosted-client-a-WmqE2L.mjs +197 -0
- package/dist/hosted-ios-CIWelIqf.mjs +220 -0
- package/dist/hosted-logs-D5g041Q3.mjs +187 -0
- package/dist/hosted-macos-BSZyIb3y.mjs +135 -0
- package/dist/{build-progress-C2Jv1AtA.mjs → idle-DYVymG92.mjs} +233 -6
- package/dist/{idle-shutdown-B5LPaNyY.mjs → idle-shutdown-CTsDnAde.mjs} +6 -6
- package/dist/{in-use-CndPMilN.mjs → in-use-DvBw0Kxo.mjs} +29 -9
- package/dist/{ios-3CtCRhwr.mjs → ios-BaInn3sW.mjs} +2 -2
- package/dist/ios-Cr1h5gnO.mjs +189 -0
- package/dist/{ios-Chob8ny7.mjs → ios-aq3QoGI3.mjs} +729 -452
- package/dist/{ios-device-DERmMmk-.mjs → ios-device-FfCN_C8p.mjs} +1 -1
- package/dist/ios-state-DS5UpYTH.mjs +48 -0
- package/dist/{launch-verify-CLlofxRt.mjs → launch-verify-2DBRizjo.mjs} +3 -3
- package/dist/logs-CHEVK8zD.mjs +371 -0
- package/dist/{machines-CYtOCqcj.mjs → machines-BIBUdhpj.mjs} +16 -10
- package/dist/{macos-BwtbUtTN.mjs → macos-CSYEAFmF.mjs} +22 -17
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.d.mts +4 -0
- package/dist/maintenance-run.mjs +254 -0
- package/dist/{metro-Di8EN2Tb.mjs → metro-BeK1ZPz1.mjs} +1 -1
- package/dist/metro-gateway-BJ-rxQAB.mjs +84 -0
- package/dist/{named-ports-Dj_XBNaL.mjs → named-ports-Bk_YXEmE.mjs} +1 -1
- package/dist/{native-run-DFDoIRbY.mjs → native-run-NaBrRFtQ.mjs} +2 -2
- package/dist/{native-runtime-i6OkVg-w.mjs → native-runtime-DBzRnEKS.mjs} +6 -6
- package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
- package/dist/offload-worker.d.mts +1 -0
- package/dist/offload-worker.mjs +13 -8
- package/dist/{ownership-BkYxUtuj.mjs → ownership-DiA16AsN.mjs} +2 -2
- package/dist/{stop-cause-CZ_Mr-v9.mjs → ownership-hCkPzGmr.mjs} +4 -98
- package/dist/{ports-DddwttKM.mjs → ports-R2k4Knz3.mjs} +2 -2
- package/dist/{prebuild-BvxDlD6C.mjs → prebuild-D2fsca_Z.mjs} +4 -4
- package/dist/preview-DhVbvPAZ.mjs +318 -0
- package/dist/{project-CV3tDFHl.mjs → project-Du91Ilvc.mjs} +67 -3
- package/dist/{recordings-GEXp4iBT.mjs → recordings-BIXOzVnr.mjs} +1 -1
- package/dist/{reload-ChPiwvot.mjs → reload-rM9jV1fz.mjs} +49 -12
- package/dist/{remote-cache-CO2elm_b.mjs → remote-cache-C8-V13XN.mjs} +4 -4
- package/dist/{run-B-xqRrsg.mjs → run-f4vI6EnK.mjs} +2 -2
- package/dist/{server-bare-X7PUgizL.mjs → server-bare-CpPGmnmO.mjs} +1 -1
- package/dist/server-expo-CVqO2c35.mjs +2 -0
- package/dist/{server-expo-DhtX6bom.mjs → server-expo-MC0rHlOC.mjs} +4 -4
- package/dist/settings-BBpMcDbD.mjs +25 -0
- package/dist/{settings-DbL6uhH8.mjs → settings-ZkDzkzvB.mjs} +10 -4
- package/dist/settings-oTSkkNrM.mjs +1439 -0
- package/dist/settings.schema.json +432 -39
- package/dist/{simslim-Ciluxlq4.mjs → simslim-B8-7AoTI.mjs} +3 -3
- package/dist/{slot-launch-BY0BrOLh.mjs → slot-launch-B8VrHxn1.mjs} +10 -7
- package/dist/{spawn-entry-DncpJvWL.mjs → spawn-entry-jw19m8Nl.mjs} +1 -0
- package/dist/{start-CPO4wRg7.mjs → start-CX8Ig-U2.mjs} +1 -1
- package/dist/{start-CUD9nasA.mjs → start-ClDfwXa4.mjs} +21 -17
- package/dist/{state-BxCgeTQu.mjs → state-3DwSQIBK.mjs} +2 -2
- package/dist/{state-DFm77wRn.mjs → state-C1jB7Q8q.mjs} +5 -5
- package/dist/{state-CaeaHbDA.mjs → state-DcW6vOd_.mjs} +1 -1
- package/dist/{stats-DBLLFmKd.mjs → stats-CEZ_i5je.mjs} +16 -5
- package/dist/{status-l84Gbvp2.mjs → status-BuvBFeII.mjs} +5 -3
- package/dist/{status-DYXxNOT0.mjs → status-Cs7VLg9D.mjs} +148 -44
- package/dist/status-DlKvVJBE.mjs +49 -0
- package/dist/{stop-Bvf1MflS.mjs → stop-B0Yxtmiy.mjs} +3 -3
- package/dist/{stop-Bad61z_2.mjs → stop-Bffozsac.mjs} +61 -20
- package/dist/{stop-CceYh1yG.mjs → stop-Dt_dM16A.mjs} +2 -2
- package/dist/stop-cause-CidxIqup.mjs +97 -0
- package/dist/supervisor-run.mjs +148 -17
- package/dist/{support-CDQbDkL5.mjs → support-CAP8qDUF.mjs} +4 -3
- package/dist/{support-WLOzVKUv.mjs → support-D4aYJniO.mjs} +34 -9
- package/dist/{teardown-BYuQafiA.mjs → teardown-CL6vDs2Y.mjs} +56 -47
- package/dist/{toolchain-DmeYjXD9.mjs → toolchain-DkUflduf.mjs} +10 -10
- package/dist/trigger-Bemu8ytw.mjs +65 -0
- package/dist/trigger-CXts6qt1.mjs +2 -0
- package/dist/{warm-progress-DVHLN4uC.mjs → warm-progress-CpGv3Wb6.mjs} +274 -5
- package/dist/{web-k6w8-HnE.mjs → web-DWUaMp8P.mjs} +15 -16
- package/dist/web-run.mjs +5 -5
- package/dist/{workspace-state-CL2V2Ons.mjs → workspace-state-B8-aJ1_5.mjs} +33 -33
- package/dist/workspaces-BV4pdHFz.mjs +306 -0
- package/dist/{worktree-qXKzADSC.mjs → worktree-K-xj9Db0.mjs} +497 -30
- package/dist/{worktree-BT_uULFx.mjs → worktree-jnG8Iph5.mjs} +1 -1
- package/package.json +5 -5
- package/dist/hosted-macos-DwowNU7W.mjs +0 -447
- package/dist/logs-h9h6MUvE.mjs +0 -568
- package/dist/machine-usage-D_YYMzfR.mjs +0 -283
- package/dist/merge-state-BnxLNQiM.mjs +0 -272
- package/dist/server-expo-KJeD109I.mjs +0 -2
- package/dist/settings-CduVcoxj.mjs +0 -680
- package/dist/watchman-Bx0wceoF.mjs +0 -238
- package/dist/worktree-C0c5Udwa.mjs +0 -753
|
@@ -2,37 +2,466 @@ import { t as getExecutor } from "./exec-CaErVv7t.mjs";
|
|
|
2
2
|
import { E as upsertProject, H as projectDeviceSlots, S as removeProject, _ as loadConfig$1, a as clearDevice, d as getConfigDir, g as isPathPrefix, k as acquireAvdClaim, p as getProject } from "./config-D0DbpZtM.mjs";
|
|
3
3
|
import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
|
|
4
4
|
import { a as pullRequestLookup, r as endedPullRequest } from "./pull-request-pxnDCQzn.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
5
|
+
import { a as formatElapsed, h as shortUdid, i as formatDuration, l as phaseLine, p as releasedLeaseFact, u as plural } from "./command-output-DnJMdEY-.mjs";
|
|
6
|
+
import { $ as cloneIgnoredEntries, A as readCommittedSettings, H as unknownSettingKeys, P as resolveSettings, Q as branchExists, R as settingShapeErrors, St as warmWorktreePaths, _t as resolveFullRef, a as SETTING_SHAPE_REMEDY, at as gitCommonDirOnDisk, bt as sourceCheckoutOf, ct as hasUncommittedWork, dt as listWorktrees, et as deleteBranch, ft as locallyKnownUpstream, ht as removeWorktree, it as gitCommonDir, lt as isPodInstallChurn, mt as readWorktreeExclude, ot as hasPopulatedSubmodules, pt as podsOutOfSync, rt as dirtyPaths, st as hasRemote, tt as depsOutOfSync, xt as unpushedCommits, yt as restoreFile } from "./settings-oTSkkNrM.mjs";
|
|
7
|
+
import { n as appProjectProblem, o as findProjectRoot$1 } from "./project-Du91Ilvc.mjs";
|
|
8
|
+
import { l as resolveProjectMetro, o as pidExists, r as killMetroTree, u as signalProcessTree } from "./metro-BeK1ZPz1.mjs";
|
|
9
|
+
import { g as workspaceAgentDeviceDir, i as recordWorkspaceUse, r as readWorkspaceState$1, v as workspaceDir } from "./workspace-state-B8-aJ1_5.mjs";
|
|
10
|
+
import { o as readWebRecord } from "./state-C1jB7Q8q.mjs";
|
|
11
|
+
import { a as machineNumber } from "./budget-DjrURfQn.mjs";
|
|
12
|
+
import { n as withIdleWorkspace, t as emptyWorkspaceDir } from "./in-use-DvBw0Kxo.mjs";
|
|
13
|
+
import { t as agentDeviceDaemon } from "./activity-BxwdD81S.mjs";
|
|
14
|
+
import { u as releaseWorkspaceLeases } from "./device-lease-D-hLqx1b.mjs";
|
|
15
|
+
import { a as clearRemoteSession, c as readMetroTunnel, d as readRemoteSessionId, i as clearManagedMetroTunnel, u as readRemoteSession } from "./state-DcW6vOd_.mjs";
|
|
16
|
+
import { ft as parkedMaxSetting, ot as POOL_SETTING_REMEDY } from "./android-B_NumVyQ.mjs";
|
|
11
17
|
import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-Cnz8CPnl.mjs";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import { t as
|
|
23
|
-
import { a as
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-BmsjKZ2H.mjs";
|
|
27
|
-
import { a as forgetStatusMeasures, t as startWarmProgress } from "./warm-progress-DVHLN4uC.mjs";
|
|
18
|
+
import { a as teardownOwnedBrowser, i as teardownOwnedAvd, o as teardownOwnedIosSim } from "./teardown-CL6vDs2Y.mjs";
|
|
19
|
+
import { t as clearNamedPorts } from "./named-ports-Bk_YXEmE.mjs";
|
|
20
|
+
import { i as stopTunnel, l as withManagedTunnelRemovalLock, s as withManagedRemoteWorktreeRemovalLock, t as resolveSupervisorTarget } from "./ownership-hCkPzGmr.mjs";
|
|
21
|
+
import { l as withdrawDevServerStopRequest, s as requestDevServerStop } from "./stop-cause-CidxIqup.mjs";
|
|
22
|
+
import { C as podInstallCommand, u as HEARTBEAT_INTERVAL_MS } from "./toolchain-DkUflduf.mjs";
|
|
23
|
+
import { n as verifyCollectorOwnership } from "./ownership-DiA16AsN.mjs";
|
|
24
|
+
import { t as stopMacosApp } from "./stop-B0Yxtmiy.mjs";
|
|
25
|
+
import { a as runPodInstall, i as runCaptured, n as podsAreStale, r as readPodState, t as DEPS_ERROR } from "./deps-urbmfXig.mjs";
|
|
26
|
+
import { f as resolveEasCliBin } from "./remote-cache-C8-V13XN.mjs";
|
|
27
|
+
import { i as endRecordedSession } from "./device-remote-D5WVWqOq.mjs";
|
|
28
|
+
import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-B6cMXZab.mjs";
|
|
29
|
+
import { a as forgetStatusMeasures, s as mergeState, t as startWarmProgress } from "./warm-progress-CpGv3Wb6.mjs";
|
|
30
|
+
import { r as workspaceRecordingEnabled } from "./recordings-BIXOzVnr.mjs";
|
|
31
|
+
import { r as stopHostedIos } from "./hosted-ios-CIWelIqf.mjs";
|
|
28
32
|
import { createHash, randomUUID } from "node:crypto";
|
|
29
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
30
|
-
import { join, relative } from "node:path";
|
|
33
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
|
|
34
|
+
import { basename, dirname, join, relative } from "node:path";
|
|
31
35
|
import { existsSync as existsSync$1, realpathSync as realpathSync$1, rmSync as rmSync$1 } from "fs";
|
|
32
36
|
import { basename as basename$1, dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1 } from "path";
|
|
33
|
-
import { workspaceName } from "@stim-cli/core";
|
|
34
|
-
import { LAST_BUILD_KEYS, readMacosRecord } from "@stim-cli/core/state";
|
|
37
|
+
import { withDirLock, workspaceName } from "@stim-cli/core";
|
|
38
|
+
import { LAST_BUILD_KEYS, archiveDir, archiveEnabled, archiveLock, archiveRoot, archivedUsage, countErrorEntries, endedAgentSessionOf, listRecordedDevices, loadConfig, queryLogs, readArchives, readBuildHistory, readJsonObject, readLastBuilds, readMacosRecord, readPullRequestCache, readWorkspaceState, recordingEnabled, sortByTs } from "@stim-cli/core/state";
|
|
39
|
+
import { claimRemoveCommand, clearFreeClaimSet, readClaimSet, releaseClaim, tryAcquireClaim } from "@stim-cli/core/ownership-claim";
|
|
35
40
|
import chalk from "chalk";
|
|
41
|
+
//#region src/archive.ts
|
|
42
|
+
const DAY = 864e5;
|
|
43
|
+
const KINDS = [
|
|
44
|
+
"logs",
|
|
45
|
+
"recordings",
|
|
46
|
+
"agentActions"
|
|
47
|
+
];
|
|
48
|
+
const KIND_PATH = {
|
|
49
|
+
logs: "logs",
|
|
50
|
+
recordings: "recordings",
|
|
51
|
+
agentActions: "agent-device/sessions"
|
|
52
|
+
};
|
|
53
|
+
function archiveSettings() {
|
|
54
|
+
const config = loadConfig();
|
|
55
|
+
return Object.fromEntries([
|
|
56
|
+
"maxAgeDays",
|
|
57
|
+
"maxCount",
|
|
58
|
+
"maxTotalGb",
|
|
59
|
+
"logs.maxAgeDays",
|
|
60
|
+
"logs.maxMbPerWorkspace",
|
|
61
|
+
"recordings.maxAgeDays",
|
|
62
|
+
"recordings.maxTotalGb",
|
|
63
|
+
"agentActions.maxAgeDays"
|
|
64
|
+
].map((key) => {
|
|
65
|
+
const result = machineNumber(`archive.${key}`, config, process.env);
|
|
66
|
+
if (result.error) throw new Error(result.error);
|
|
67
|
+
return [key, result.value];
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
function enabled(root) {
|
|
71
|
+
const config = loadConfig();
|
|
72
|
+
const project = config?.projects?.[root];
|
|
73
|
+
const present = existsSync(root);
|
|
74
|
+
const common = present ? gitCommonDirOnDisk(root) : null;
|
|
75
|
+
if (!present && Object.values(config?.repos ?? {}).some((repo) => repo.settings?.archive?.enabled === false)) return false;
|
|
76
|
+
return archiveEnabled(process.env, [
|
|
77
|
+
project?.settings,
|
|
78
|
+
common ? config?.repos?.[common]?.settings : void 0,
|
|
79
|
+
present ? readCommittedSettings(root) : void 0,
|
|
80
|
+
config
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
function files(dir) {
|
|
84
|
+
if (!existsSync(dir)) return [];
|
|
85
|
+
return readdirSync(dir).flatMap((name) => {
|
|
86
|
+
const path = join(dir, name);
|
|
87
|
+
const stat = lstatSync(path);
|
|
88
|
+
if (stat.isSymbolicLink()) throw new Error(`refusing symlink ${path}`);
|
|
89
|
+
return stat.isDirectory() ? files(path) : [{
|
|
90
|
+
path,
|
|
91
|
+
bytes: stat.size,
|
|
92
|
+
at: stat.mtimeMs
|
|
93
|
+
}];
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function secureTree(dir) {
|
|
97
|
+
if (lstatSync(dir).isSymbolicLink()) throw new Error(`refusing symlink ${dir}`);
|
|
98
|
+
chmodSync(dir, 448);
|
|
99
|
+
for (const name of readdirSync(dir)) {
|
|
100
|
+
const path = join(dir, name);
|
|
101
|
+
const stat = lstatSync(path);
|
|
102
|
+
if (stat.isSymbolicLink()) throw new Error(`refusing symlink ${path}`);
|
|
103
|
+
if (stat.isDirectory()) secureTree(path);
|
|
104
|
+
else chmodSync(path, 384);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function writeRecord(dir, record) {
|
|
108
|
+
const small = [
|
|
109
|
+
"state.json",
|
|
110
|
+
"ended-agents.json",
|
|
111
|
+
"log-error-index.json"
|
|
112
|
+
].reduce((sum, name) => {
|
|
113
|
+
const path = join(dir, name);
|
|
114
|
+
return sum + (existsSync(path) ? statSync(path).size : 0);
|
|
115
|
+
}, 0);
|
|
116
|
+
for (let pass = 0; pass < 8; pass++) {
|
|
117
|
+
record.bytes.total = KINDS.reduce((sum, kind) => sum + record.bytes[kind], record.bytes.record);
|
|
118
|
+
const bytes = small + Buffer.byteLength(`${JSON.stringify(record)}\n`);
|
|
119
|
+
if (bytes === record.bytes.record) break;
|
|
120
|
+
record.bytes.record = bytes;
|
|
121
|
+
}
|
|
122
|
+
record.bytes.total = KINDS.reduce((sum, kind) => sum + record.bytes[kind], record.bytes.record);
|
|
123
|
+
const tmp = join(dir, `.archive-${randomUUID()}.tmp`);
|
|
124
|
+
try {
|
|
125
|
+
writeFileSync(tmp, `${JSON.stringify(record)}\n`, { mode: 384 });
|
|
126
|
+
renameSync(tmp, join(dir, "archive.json"));
|
|
127
|
+
} finally {
|
|
128
|
+
rmSync(tmp, { force: true });
|
|
129
|
+
}
|
|
130
|
+
const now = /* @__PURE__ */ new Date();
|
|
131
|
+
utimesSync(archiveRoot(), now, now);
|
|
132
|
+
}
|
|
133
|
+
function removeRecord(id) {
|
|
134
|
+
const dir = archiveDir(id);
|
|
135
|
+
if (!existsSync(dir)) return;
|
|
136
|
+
const removing = join(archiveRoot(), `.removing-${id}`);
|
|
137
|
+
renameSync(dir, removing);
|
|
138
|
+
rmSync(removing, {
|
|
139
|
+
recursive: true,
|
|
140
|
+
force: true
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function sweepArchiveStaging(remove, waitMs = 0) {
|
|
144
|
+
if (!existsSync(archiveRoot())) return [];
|
|
145
|
+
return withDirLock(archiveLock(), () => readdirSync(archiveRoot()).filter((name) => name.startsWith(".incoming-") && (!name.endsWith(".claims") || !existsSync(join(archiveRoot(), name.slice(0, -7)))) || name.startsWith(".removing-")).flatMap((name) => {
|
|
146
|
+
const path = join(archiveRoot(), name.endsWith(".claims") ? name.slice(0, -7) : name);
|
|
147
|
+
const root = `${path}.claims`;
|
|
148
|
+
const survey = name.startsWith(".incoming-") ? readClaimSet(root) : null;
|
|
149
|
+
const unresolved = survey?.unresolved[0];
|
|
150
|
+
const kept = Boolean(survey && (survey.live.length || survey.unresolved.length));
|
|
151
|
+
const entry = {
|
|
152
|
+
path,
|
|
153
|
+
kept,
|
|
154
|
+
reason: unresolved?.reason ?? (kept ? "live archive writer" : null),
|
|
155
|
+
removeCommand: unresolved ? claimRemoveCommand(root) : null
|
|
156
|
+
};
|
|
157
|
+
if (remove && !kept) {
|
|
158
|
+
if (!survey || clearFreeClaimSet({
|
|
159
|
+
root,
|
|
160
|
+
label: "archive staging"
|
|
161
|
+
}).status === "cleared") {
|
|
162
|
+
rmSync(path, {
|
|
163
|
+
recursive: true,
|
|
164
|
+
force: true
|
|
165
|
+
});
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return [entry];
|
|
170
|
+
}), { waitMs });
|
|
171
|
+
}
|
|
172
|
+
function stripPids(value) {
|
|
173
|
+
if (Array.isArray(value)) return value.map(stripPids);
|
|
174
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).filter(([key]) => !/pids?$/i.test(key)).map(([key, child]) => [key, stripPids(child)]));
|
|
175
|
+
return value;
|
|
176
|
+
}
|
|
177
|
+
function gitFacts(root) {
|
|
178
|
+
const empty = {
|
|
179
|
+
repository: null,
|
|
180
|
+
branch: null,
|
|
181
|
+
head: null,
|
|
182
|
+
subject: null,
|
|
183
|
+
merged: null,
|
|
184
|
+
pullRequest: null
|
|
185
|
+
};
|
|
186
|
+
if (!existsSync(root)) return empty;
|
|
187
|
+
const exec = getExecutor();
|
|
188
|
+
const run = (args) => exec.runFileQuiet("git", [
|
|
189
|
+
"-C",
|
|
190
|
+
root,
|
|
191
|
+
...args
|
|
192
|
+
], { timeoutMs: 1e3 });
|
|
193
|
+
const common = gitCommonDirOnDisk(root);
|
|
194
|
+
const summary = run([
|
|
195
|
+
"show",
|
|
196
|
+
"-s",
|
|
197
|
+
"--format=%H%n%s",
|
|
198
|
+
"HEAD"
|
|
199
|
+
])?.split("\n");
|
|
200
|
+
const location = run([
|
|
201
|
+
"rev-parse",
|
|
202
|
+
"--show-toplevel",
|
|
203
|
+
"--abbrev-ref",
|
|
204
|
+
"HEAD"
|
|
205
|
+
])?.split("\n");
|
|
206
|
+
const branch = location?.[1] && location[1] !== "HEAD" ? location[1] : null;
|
|
207
|
+
const defaultRef = run([
|
|
208
|
+
"symbolic-ref",
|
|
209
|
+
"--quiet",
|
|
210
|
+
"refs/remotes/origin/HEAD"
|
|
211
|
+
]);
|
|
212
|
+
let merged = null;
|
|
213
|
+
if (defaultRef?.startsWith("refs/remotes/")) {
|
|
214
|
+
const state = mergeState(root, {
|
|
215
|
+
ref: defaultRef,
|
|
216
|
+
name: defaultRef.slice(13)
|
|
217
|
+
}, { timeoutMs: 2e3 });
|
|
218
|
+
merged = state.merged ? true : state.unknown ? null : false;
|
|
219
|
+
}
|
|
220
|
+
let prPath = root;
|
|
221
|
+
try {
|
|
222
|
+
if (location?.[0]) prPath = realpathSync(location[0]);
|
|
223
|
+
} catch {}
|
|
224
|
+
const pr = readPullRequestCache(prPath)?.pullRequest;
|
|
225
|
+
if (pr?.state === "merged") merged = true;
|
|
226
|
+
return {
|
|
227
|
+
repository: common ? basename(common) === ".git" ? dirname(common) : common : null,
|
|
228
|
+
branch,
|
|
229
|
+
head: summary?.[0] ?? null,
|
|
230
|
+
subject: summary?.[1] ?? null,
|
|
231
|
+
merged,
|
|
232
|
+
pullRequest: pr ? {
|
|
233
|
+
number: pr.number,
|
|
234
|
+
state: pr.state,
|
|
235
|
+
title: pr.title,
|
|
236
|
+
url: pr.url
|
|
237
|
+
} : null
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function archiveWorkspace(root, removedBy, state = readWorkspaceState(root)) {
|
|
241
|
+
writeArchive(root, removedBy, state);
|
|
242
|
+
try {
|
|
243
|
+
enforceArchiveRetention();
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error(`could not enforce archive retention: ${String(error?.message ?? error).split("\n")[0]}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function writeArchive(root, removedBy, state) {
|
|
249
|
+
let staging = null;
|
|
250
|
+
let claim;
|
|
251
|
+
try {
|
|
252
|
+
const dir = workspaceDir(root);
|
|
253
|
+
if (!existsSync(dir) || lstatSync(dir).isSymbolicLink()) return;
|
|
254
|
+
if (existsSync(root)) root = realpathSync(root);
|
|
255
|
+
try {
|
|
256
|
+
sweepArchiveStaging(true, 5e3);
|
|
257
|
+
} catch {}
|
|
258
|
+
if (!enabled(root)) return;
|
|
259
|
+
const limits = archiveSettings();
|
|
260
|
+
if (!limits.maxAgeDays || !limits.maxCount) return;
|
|
261
|
+
const logs = join(dir, "logs");
|
|
262
|
+
if (!existsSync(join(dir, "state.json")) && !Object.values(readBuildHistory(state)).some((entries) => entries?.length) && (!existsSync(logs) || readdirSync(logs).length === 0)) return;
|
|
263
|
+
const worktree = gitFacts(root);
|
|
264
|
+
mkdirSync(archiveRoot(), {
|
|
265
|
+
recursive: true,
|
|
266
|
+
mode: 448
|
|
267
|
+
});
|
|
268
|
+
chmodSync(archiveRoot(), 448);
|
|
269
|
+
const token = randomUUID();
|
|
270
|
+
staging = join(archiveRoot(), `.incoming-${token}`);
|
|
271
|
+
const attempt = tryAcquireClaim({
|
|
272
|
+
root: `${staging}.claims`,
|
|
273
|
+
mode: "exclusive",
|
|
274
|
+
label: "archive staging"
|
|
275
|
+
});
|
|
276
|
+
claim = attempt.acquired;
|
|
277
|
+
if (!claim) {
|
|
278
|
+
releaseClaim(attempt.pending);
|
|
279
|
+
throw new Error("archive staging claim is held");
|
|
280
|
+
}
|
|
281
|
+
withDirLock(archiveLock(), () => {
|
|
282
|
+
mkdirSync(staging, { mode: 448 });
|
|
283
|
+
let at = Date.now();
|
|
284
|
+
const workspace = workspaceName(root);
|
|
285
|
+
while (existsSync(archiveDir(`${workspace}--${at}`))) at++;
|
|
286
|
+
const id = `${workspace}--${at}`;
|
|
287
|
+
if (state) {
|
|
288
|
+
const { supervisor: _supervisor, collectors: _collectors, warm: _warm, ...stable } = state;
|
|
289
|
+
writeFileSync(join(staging, "state.json"), `${JSON.stringify(stripPids(stable))}\n`, { mode: 384 });
|
|
290
|
+
}
|
|
291
|
+
for (const name of ["ended-agents.json", "log-error-index.json"]) {
|
|
292
|
+
const data = readJsonObject(join(dir, name));
|
|
293
|
+
if (data) writeFileSync(join(staging, name), `${JSON.stringify(stripPids(data))}\n`, { mode: 384 });
|
|
294
|
+
}
|
|
295
|
+
const move = (kind) => {
|
|
296
|
+
const from = join(dir, KIND_PATH[kind]);
|
|
297
|
+
if (!limits[`${kind}.maxAgeDays`] || !existsSync(from)) return;
|
|
298
|
+
const to = join(staging, KIND_PATH[kind]);
|
|
299
|
+
mkdirSync(join(to, ".."), {
|
|
300
|
+
recursive: true,
|
|
301
|
+
mode: 448
|
|
302
|
+
});
|
|
303
|
+
renameSync(from, to);
|
|
304
|
+
secureTree(to);
|
|
305
|
+
};
|
|
306
|
+
move("logs");
|
|
307
|
+
move("agentActions");
|
|
308
|
+
const config = loadConfig();
|
|
309
|
+
if (limits["recordings.maxAgeDays"] && workspaceRecordingEnabled(root, config?.projects?.[root] ?? {}, config, process.env)) {
|
|
310
|
+
const recordingDir = join(dir, "recordings");
|
|
311
|
+
if (existsSync(recordingDir) && lstatSync(recordingDir).isSymbolicLink()) throw new Error(`refusing symlink ${recordingDir}`);
|
|
312
|
+
for (const device of listRecordedDevices(recordingDir)) {
|
|
313
|
+
if (lstatSync(device.dir).isSymbolicLink()) throw new Error(`refusing symlink ${device.dir}`);
|
|
314
|
+
const to = join(staging, "recordings", `${device.platform}-${device.slot}`);
|
|
315
|
+
for (const segment of device.segments.filter((entry) => !entry.open)) {
|
|
316
|
+
mkdirSync(to, {
|
|
317
|
+
recursive: true,
|
|
318
|
+
mode: 448
|
|
319
|
+
});
|
|
320
|
+
try {
|
|
321
|
+
if (lstatSync(segment.file).isSymbolicLink()) throw new Error(`refusing symlink ${segment.file}`);
|
|
322
|
+
renameSync(segment.file, join(to, basename(segment.file)));
|
|
323
|
+
chmodSync(join(to, basename(segment.file)), 384);
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (error.code !== "ENOENT") throw error;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
const index = readJsonObject(join(staging, "log-error-index.json"));
|
|
331
|
+
const indexed = Object.values(index?.files ?? {}).flatMap((file) => Object.values(file.markers ?? {}).concat(file.errors ?? []));
|
|
332
|
+
const lastErrorCount = countErrorEntries(sortByTs(queryLogs({
|
|
333
|
+
records: indexed,
|
|
334
|
+
errorsOnly: true
|
|
335
|
+
})));
|
|
336
|
+
const history = Object.values(readBuildHistory(state)).flat();
|
|
337
|
+
const last = Object.values(readLastBuilds(state)).toSorted((a, b) => Date.parse(b.startedAt) - Date.parse(a.startedAt))[0] ?? null;
|
|
338
|
+
const bytes = {
|
|
339
|
+
logs: 0,
|
|
340
|
+
recordings: 0,
|
|
341
|
+
agentActions: 0,
|
|
342
|
+
record: 0,
|
|
343
|
+
total: 0
|
|
344
|
+
};
|
|
345
|
+
const expires = {
|
|
346
|
+
logs: null,
|
|
347
|
+
recordings: null,
|
|
348
|
+
agentActions: null,
|
|
349
|
+
record: new Date(at + limits.maxAgeDays * DAY).toISOString()
|
|
350
|
+
};
|
|
351
|
+
for (const kind of KINDS) {
|
|
352
|
+
bytes[kind] = files(join(staging, KIND_PATH[kind])).reduce((sum, file) => sum + file.bytes, 0);
|
|
353
|
+
if (bytes[kind]) expires[kind] = new Date(at + limits[`${kind}.maxAgeDays`] * DAY).toISOString();
|
|
354
|
+
}
|
|
355
|
+
const record = {
|
|
356
|
+
id,
|
|
357
|
+
projectRoot: root,
|
|
358
|
+
project: basename(root),
|
|
359
|
+
workspace,
|
|
360
|
+
worktree,
|
|
361
|
+
removedAt: new Date(at).toISOString(),
|
|
362
|
+
removedBy,
|
|
363
|
+
lastUsedAt: typeof state?.lastUsedAt === "string" ? state.lastUsedAt : null,
|
|
364
|
+
builds: {
|
|
365
|
+
count: history.length,
|
|
366
|
+
last,
|
|
367
|
+
lastErrorCount
|
|
368
|
+
},
|
|
369
|
+
agents: (readJsonObject(join(dir, "ended-agents.json"))?.sessions ?? []).map(endedAgentSessionOf).filter((session) => session !== null),
|
|
370
|
+
bytes,
|
|
371
|
+
expires,
|
|
372
|
+
version: 1
|
|
373
|
+
};
|
|
374
|
+
writeRecord(staging, record);
|
|
375
|
+
renameSync(staging, archiveDir(id));
|
|
376
|
+
}, { waitMs: 3e4 });
|
|
377
|
+
releaseClaim(claim);
|
|
378
|
+
claim = void 0;
|
|
379
|
+
staging = null;
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (staging) try {
|
|
382
|
+
rmSync(staging, {
|
|
383
|
+
recursive: true,
|
|
384
|
+
force: true
|
|
385
|
+
});
|
|
386
|
+
} catch {}
|
|
387
|
+
console.error(`could not archive: ${String(error?.message ?? error).split("\n")[0]}`);
|
|
388
|
+
} finally {
|
|
389
|
+
releaseClaim(claim);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function removeKind(record, kind) {
|
|
393
|
+
rmSync(join(archiveDir(record.id), KIND_PATH[kind]), {
|
|
394
|
+
recursive: true,
|
|
395
|
+
force: true
|
|
396
|
+
});
|
|
397
|
+
record.bytes[kind] = 0;
|
|
398
|
+
record.expires[kind] = null;
|
|
399
|
+
writeRecord(archiveDir(record.id), record);
|
|
400
|
+
}
|
|
401
|
+
function logRemovalOrder(path) {
|
|
402
|
+
return /\.ndjson\.\d+$/.test(path) ? 0 : /^build-.*\.ndjson$/.test(basename(path)) ? 1 : 2;
|
|
403
|
+
}
|
|
404
|
+
function enforceArchiveRetention(now = Date.now()) {
|
|
405
|
+
if (!existsSync(archiveRoot())) return;
|
|
406
|
+
let limits;
|
|
407
|
+
try {
|
|
408
|
+
limits = archiveSettings();
|
|
409
|
+
} catch (error) {
|
|
410
|
+
console.error(`could not enforce archive retention: ${String(error.message).split("\n")[0]}`);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
withDirLock(archiveLock(), () => {
|
|
414
|
+
const listed = readArchives().toReversed();
|
|
415
|
+
const records = listed.filter((record, index) => limits.maxAgeDays > 0 && now - Date.parse(record.removedAt) < limits.maxAgeDays * DAY && index >= listed.length - limits.maxCount);
|
|
416
|
+
for (const record of listed) if (!records.includes(record)) removeRecord(record.id);
|
|
417
|
+
const recordings = recordingEnabled(process.env, [loadConfig()]);
|
|
418
|
+
for (const record of records) {
|
|
419
|
+
for (const kind of [
|
|
420
|
+
"recordings",
|
|
421
|
+
"agentActions",
|
|
422
|
+
"logs"
|
|
423
|
+
]) {
|
|
424
|
+
const age = limits[`${kind}.maxAgeDays`];
|
|
425
|
+
if (record.bytes[kind] && (!age || now - Date.parse(record.removedAt) >= age * DAY || kind === "recordings" && !recordings)) removeKind(record, kind);
|
|
426
|
+
}
|
|
427
|
+
const cap = limits["logs.maxMbPerWorkspace"] * 1024 ** 2;
|
|
428
|
+
if (record.bytes.logs > cap) {
|
|
429
|
+
const entries = files(join(archiveDir(record.id), "logs")).toSorted((a, b) => logRemovalOrder(a.path) - logRemovalOrder(b.path) || (logRemovalOrder(a.path) === 0 ? Number(b.path.split(".").at(-1)) - Number(a.path.split(".").at(-1)) : 0) || a.at - b.at);
|
|
430
|
+
for (const file of entries) {
|
|
431
|
+
if (record.bytes.logs <= cap) break;
|
|
432
|
+
rmSync(file.path);
|
|
433
|
+
record.bytes.logs = Math.max(0, record.bytes.logs - file.bytes);
|
|
434
|
+
}
|
|
435
|
+
if (!record.bytes.logs) record.expires.logs = null;
|
|
436
|
+
writeRecord(archiveDir(record.id), record);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
const totalCap = limits.maxTotalGb * 1024 ** 3;
|
|
440
|
+
const recordingCap = limits["recordings.maxTotalGb"] * 1024 ** 3;
|
|
441
|
+
for (const record of records) for (;;) {
|
|
442
|
+
const usage = archivedUsage(records);
|
|
443
|
+
const totalOver = usage.bytes > totalCap;
|
|
444
|
+
const recordingsOver = usage.byKind.recordings > recordingCap;
|
|
445
|
+
if (!totalOver && !recordingsOver) break;
|
|
446
|
+
const candidates = KINDS.filter((kind) => record.bytes[kind] > 0 && (totalOver || kind === "recordings")).toSorted((a, b) => record.bytes[b] - record.bytes[a]);
|
|
447
|
+
if (!candidates.length) break;
|
|
448
|
+
removeKind(record, candidates[0]);
|
|
449
|
+
}
|
|
450
|
+
for (const record of records) {
|
|
451
|
+
const before = JSON.stringify(record.expires);
|
|
452
|
+
for (const kind of KINDS) record.expires[kind] = record.bytes[kind] ? new Date(Date.parse(record.removedAt) + limits[`${kind}.maxAgeDays`] * DAY).toISOString() : null;
|
|
453
|
+
record.expires.record = new Date(Date.parse(record.removedAt) + limits.maxAgeDays * DAY).toISOString();
|
|
454
|
+
if (JSON.stringify(record.expires) !== before) writeRecord(archiveDir(record.id), record);
|
|
455
|
+
}
|
|
456
|
+
}, { waitMs: 3e4 });
|
|
457
|
+
}
|
|
458
|
+
function deleteSelectedArchives(ids, kind) {
|
|
459
|
+
withDirLock(archiveLock(), () => {
|
|
460
|
+
for (const record of readArchives().filter((entry) => ids.includes(entry.id))) if (kind) removeKind(record, kind);
|
|
461
|
+
else removeRecord(record.id);
|
|
462
|
+
}, { waitMs: 3e4 });
|
|
463
|
+
}
|
|
464
|
+
//#endregion
|
|
36
465
|
//#region src/devices/reclaim.ts
|
|
37
466
|
async function reapCollectors(root, { verify = verifyCollectorOwnership, collectors }) {
|
|
38
467
|
const skippedDevices = [];
|
|
@@ -288,6 +717,7 @@ async function reclaimProject(path, options = {}) {
|
|
|
288
717
|
const existed = existsSync(dir);
|
|
289
718
|
const run = await withIdleWorkspace(path, () => reclaimIdleProject(path, existed, options), {
|
|
290
719
|
purpose: "workspace removal",
|
|
720
|
+
hosted: false,
|
|
291
721
|
supervisor: false,
|
|
292
722
|
managedLocks: false
|
|
293
723
|
});
|
|
@@ -316,7 +746,7 @@ async function reclaimProject(path, options = {}) {
|
|
|
316
746
|
failedWorkspaceDirs: []
|
|
317
747
|
};
|
|
318
748
|
}
|
|
319
|
-
async function reclaimIdleProject(path, workspaceExisted, { deleteOwnedDevices = false, parkOwnedDevices = false, preserveProjectRecord = false, stopSession = defaultStopSession, stopMetroTunnel = defaultStopMetroTunnel, releaseLeases = releaseWorkspaceLeases, verifyCollector = verifyCollectorOwnership, teardownBrowser = teardownOwnedBrowser, stopRequester = { by: "stim gc --delete" } }) {
|
|
749
|
+
async function reclaimIdleProject(path, workspaceExisted, { archive, deleteOwnedDevices = false, parkOwnedDevices = false, preserveProjectRecord = false, stopSession = defaultStopSession, stopMetroTunnel = defaultStopMetroTunnel, releaseLeases = releaseWorkspaceLeases, verifyCollector = verifyCollectorOwnership, teardownBrowser = teardownOwnedBrowser, stopRequester = { by: "stim gc --delete" } }) {
|
|
320
750
|
await clearNamedPorts(path, { stop: true });
|
|
321
751
|
const project = getProject(path);
|
|
322
752
|
for (const { platforms } of projectDeviceSlots(project)) if (platforms.android?.owned && platforms.android.avdName) (0, ownership_claim_exports.releaseClaim)(acquireAvdClaim(platforms.android.avdName));
|
|
@@ -395,6 +825,17 @@ async function reclaimIdleProject(path, workspaceExisted, { deleteOwnedDevices =
|
|
|
395
825
|
};
|
|
396
826
|
skippedDevices.push(...skippedCollectors);
|
|
397
827
|
failedDevices.push(...failedCollectors);
|
|
828
|
+
try {
|
|
829
|
+
await stopHostedIos(path);
|
|
830
|
+
} catch (error) {
|
|
831
|
+
const failed = {
|
|
832
|
+
platform: "ios",
|
|
833
|
+
name: "hosted iOS simulator",
|
|
834
|
+
reason: error.message
|
|
835
|
+
};
|
|
836
|
+
failedDevices.push(failed);
|
|
837
|
+
skippedDevices.push(failed);
|
|
838
|
+
}
|
|
398
839
|
const remote = reclaimRemoteSession(path, { stopSession });
|
|
399
840
|
if (remote.failed) {
|
|
400
841
|
skippedDevices.push(remote.failed);
|
|
@@ -428,6 +869,28 @@ async function reclaimIdleProject(path, workspaceExisted, { deleteOwnedDevices =
|
|
|
428
869
|
if (failedDevices.length === 0 && !supervisorHeld) {
|
|
429
870
|
const dir = workspaceDir(path);
|
|
430
871
|
if (existsSync(dir)) try {
|
|
872
|
+
const stateDir = workspaceAgentDeviceDir(path);
|
|
873
|
+
if (!["none", "dead"].includes(agentDeviceDaemon(stateDir))) {
|
|
874
|
+
const exec = getExecutor();
|
|
875
|
+
try {
|
|
876
|
+
if (!exec.findExecutable("agent-device")) throw new Error("agent-device is not on PATH");
|
|
877
|
+
exec.runFile("agent-device", [
|
|
878
|
+
"daemon",
|
|
879
|
+
"stop",
|
|
880
|
+
"--state-dir",
|
|
881
|
+
stateDir,
|
|
882
|
+
"--clean"
|
|
883
|
+
], {
|
|
884
|
+
timeoutMs: 2e4,
|
|
885
|
+
killSignal: "SIGKILL",
|
|
886
|
+
env: { AGENT_DEVICE_NO_UPDATE_NOTIFIER: "1" }
|
|
887
|
+
});
|
|
888
|
+
} catch (error) {
|
|
889
|
+
process.stderr.write(`${phaseLine("agent-device", `daemon could not be stopped: ${error.message}; run agent-device daemon stop --state-dir "${stateDir}" --clean`)}\n`);
|
|
890
|
+
throw error;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
if (archive) archiveWorkspace(path, archive.removedBy, initialState);
|
|
431
894
|
emptyWorkspaceDir(dir);
|
|
432
895
|
if (workspaceExisted) removedWorkspaceDirs.push(dir);
|
|
433
896
|
} catch {
|
|
@@ -1492,7 +1955,7 @@ async function withReclaimLocks(rootPath, fn) {
|
|
|
1492
1955
|
const acquire = (index) => index === keys.length ? fn(keys) : withManagedTunnelRemovalLock(keys[index], () => acquire(index + 1));
|
|
1493
1956
|
return acquire(0);
|
|
1494
1957
|
}
|
|
1495
|
-
async function reclaimAll(rootPath, keys = reclaimKeys(rootPath), { preserveRootProject = false } = {}) {
|
|
1958
|
+
async function reclaimAll(rootPath, keys = reclaimKeys(rootPath), { preserveRootProject = false, removedBy } = {}) {
|
|
1496
1959
|
const dereferenced = [];
|
|
1497
1960
|
const killedPids = [];
|
|
1498
1961
|
const deletedDevices = [];
|
|
@@ -1511,6 +1974,7 @@ async function reclaimAll(rootPath, keys = reclaimKeys(rootPath), { preserveRoot
|
|
|
1511
1974
|
let r;
|
|
1512
1975
|
try {
|
|
1513
1976
|
r = await reclaimProject(key, {
|
|
1977
|
+
archive: removedBy ? { removedBy } : void 0,
|
|
1514
1978
|
deleteOwnedDevices: true,
|
|
1515
1979
|
parkOwnedDevices: true,
|
|
1516
1980
|
preserveProjectRecord: preserveRootProject && key === rootPath,
|
|
@@ -1835,7 +2299,10 @@ async function runRemove(target, opts, onRemoved) {
|
|
|
1835
2299
|
printRemovalRefusal(path, current);
|
|
1836
2300
|
return;
|
|
1837
2301
|
}
|
|
1838
|
-
const result = await reclaimAll(path, lockedKeys, {
|
|
2302
|
+
const result = await reclaimAll(path, lockedKeys, {
|
|
2303
|
+
preserveRootProject: true,
|
|
2304
|
+
removedBy: opts.removedBy ?? "worktree-remove"
|
|
2305
|
+
});
|
|
1839
2306
|
if (result.keptEntries.length) {
|
|
1840
2307
|
reportRetainedResources(path, result);
|
|
1841
2308
|
return;
|
|
@@ -1914,4 +2381,4 @@ function worktreeCommand(program) {
|
|
|
1914
2381
|
registerRemove(worktree);
|
|
1915
2382
|
}
|
|
1916
2383
|
//#endregion
|
|
1917
|
-
export { porcelainPath as a, registerWarm as c, removalRemedy as d, removeWorktreeTarget as f, reclaimProject as g, describeDereferenced as h, matchWorktreeEntry as i, removalBlockers as l, worktreeCommand as m, excludePodChurn as n, reclaimKeys as o, warmedWorkspace as p, excludeWatchmanCookies as r, registerRemove as s, dependencyInstallCommand as t, removalPath as u };
|
|
2384
|
+
export { deleteSelectedArchives as _, porcelainPath as a, registerWarm as c, removalRemedy as d, removeWorktreeTarget as f, reclaimProject as g, describeDereferenced as h, matchWorktreeEntry as i, removalBlockers as l, worktreeCommand as m, excludePodChurn as n, reclaimKeys as o, warmedWorkspace as p, excludeWatchmanCookies as r, registerRemove as s, dependencyInstallCommand as t, removalPath as u, sweepArchiveStaging as v };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as porcelainPath, c as registerWarm, d as removalRemedy, f as removeWorktreeTarget, i as matchWorktreeEntry, l as removalBlockers, m as worktreeCommand, n as excludePodChurn, o as reclaimKeys, p as warmedWorkspace, r as excludeWatchmanCookies, s as registerRemove, t as dependencyInstallCommand, u as removalPath } from "./worktree-
|
|
1
|
+
import { a as porcelainPath, c as registerWarm, d as removalRemedy, f as removeWorktreeTarget, i as matchWorktreeEntry, l as removalBlockers, m as worktreeCommand, n as excludePodChurn, o as reclaimKeys, p as warmedWorkspace, r as excludeWatchmanCookies, s as registerRemove, t as dependencyInstallCommand, u as removalPath } from "./worktree-K-xj9Db0.mjs";
|
|
2
2
|
export { worktreeCommand as default, dependencyInstallCommand, excludePodChurn, excludeWatchmanCookies, matchWorktreeEntry, porcelainPath, reclaimKeys, registerRemove, registerWarm, removalBlockers, removalPath, removalRemedy, removeWorktreeTarget, warmedWorkspace };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stim",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "Stim: fast, isolated React Native environments for coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@expo/fingerprint": "^0.20.10",
|
|
51
|
-
"@stim-cli/cache": "^1.
|
|
52
|
-
"@stim-cli/core": "^1.
|
|
53
|
-
"@stim-cli/metro": "^1.
|
|
51
|
+
"@stim-cli/cache": "^1.16.0",
|
|
52
|
+
"@stim-cli/core": "^1.16.0",
|
|
53
|
+
"@stim-cli/metro": "^1.16.0",
|
|
54
54
|
"chalk": "^6.0.0",
|
|
55
55
|
"commander": "^15.0.0",
|
|
56
56
|
"cross-spawn": "^7.0.6",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"ws": "^8.21.3"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@stim-cli/expo-build-cache": "1.
|
|
61
|
+
"@stim-cli/expo-build-cache": "1.16.0",
|
|
62
62
|
"@types/cross-spawn": "^6.0.6",
|
|
63
63
|
"@types/which": "^3.0.4",
|
|
64
64
|
"@types/ws": "^8.18.1",
|