@stim-cli/core 1.20.2 → 1.22.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/README.md +2 -0
- package/dist/{core-BkgfKqJ8.mjs → core-BTsCO1nW.mjs} +444 -60
- package/dist/{index-VDWBflH3.d.mts → index-DKq1j8dw.d.mts} +340 -91
- package/dist/index.d.mts +101 -5
- package/dist/index.mjs +3 -3
- package/dist/keyed-delta-BBoOeR2J.mjs +289 -0
- package/dist/noise-DYIbymAJ.d.mts +100 -0
- package/dist/noise-session.d.mts +164 -0
- package/dist/noise-session.mjs +402 -0
- package/dist/noise.d.mts +2 -0
- package/dist/noise.mjs +393 -0
- package/dist/oversight.mjs +16 -6
- package/dist/{ownership-claim-O31Y5iIb.mjs → ownership-claim-DHUs_Dir.mjs} +1 -1
- package/dist/{ownership-claim-Xxxr9MPp.d.mts → ownership-claim-RY3fbm1D.d.mts} +3 -2
- package/dist/ownership-claim.d.mts +2 -2
- package/dist/ownership-claim.mjs +2 -2
- package/dist/phone-protocol.d.mts +85 -16
- package/dist/phone-protocol.mjs +2 -2
- package/dist/{process-identity-BTyww2Xj.d.mts → process-identity-DVNGpF5c.d.mts} +1 -1
- package/dist/process-identity.d.mts +1 -1
- package/dist/process-identity.mjs +3 -1
- package/dist/{protocol-pH632kA3.d.mts → protocol-BjJt4ghB.d.mts} +632 -65
- package/dist/protocol.d.mts +2 -2
- package/dist/protocol.mjs +81 -10
- package/dist/receive-protocol.d.mts +1 -1
- package/dist/receive-protocol.mjs +20163 -14469
- package/dist/state.d.mts +2 -2
- package/dist/state.mjs +1261 -208
- package/dist/toolchain.d.mts +64 -0
- package/dist/toolchain.mjs +326 -0
- package/dist/workspace-link.d.mts +16 -0
- package/dist/workspace-link.mjs +50 -0
- package/package.json +21 -1
- package/dist/status-derived-C13g6eQt.mjs +0 -151
package/dist/index.d.mts
CHANGED
|
@@ -1,13 +1,59 @@
|
|
|
1
|
+
import { N as BuildSwiftpmArtifactQuery, j as BuildReactNativeArtifactQuery, m as BuildArtifactIdentity } from "./protocol-BjJt4ghB.mjs";
|
|
1
2
|
import { Server } from "node:net";
|
|
2
3
|
//#region dir-lock.d.ts
|
|
3
4
|
interface DirLockOptions {
|
|
4
|
-
/** @deprecated Occupied locks are never expired. */
|
|
5
|
-
staleMs?: number;
|
|
6
5
|
waitMs?: number;
|
|
7
6
|
pollMs?: number;
|
|
8
7
|
ensureParent?: () => void;
|
|
9
8
|
}
|
|
10
9
|
declare function withDirLock<T>(lockPath: string, fn: () => T, { waitMs, pollMs, ensureParent }?: DirLockOptions): T;
|
|
10
|
+
/**
|
|
11
|
+
* `withDirLock` that waits for a contended lock on a timer instead of blocking the thread. `fn` still runs
|
|
12
|
+
* synchronously: nothing awaits between taking the lock, running `fn` and releasing the lock, so the held
|
|
13
|
+
* transaction and reentrant nesting behave exactly as with `withDirLock`.
|
|
14
|
+
* An optional signal cancels acquisition; it cannot interrupt the synchronous transaction.
|
|
15
|
+
*/
|
|
16
|
+
declare function withDirLockAsync<T>(lockPath: string, fn: () => T, options?: DirLockOptions & {
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
}): Promise<T>;
|
|
19
|
+
/**
|
|
20
|
+
* Holds the directory lock and its ownership claim until the body settles. Transactions are exclusive even
|
|
21
|
+
* within one process and are not reentrant. Cancellation stops acquisition; the body owns cancellation after admission.
|
|
22
|
+
*/
|
|
23
|
+
declare function withDirLockTransaction<T>(lockPath: string, fn: () => T | Promise<T>, options?: DirLockOptions & {
|
|
24
|
+
signal?: AbortSignal;
|
|
25
|
+
}): Promise<T>;
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region context.d.ts
|
|
28
|
+
/** Where an operation writes progress that the CLI prints and an API client receives through `onProgress`. */
|
|
29
|
+
type StimOutput = (stream: "stdout" | "stderr", text: string) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Everything one Stim operation takes from its caller instead of from `process`. The CLI builds one per command
|
|
32
|
+
* from `process`; `createStim` builds one per operation from the client's options. Engine code receives it as a
|
|
33
|
+
* parameter.
|
|
34
|
+
*/
|
|
35
|
+
interface StimContext {
|
|
36
|
+
home: string;
|
|
37
|
+
buildCache: string | null;
|
|
38
|
+
projectRoot: string;
|
|
39
|
+
env: NodeJS.ProcessEnv;
|
|
40
|
+
cwd: string;
|
|
41
|
+
runId: string;
|
|
42
|
+
signal: AbortSignal;
|
|
43
|
+
output: StimOutput;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Runs `fn` with `context` reachable through `ambientContext()`, including after an `await` inside it. A temporary
|
|
47
|
+
* bridge for call sites that are not passed a context yet: `configDir()`, `runId()` and the exec wrapper's base
|
|
48
|
+
* environment.
|
|
49
|
+
*/
|
|
50
|
+
declare function withAmbientContext<T>(context: StimContext, fn: () => T): T;
|
|
51
|
+
/** The context set by `withAmbientContext`, for call sites that are not passed one yet. */
|
|
52
|
+
declare function ambientContext(): StimContext | undefined;
|
|
53
|
+
/** Writes `text` to the ambient context's output, or to the process stream outside one. */
|
|
54
|
+
declare function ambientWrite(stream: "stdout" | "stderr", text: string): void;
|
|
55
|
+
/** The environment of the ambient context, or `process.env` outside one. */
|
|
56
|
+
declare function ambientEnv(): NodeJS.ProcessEnv;
|
|
11
57
|
//#endregion
|
|
12
58
|
//#region macos-resources.d.ts
|
|
13
59
|
/** Whether a resource destination is a contained, non-empty path under Contents/Resources. */
|
|
@@ -17,6 +63,22 @@ declare function validMacosResourceDestination(value: unknown): value is string;
|
|
|
17
63
|
/** Quote a path for a shell command Stim prints for a human to copy and run. */
|
|
18
64
|
declare function quotedPath(path: string): string;
|
|
19
65
|
//#endregion
|
|
66
|
+
//#region systemd-unit.d.ts
|
|
67
|
+
/**
|
|
68
|
+
* One double-quoted word as systemd.syntax(7) reads it: C escapes inside the quotes, `%` doubled because unit files
|
|
69
|
+
* expand specifiers, and in `ExecStart=` `$` doubled because systemd expands variables in command lines.
|
|
70
|
+
*/
|
|
71
|
+
declare function quoteUnitWord(value: string, exec: boolean): string;
|
|
72
|
+
/** The words of a unit value `quoteUnitWord` wrote, or null when the value is not in that form. */
|
|
73
|
+
declare function unitWords(line: string, exec: boolean): string[] | null;
|
|
74
|
+
interface UnitLine {
|
|
75
|
+
section: string;
|
|
76
|
+
key: string;
|
|
77
|
+
value: string;
|
|
78
|
+
}
|
|
79
|
+
/** The `key=value` lines of a unit file with the section each is in, skipping comments and blank lines. */
|
|
80
|
+
declare function unitLines(text: string): UnitLine[];
|
|
81
|
+
//#endregion
|
|
20
82
|
//#region artifact-store.d.ts
|
|
21
83
|
declare function artifactIn(dir: string): string | null;
|
|
22
84
|
declare function resolveArtifact(dir: string): string | null;
|
|
@@ -28,21 +90,42 @@ interface StoreArtifactOptions {
|
|
|
28
90
|
}
|
|
29
91
|
declare function storeArtifact(dest: string, buildPath: string, { runFile, overwrite, writeMetadata, onRenameError }: StoreArtifactOptions): string | null;
|
|
30
92
|
//#endregion
|
|
93
|
+
//#region artifact-store-async.d.ts
|
|
94
|
+
interface AsyncStoreArtifactOptions extends Omit<StoreArtifactOptions, "runFile"> {
|
|
95
|
+
/** Resolves after the copy and its owned child work have ended. */
|
|
96
|
+
runFile: (file: string, args: string[]) => Promise<unknown>;
|
|
97
|
+
signal?: AbortSignal;
|
|
98
|
+
}
|
|
99
|
+
/** Awaits copying before atomic publication; cancellation before publication preserves the previous entry. */
|
|
100
|
+
declare function storeArtifactAsync(dest: string, buildPath: string, { runFile, overwrite, writeMetadata, onRenameError, signal }: AsyncStoreArtifactOptions): Promise<string | null>;
|
|
101
|
+
//#endregion
|
|
31
102
|
//#region compiled-helper.d.ts
|
|
32
103
|
/**
|
|
33
104
|
* Returns `<dir>/<name>-<hash>`, compiling it with `compile` first when it is missing. The hash covers `version` and
|
|
34
105
|
* each input's name and bytes, so a changed source or compiler builds a new helper beside the old one. `compile`
|
|
35
106
|
* writes to a private temporary path that is renamed into place, so concurrent builders never expose a partial file.
|
|
36
|
-
* Pruning is best effort and keeps this helper, the newest previous build, helpers modified in the last ten minutes and running helpers. It runs after a build and on the first call per process.
|
|
107
|
+
* Pruning is best effort and keeps this helper, the newest previous build, helpers modified in the last ten minutes and running helpers. It runs after a build and on the first call per process unless `prune` is false.
|
|
37
108
|
*/
|
|
38
|
-
declare function compiledHelper({ dir, name, inputs, version, compile }: {
|
|
109
|
+
declare function compiledHelper({ dir, name, inputs, version, compile, prune }: {
|
|
39
110
|
dir: string;
|
|
40
111
|
name: string;
|
|
41
112
|
inputs: readonly string[];
|
|
42
113
|
version: string;
|
|
43
114
|
compile: (output: string) => Promise<void>;
|
|
115
|
+
prune?: boolean;
|
|
44
116
|
}): Promise<string>;
|
|
45
117
|
//#endregion
|
|
118
|
+
//#region dependency-install.d.ts
|
|
119
|
+
/** The existing worker install command and marker identity for a repository's selected lockfile. */
|
|
120
|
+
declare function dependencyInstall(root: string, packageName: string | null): {
|
|
121
|
+
command: "pnpm" | "yarn" | "npm";
|
|
122
|
+
args: string[];
|
|
123
|
+
lockfile: string;
|
|
124
|
+
digest: string;
|
|
125
|
+
} | null;
|
|
126
|
+
/** Whether the worker can reuse this project's dependency install without running the package manager. */
|
|
127
|
+
declare function reusableDependencies(area: string, project: string, packageName: string | null, lockfile: string): boolean;
|
|
128
|
+
//#endregion
|
|
46
129
|
//#region metro-bridge.d.ts
|
|
47
130
|
interface MetroBridge {
|
|
48
131
|
server: Server;
|
|
@@ -61,6 +144,19 @@ declare function createMetroBridge({ gatewayPort, peer, secret }: {
|
|
|
61
144
|
secret: string;
|
|
62
145
|
}): MetroBridge;
|
|
63
146
|
//#endregion
|
|
147
|
+
//#region react-native-artifact.d.ts
|
|
148
|
+
/** Validates and canonicalizes the choices used by completed React Native peer artifacts. */
|
|
149
|
+
declare function reactNativeArtifactQuery(value: unknown): BuildReactNativeArtifactQuery | null;
|
|
150
|
+
/** The identity of a canonical React Native query and the toolchain verified around its build. */
|
|
151
|
+
declare function reactNativeArtifactIdentity(receipt: {
|
|
152
|
+
query: BuildReactNativeArtifactQuery;
|
|
153
|
+
toolchain: object;
|
|
154
|
+
}): BuildArtifactIdentity;
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region swiftpm-artifact.d.ts
|
|
157
|
+
/** Validates and canonicalizes the source and build choices of a completed SwiftPM peer artifact. */
|
|
158
|
+
declare function swiftpmArtifactQuery(value: unknown): BuildSwiftpmArtifactQuery | null;
|
|
159
|
+
//#endregion
|
|
64
160
|
//#region index.d.ts
|
|
65
161
|
declare function configDir(): string;
|
|
66
162
|
/** Creates `dir` and its missing parents owner-only, and narrows an existing `dir` that grants group or other access when the filesystem and owner allow it. */
|
|
@@ -125,4 +221,4 @@ declare function updateCacheManifest(file: string, mutate: (caches: Array<Record
|
|
|
125
221
|
declare function cacheDirRefusal(dir: string): string | null;
|
|
126
222
|
declare function registerCache({ dir, name, prune, note, entriesDepth, layout, replaces }: RegisterOptions): void;
|
|
127
223
|
//#endregion
|
|
128
|
-
export { BuildRunOptions, CacheManifest, CacheRegistrationMatch, type DirLockOptions, LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, type MetroBridge, RegisterOptions, STORE_ROOT_TAG, type StoreArtifactOptions, artifactIn, buildCacheKey, buildCacheRoot, cacheDirRefusal, cacheManifestLockPath, cacheNameSegment, cachePathSetting, compiledHelper, configDir, createMetroBridge, createMetroGateway, ensurePrivateDir, hostSimulatorArch, metroCacheRoot, quotedPath, readCacheManifest, registerCache, resolveArtifact, rotateLog, rotatedLogPath, sharedStoreRoot, storeArtifact, tagSharedStore, updateCacheManifest, validMacosResourceDestination, withDirLock, workspaceId, workspaceLogDir, workspaceName, workspaceSlug, workspaceStateDir };
|
|
224
|
+
export { type AsyncStoreArtifactOptions, BuildRunOptions, CacheManifest, CacheRegistrationMatch, type DirLockOptions, LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, type MetroBridge, RegisterOptions, STORE_ROOT_TAG, type StimContext, type StimOutput, type StoreArtifactOptions, type UnitLine, ambientContext, ambientEnv, ambientWrite, artifactIn, buildCacheKey, buildCacheRoot, cacheDirRefusal, cacheManifestLockPath, cacheNameSegment, cachePathSetting, compiledHelper, configDir, createMetroBridge, createMetroGateway, dependencyInstall, ensurePrivateDir, hostSimulatorArch, metroCacheRoot, quoteUnitWord, quotedPath, reactNativeArtifactIdentity, reactNativeArtifactQuery, readCacheManifest, registerCache, resolveArtifact, reusableDependencies, rotateLog, rotatedLogPath, sharedStoreRoot, storeArtifact, storeArtifactAsync, swiftpmArtifactQuery, tagSharedStore, unitLines, unitWords, updateCacheManifest, validMacosResourceDestination, withAmbientContext, withDirLock, withDirLockAsync, withDirLockTransaction, workspaceId, workspaceLogDir, workspaceName, workspaceSlug, workspaceStateDir };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as
|
|
3
|
-
export { LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, STORE_ROOT_TAG, artifactIn, buildCacheKey, buildCacheRoot, cacheDirRefusal, cacheManifestLockPath, cacheNameSegment, cachePathSetting, compiledHelper, configDir, createMetroBridge, createMetroGateway, ensurePrivateDir, hostSimulatorArch, metroCacheRoot, quotedPath, readCacheManifest, registerCache, resolveArtifact, rotateLog, rotatedLogPath, sharedStoreRoot, storeArtifact, tagSharedStore, updateCacheManifest, validMacosResourceDestination, withDirLock, workspaceId, workspaceLogDir, workspaceName, workspaceSlug, workspaceStateDir };
|
|
1
|
+
import { C as quotedPath } from "./ownership-claim-DHUs_Dir.mjs";
|
|
2
|
+
import { $ as withDirLockTransaction, A as createMetroGateway, B as unitWords, C as workspaceName, D as reactNativeArtifactIdentity, E as swiftpmArtifactQuery, F as artifactIn, G as withAmbientContext, H as ambientContext, I as resolveArtifact, L as storeArtifact, M as reusableDependencies, N as compiledHelper, O as reactNativeArtifactQuery, P as storeArtifactAsync, Q as withDirLockAsync, R as quoteUnitWord, S as workspaceLogDir, T as workspaceStateDir, U as ambientEnv, V as validMacosResourceDestination, W as ambientWrite, Z as withDirLock, _ as rotatedLogPath, a as buildCacheRoot, b as updateCacheManifest, c as cacheNameSegment, d as ensurePrivateDir, f as hostSimulatorArch, g as rotateLog, h as registerCache, i as buildCacheKey, j as dependencyInstall, k as createMetroBridge, l as cachePathSetting, m as readCacheManifest, n as METRO_NAMED_CACHE_LAYOUT, o as cacheDirRefusal, p as metroCacheRoot, r as STORE_ROOT_TAG, s as cacheManifestLockPath, t as LOG_ROTATE_BYTES, u as configDir, v as sharedStoreRoot, w as workspaceSlug, x as workspaceId, y as tagSharedStore, z as unitLines } from "./core-BTsCO1nW.mjs";
|
|
3
|
+
export { LOG_ROTATE_BYTES, METRO_NAMED_CACHE_LAYOUT, STORE_ROOT_TAG, ambientContext, ambientEnv, ambientWrite, artifactIn, buildCacheKey, buildCacheRoot, cacheDirRefusal, cacheManifestLockPath, cacheNameSegment, cachePathSetting, compiledHelper, configDir, createMetroBridge, createMetroGateway, dependencyInstall, ensurePrivateDir, hostSimulatorArch, metroCacheRoot, quoteUnitWord, quotedPath, reactNativeArtifactIdentity, reactNativeArtifactQuery, readCacheManifest, registerCache, resolveArtifact, reusableDependencies, rotateLog, rotatedLogPath, sharedStoreRoot, storeArtifact, storeArtifactAsync, swiftpmArtifactQuery, tagSharedStore, unitLines, unitWords, updateCacheManifest, validMacosResourceDestination, withAmbientContext, withDirLock, withDirLockAsync, withDirLockTransaction, workspaceId, workspaceLogDir, workspaceName, workspaceSlug, workspaceStateDir };
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
//#region state/status-derived.ts
|
|
2
|
+
const PLATFORMS = ["ios", "android"];
|
|
3
|
+
function isRunning(platform, device) {
|
|
4
|
+
if (platform === "ios") return device.state === "Booted";
|
|
5
|
+
return device.state === "detected" && !device.physical;
|
|
6
|
+
}
|
|
7
|
+
function appPresence(env, platform, device) {
|
|
8
|
+
if (!isRunning(platform, device) || device.app?.state === "running") return null;
|
|
9
|
+
const everBuilt = env.builds?.[platform]?.some((entry) => entry.result === "succeeded") ?? true;
|
|
10
|
+
if (env.lastBuilds?.[platform]?.status === "failed" && !everBuilt) return "none";
|
|
11
|
+
return device.app?.state === "stopped" ? "closed" : null;
|
|
12
|
+
}
|
|
13
|
+
function deviceSlots(env) {
|
|
14
|
+
return [{
|
|
15
|
+
slot: "default",
|
|
16
|
+
ios: env.ios,
|
|
17
|
+
android: env.android
|
|
18
|
+
}, ...env.slots ?? []];
|
|
19
|
+
}
|
|
20
|
+
function latestBuild(env) {
|
|
21
|
+
let latest = null;
|
|
22
|
+
for (const platform of PLATFORMS) {
|
|
23
|
+
const build = env.lastBuilds?.[platform];
|
|
24
|
+
if (build && (latest === null || Date.parse(build.startedAt) > Date.parse(latest.startedAt))) latest = build;
|
|
25
|
+
}
|
|
26
|
+
return latest;
|
|
27
|
+
}
|
|
28
|
+
function stage(kind, since, platform = null) {
|
|
29
|
+
return {
|
|
30
|
+
kind,
|
|
31
|
+
since: since ?? null,
|
|
32
|
+
platform,
|
|
33
|
+
closedApps: []
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function workspaceStage(env) {
|
|
37
|
+
if (env.macos?.build.state === "running") return stage("building", env.macos.build.startedAt, "macos");
|
|
38
|
+
if (env.macos?.build.state === "failed") return stage("build-failed", env.macos.build.finishedAt ?? env.macos.build.startedAt, "macos");
|
|
39
|
+
if (env.build?.state === "running") return stage("building", env.build.startedAt, env.build.platform);
|
|
40
|
+
const macosApp = env.macos?.state === "running" || env.macos?.state === "orphaned" ? env.macos : null;
|
|
41
|
+
const live = env.live || macosApp !== null;
|
|
42
|
+
if (!live && env.phase === "warming") return stage("warming", env.phaseSince);
|
|
43
|
+
if (!live && env.phase === "ready") return stage("ready", env.phaseSince);
|
|
44
|
+
const latest = latestBuild(env);
|
|
45
|
+
if (latest?.status === "failed") return stage("build-failed", latest.finishedAt ?? latest.startedAt, latest.platform);
|
|
46
|
+
if (live || (env.remoteDevices?.length ?? 0) > 0) {
|
|
47
|
+
const closedApps = deviceSlots(env).flatMap(({ slot, ...devices }) => PLATFORMS.flatMap((platform) => {
|
|
48
|
+
const device = devices[platform];
|
|
49
|
+
return device && appPresence(env, platform, device) === "closed" ? [{
|
|
50
|
+
platform,
|
|
51
|
+
slot
|
|
52
|
+
}] : [];
|
|
53
|
+
}));
|
|
54
|
+
return {
|
|
55
|
+
...stage("running", env.supervisor?.startedAt ?? macosApp?.build.finishedAt ?? macosApp?.build.startedAt),
|
|
56
|
+
closedApps
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const lastStop = env.metro?.lastStop;
|
|
60
|
+
return stage("stopped", lastStop && "at" in lastStop ? lastStop.at : null);
|
|
61
|
+
}
|
|
62
|
+
function checksState(checks) {
|
|
63
|
+
if (!checks) return null;
|
|
64
|
+
if (checks.failing > 0) return "failing";
|
|
65
|
+
if (checks.pending > 0) return "pending";
|
|
66
|
+
return checks.passing > 0 ? "passing" : null;
|
|
67
|
+
}
|
|
68
|
+
function gitChip(worktree) {
|
|
69
|
+
const git = worktree.git;
|
|
70
|
+
if (!git) return null;
|
|
71
|
+
const pull = worktree.pullRequest;
|
|
72
|
+
const parts = [];
|
|
73
|
+
const ahead = git.ahead ?? 0;
|
|
74
|
+
const behind = git.behind ?? 0;
|
|
75
|
+
if (ahead || behind) parts.push({
|
|
76
|
+
kind: "arrows",
|
|
77
|
+
ahead,
|
|
78
|
+
behind
|
|
79
|
+
});
|
|
80
|
+
const uncommitted = git.changed + git.untracked;
|
|
81
|
+
if (uncommitted) parts.push({
|
|
82
|
+
kind: "changed",
|
|
83
|
+
count: uncommitted
|
|
84
|
+
});
|
|
85
|
+
if (git.mergedInto !== null) {
|
|
86
|
+
if (pull?.state !== "merged") parts.push({
|
|
87
|
+
kind: "merged",
|
|
88
|
+
into: git.mergedInto
|
|
89
|
+
});
|
|
90
|
+
} else if (git.upstream === null) parts.push({ kind: "no-upstream" });
|
|
91
|
+
return {
|
|
92
|
+
parts,
|
|
93
|
+
ci: pull ? checksState(pull.checks) : null
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function buildCause(build) {
|
|
97
|
+
if (("result" in build ? build.result : build.status) !== "failed") return null;
|
|
98
|
+
const at = build.diagnostics?.find((d) => d.file && d.line !== null && d.line !== void 0);
|
|
99
|
+
if (at) return {
|
|
100
|
+
key: `${at.file}:${at.line}`,
|
|
101
|
+
file: at.file,
|
|
102
|
+
line: at.line
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
key: build.errorCode ?? "failed",
|
|
106
|
+
file: null,
|
|
107
|
+
line: null
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function withCauses(builds) {
|
|
111
|
+
if (!builds) return builds;
|
|
112
|
+
return Object.fromEntries(Object.entries(builds).map(([platform, list]) => [platform, list?.map((build) => withCause(build))]));
|
|
113
|
+
}
|
|
114
|
+
function withCause(build) {
|
|
115
|
+
const cause = buildCause(build);
|
|
116
|
+
return cause ? {
|
|
117
|
+
...build,
|
|
118
|
+
cause
|
|
119
|
+
} : build;
|
|
120
|
+
}
|
|
121
|
+
function withGitChip(worktree) {
|
|
122
|
+
const chip = gitChip(worktree);
|
|
123
|
+
return chip ? {
|
|
124
|
+
...worktree,
|
|
125
|
+
gitChip: chip
|
|
126
|
+
} : worktree;
|
|
127
|
+
}
|
|
128
|
+
/** The environment with the conclusions clients draw from it: `stage`, `appPresence`, `gitChip` and `cause`. */
|
|
129
|
+
function withDerivedFacts(env) {
|
|
130
|
+
const device = (platform, record) => record ? {
|
|
131
|
+
...record,
|
|
132
|
+
appPresence: appPresence(env, platform, record)
|
|
133
|
+
} : record;
|
|
134
|
+
const derived = {
|
|
135
|
+
...env,
|
|
136
|
+
stage: workspaceStage(env)
|
|
137
|
+
};
|
|
138
|
+
if (env.ios) derived.ios = device("ios", env.ios);
|
|
139
|
+
if (env.android) derived.android = device("android", env.android);
|
|
140
|
+
if (env.slots) derived.slots = env.slots.map((slot) => ({
|
|
141
|
+
...slot,
|
|
142
|
+
ios: device("ios", slot.ios),
|
|
143
|
+
android: device("android", slot.android)
|
|
144
|
+
}));
|
|
145
|
+
if (env.worktree) derived.worktree = withGitChip(env.worktree);
|
|
146
|
+
if (env.lastBuilds) derived.lastBuilds = Object.fromEntries(Object.entries(env.lastBuilds).map(([platform, build]) => [platform, withCause(build)]));
|
|
147
|
+
if (env.builds) derived.builds = withCauses(env.builds);
|
|
148
|
+
return derived;
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region state/status-views.ts
|
|
152
|
+
/** How many of the newest archives the workspace list names. */
|
|
153
|
+
const SUMMARY_ARCHIVES = 10;
|
|
154
|
+
function summaryBuild({ fingerprint: _fingerprint, missReason: _missReason, diagnostics, ...build }) {
|
|
155
|
+
const located = diagnostics?.find((diagnostic) => diagnostic.file && diagnostic.line !== null);
|
|
156
|
+
const kept = build;
|
|
157
|
+
if (located) kept.diagnostics = [located];
|
|
158
|
+
return kept;
|
|
159
|
+
}
|
|
160
|
+
function summaryHistory(entries) {
|
|
161
|
+
const streak = entries.findIndex((entry) => entry.result !== "failed");
|
|
162
|
+
return entries.slice(0, Math.max(1, streak === -1 ? entries.length : streak)).map(({ result, slot, phases, configuration: _configuration, cacheKey: _cacheKey, compileSteps: _steps, ...build }) => Object.assign(summaryBuild(build), {
|
|
163
|
+
result,
|
|
164
|
+
slot,
|
|
165
|
+
phases
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
function summaryWorktree({ pullRequest, ...worktree }) {
|
|
169
|
+
if (!pullRequest) return pullRequest === void 0 ? worktree : {
|
|
170
|
+
...worktree,
|
|
171
|
+
pullRequest
|
|
172
|
+
};
|
|
173
|
+
const { reviewDecision: _reviewDecision, checkedAt: _checkedAt, ...kept } = pullRequest;
|
|
174
|
+
return {
|
|
175
|
+
...worktree,
|
|
176
|
+
pullRequest: kept
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/** The summary of one environment of a status payload. */
|
|
180
|
+
function workspaceSummary(environment) {
|
|
181
|
+
const { logs, builds, lastBuilds, macos, agentDevice, worktree, ...rest } = environment;
|
|
182
|
+
const summary = rest;
|
|
183
|
+
if (worktree !== void 0) summary.worktree = worktree && summaryWorktree(worktree);
|
|
184
|
+
if (logs !== void 0) summary.logs = logs && { errorsSinceMarker: logs.errorsSinceMarker };
|
|
185
|
+
if (lastBuilds) {
|
|
186
|
+
summary.lastBuilds = {};
|
|
187
|
+
if (lastBuilds.ios) summary.lastBuilds.ios = summaryBuild(lastBuilds.ios);
|
|
188
|
+
if (lastBuilds.android) summary.lastBuilds.android = summaryBuild(lastBuilds.android);
|
|
189
|
+
}
|
|
190
|
+
if (builds) {
|
|
191
|
+
summary.builds = {};
|
|
192
|
+
for (const platform of [
|
|
193
|
+
"ios",
|
|
194
|
+
"android",
|
|
195
|
+
"macos"
|
|
196
|
+
]) {
|
|
197
|
+
const entries = builds[platform];
|
|
198
|
+
if (entries) summary.builds[platform] = summaryHistory(entries);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (macos !== void 0) summary.macos = macos && (({ arguments: _arguments, ...app }) => app)(macos);
|
|
202
|
+
if (environment.tutorial && agentDevice) summary.agentDevice = agentDevice;
|
|
203
|
+
return summary;
|
|
204
|
+
}
|
|
205
|
+
function worktreeRow({ path, branch, repository, git, gitChip, pullRequest }) {
|
|
206
|
+
const row = { path };
|
|
207
|
+
if (branch !== void 0) row.branch = branch;
|
|
208
|
+
if (repository !== void 0) row.repository = repository;
|
|
209
|
+
if (git !== void 0) row.git = git;
|
|
210
|
+
if (gitChip !== void 0) row.gitChip = gitChip;
|
|
211
|
+
if (pullRequest !== void 0) row.pullRequest = pullRequest && (({ reviewDecision: _review, checkedAt: _checked, ...kept }) => kept)(pullRequest);
|
|
212
|
+
return row;
|
|
213
|
+
}
|
|
214
|
+
/** The live views of a status payload as `stim status --json` prints it. */
|
|
215
|
+
function statusViews(payload) {
|
|
216
|
+
const archived = payload.archived ?? [];
|
|
217
|
+
return {
|
|
218
|
+
workspaces: payload.environments.map(workspaceSummary),
|
|
219
|
+
worktrees: payload.unprovisionedWorktrees.map(worktreeRow),
|
|
220
|
+
archived: {
|
|
221
|
+
count: payload.archivedUsage?.count ?? archived.length,
|
|
222
|
+
bytes: payload.archivedUsage?.bytes ?? 0,
|
|
223
|
+
newest: archived.toSorted((a, b) => b.removedAt.localeCompare(a.removedAt)).slice(0, 10).map(({ id, projectRoot, project, workspace, removedAt }) => ({
|
|
224
|
+
id,
|
|
225
|
+
projectRoot,
|
|
226
|
+
project,
|
|
227
|
+
workspace,
|
|
228
|
+
removedAt
|
|
229
|
+
}))
|
|
230
|
+
},
|
|
231
|
+
machine: {
|
|
232
|
+
capacity: payload.capacity,
|
|
233
|
+
machine: payload.machine ?? null,
|
|
234
|
+
deviceLeases: payload.deviceLeases,
|
|
235
|
+
simctlAvailable: payload.simctlAvailable,
|
|
236
|
+
maintenance: payload.maintenance ? (({ recent: _recent, sizes: _sizes, ...rest }) => rest)(payload.maintenance) : null
|
|
237
|
+
},
|
|
238
|
+
details: new Map(payload.environments.map((environment) => [environment.path, environment]))
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region state/keyed-delta.ts
|
|
243
|
+
/**
|
|
244
|
+
* Tracks the JSON of the entities last sent on one subscription, so the next emit carries only what changed. An
|
|
245
|
+
* entity counts as changed when its JSON differs, so the order of its fields matters and producers build entities
|
|
246
|
+
* with a stable field order.
|
|
247
|
+
*/
|
|
248
|
+
var KeyedEntities = class {
|
|
249
|
+
sent = /* @__PURE__ */ new Map();
|
|
250
|
+
key;
|
|
251
|
+
constructor(key) {
|
|
252
|
+
this.key = key;
|
|
253
|
+
}
|
|
254
|
+
/** Records `entities` as sent in full, for a snapshot. */
|
|
255
|
+
reset(entities) {
|
|
256
|
+
this.sent = new Map(entities.map((entity) => [this.key(entity), JSON.stringify(entity)]));
|
|
257
|
+
}
|
|
258
|
+
/** The delta from what was last sent to `entities`, recorded as sent; null when nothing changed. */
|
|
259
|
+
next(entities) {
|
|
260
|
+
const sent = /* @__PURE__ */ new Map();
|
|
261
|
+
const upsert = [];
|
|
262
|
+
for (const entity of entities) {
|
|
263
|
+
const key = this.key(entity);
|
|
264
|
+
const json = JSON.stringify(entity);
|
|
265
|
+
sent.set(key, json);
|
|
266
|
+
if (this.sent.get(key) !== json) upsert.push(entity);
|
|
267
|
+
}
|
|
268
|
+
const remove = [...this.sent.keys()].filter((key) => !sent.has(key));
|
|
269
|
+
this.sent = sent;
|
|
270
|
+
return upsert.length || remove.length ? {
|
|
271
|
+
upsert,
|
|
272
|
+
remove
|
|
273
|
+
} : null;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
/** Applies `delta` to `entities` keyed by `key`, keeping the order of `entities` and appending new keys. */
|
|
277
|
+
function applyKeyedDelta(entities, delta, key) {
|
|
278
|
+
const removed = new Set(delta.remove);
|
|
279
|
+
const replaced = new Map(delta.upsert.map((entity) => [key(entity), entity]));
|
|
280
|
+
return [...entities.flatMap((entity) => {
|
|
281
|
+
const id = key(entity);
|
|
282
|
+
if (removed.has(id)) return [];
|
|
283
|
+
const replacement = replaced.get(id);
|
|
284
|
+
replaced.delete(id);
|
|
285
|
+
return [replacement ?? entity];
|
|
286
|
+
}), ...replaced.values()];
|
|
287
|
+
}
|
|
288
|
+
//#endregion
|
|
289
|
+
export { workspaceSummary as a, statusViews as i, applyKeyedDelta as n, withDerivedFacts as o, SUMMARY_ARCHIVES as r, withGitChip as s, KeyedEntities as t };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
//#region noise.d.ts
|
|
2
|
+
declare const NOISE_XX = "Noise_XX_25519_ChaChaPoly_SHA256";
|
|
3
|
+
declare const NOISE_XX_PSK2 = "Noise_XXpsk2_25519_ChaChaPoly_SHA256";
|
|
4
|
+
type NoiseProtocolName = typeof NOISE_XX | typeof NOISE_XX_PSK2;
|
|
5
|
+
/** Largest handshake message either side writes or reads, payload included. */
|
|
6
|
+
declare const MAX_HANDSHAKE_MESSAGE_BYTES = 1024;
|
|
7
|
+
/** Thrown for any handshake or transport failure; the handshake or transport is unusable afterwards. */
|
|
8
|
+
declare class NoiseError extends Error {
|
|
9
|
+
override name: string;
|
|
10
|
+
}
|
|
11
|
+
/** A raw 32-byte X25519 key pair. */
|
|
12
|
+
interface NoiseKeyPair {
|
|
13
|
+
readonly publicKey: Buffer;
|
|
14
|
+
readonly privateKey: Buffer;
|
|
15
|
+
}
|
|
16
|
+
declare function generateNoiseKeyPair(): NoiseKeyPair;
|
|
17
|
+
declare function noiseKeyPairFromPrivateKey(privateKey: Uint8Array): NoiseKeyPair;
|
|
18
|
+
/** X25519 of a raw 32-byte private key and a peer's raw 32-byte public key; it throws for a low-order key. */
|
|
19
|
+
type NoiseDh = (privateKey: Buffer, publicKey: Buffer) => Uint8Array;
|
|
20
|
+
/** Noise's CipherState: a key and a 64-bit counter used as the nonce. */
|
|
21
|
+
declare class CipherState {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(key?: Uint8Array);
|
|
24
|
+
hasKey(): boolean;
|
|
25
|
+
setNonce(n: bigint): void;
|
|
26
|
+
encryptWithAd(ad: Uint8Array, plaintext: Uint8Array): Buffer;
|
|
27
|
+
decryptWithAd(ad: Uint8Array, ciphertext: Uint8Array): Buffer;
|
|
28
|
+
}
|
|
29
|
+
interface HandshakeOptions {
|
|
30
|
+
protocol: NoiseProtocolName;
|
|
31
|
+
initiator: boolean;
|
|
32
|
+
prologue: Uint8Array;
|
|
33
|
+
staticKeyPair: NoiseKeyPair;
|
|
34
|
+
/** 32 bytes, required for {@link NOISE_XX_PSK2} and refused for {@link NOISE_XX}. */
|
|
35
|
+
psk?: Uint8Array;
|
|
36
|
+
/** Fixed ephemeral key, only for reproducing published test vectors. */
|
|
37
|
+
ephemeralKeyPair?: NoiseKeyPair;
|
|
38
|
+
/**
|
|
39
|
+
* X25519 in place of node:crypto's, for a runtime whose node:crypto cannot derive with an imported raw public key,
|
|
40
|
+
* such as Cloudflare workerd.
|
|
41
|
+
*/
|
|
42
|
+
dh?: NoiseDh;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Noise's HandshakeState for {@link NOISE_XX} and {@link NOISE_XX_PSK2}. The initiator writes messages 1 and 3. Any
|
|
46
|
+
* error from {@link writeMessage} or {@link readMessage}, including an out-of-turn call, a call after the last
|
|
47
|
+
* message, or an invalid or oversized message, ends the handshake: every later call throws {@link NoiseError}.
|
|
48
|
+
*/
|
|
49
|
+
declare class HandshakeState {
|
|
50
|
+
#private;
|
|
51
|
+
constructor(options: HandshakeOptions);
|
|
52
|
+
get isComplete(): boolean;
|
|
53
|
+
/** The peer's static public key, once the message that carries it has been read and authenticated. */
|
|
54
|
+
get remoteStaticPublicKey(): Buffer | undefined;
|
|
55
|
+
get handshakeHash(): Buffer;
|
|
56
|
+
writeMessage(payload?: Uint8Array): Buffer;
|
|
57
|
+
readMessage(message: Uint8Array): Buffer;
|
|
58
|
+
/** Noise's Split(), returning this side's sending and receiving cipher states. It can be called once. */
|
|
59
|
+
split(): {
|
|
60
|
+
send: CipherState;
|
|
61
|
+
receive: CipherState;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
interface NoiseTransportOptions {
|
|
65
|
+
send: CipherState;
|
|
66
|
+
receive: CipherState;
|
|
67
|
+
/** Largest message, in bytes (UTF-8 bytes for text), this side may send. */
|
|
68
|
+
maxSendBytes: number;
|
|
69
|
+
/** Largest message, in bytes (UTF-8 bytes for text), this side accepts. */
|
|
70
|
+
maxReceiveBytes: number;
|
|
71
|
+
}
|
|
72
|
+
/** A message carried by one WebSocket message: text or binary, as the sender passed it. */
|
|
73
|
+
type NoiseMessage = {
|
|
74
|
+
type: "text";
|
|
75
|
+
data: string;
|
|
76
|
+
} | {
|
|
77
|
+
type: "binary";
|
|
78
|
+
data: Buffer;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Stim's transport framing after the handshake: each WebSocket message is one ChaCha20-Poly1305 message with empty
|
|
82
|
+
* associated data under the next counter value. The first plaintext byte is the message type, 0x01 text or 0x02
|
|
83
|
+
* binary. Counters are not sent, so a reordered, replayed, dropped or altered message fails to decrypt. Any decryption
|
|
84
|
+
* failure closes the transport: every later call throws {@link NoiseError}. Nonce 2^64-1 is reserved and never used:
|
|
85
|
+
* after a direction's last usable message, at counter 2^64-2, the next call in that direction closes the transport.
|
|
86
|
+
*/
|
|
87
|
+
declare class NoiseTransport {
|
|
88
|
+
#private;
|
|
89
|
+
constructor(options: NoiseTransportOptions);
|
|
90
|
+
get closed(): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Encrypts a string as a text message or bytes as a binary message. Data over `maxSendBytes` (UTF-8 bytes for text)
|
|
93
|
+
* throws a `RangeError`, consumes no counter, and leaves the transport open: it is the caller's own error and nothing
|
|
94
|
+
* was sent.
|
|
95
|
+
*/
|
|
96
|
+
encrypt(data: string | Uint8Array): Buffer;
|
|
97
|
+
decrypt(message: Uint8Array): NoiseMessage;
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { NOISE_XX as a, NoiseError as c, NoiseProtocolName as d, NoiseTransport as f, noiseKeyPairFromPrivateKey as h, MAX_HANDSHAKE_MESSAGE_BYTES as i, NoiseKeyPair as l, generateNoiseKeyPair as m, HandshakeOptions as n, NOISE_XX_PSK2 as o, NoiseTransportOptions as p, HandshakeState as r, NoiseDh as s, CipherState as t, NoiseMessage as u };
|