@theford/runly 0.0.0-stage → 0.20.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.
Files changed (128) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +157 -2
  3. package/dist/cli/client.d.ts +80 -0
  4. package/dist/cli/client.js +294 -0
  5. package/dist/cli/client.js.map +1 -0
  6. package/dist/cli/daemon-unit.d.ts +58 -0
  7. package/dist/cli/daemon-unit.js +289 -0
  8. package/dist/cli/daemon-unit.js.map +1 -0
  9. package/dist/cli/index.d.ts +2 -0
  10. package/dist/cli/index.js +1482 -0
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/cli/install.d.ts +9 -0
  13. package/dist/cli/install.js +53 -0
  14. package/dist/cli/install.js.map +1 -0
  15. package/dist/cli/logs.d.ts +43 -0
  16. package/dist/cli/logs.js +232 -0
  17. package/dist/cli/logs.js.map +1 -0
  18. package/dist/cli/tether.d.ts +3 -0
  19. package/dist/cli/tether.js +65 -0
  20. package/dist/cli/tether.js.map +1 -0
  21. package/dist/cli/watchdog.d.ts +1 -0
  22. package/dist/cli/watchdog.js +81 -0
  23. package/dist/cli/watchdog.js.map +1 -0
  24. package/dist/core/builds.d.ts +20 -0
  25. package/dist/core/builds.js +93 -0
  26. package/dist/core/builds.js.map +1 -0
  27. package/dist/core/caller-env.d.ts +17 -0
  28. package/dist/core/caller-env.js +77 -0
  29. package/dist/core/caller-env.js.map +1 -0
  30. package/dist/core/diagnostics.d.ts +68 -0
  31. package/dist/core/diagnostics.js +34 -0
  32. package/dist/core/diagnostics.js.map +1 -0
  33. package/dist/core/env-vars.d.ts +35 -0
  34. package/dist/core/env-vars.js +32 -0
  35. package/dist/core/env-vars.js.map +1 -0
  36. package/dist/core/events.d.ts +10 -0
  37. package/dist/core/events.js +42 -0
  38. package/dist/core/events.js.map +1 -0
  39. package/dist/core/exec.d.ts +22 -0
  40. package/dist/core/exec.js +84 -0
  41. package/dist/core/exec.js.map +1 -0
  42. package/dist/core/journal.d.ts +227 -0
  43. package/dist/core/journal.js +395 -0
  44. package/dist/core/journal.js.map +1 -0
  45. package/dist/core/logs.d.ts +56 -0
  46. package/dist/core/logs.js +259 -0
  47. package/dist/core/logs.js.map +1 -0
  48. package/dist/core/manifest.d.ts +292 -0
  49. package/dist/core/manifest.js +179 -0
  50. package/dist/core/manifest.js.map +1 -0
  51. package/dist/core/namespaces.d.ts +7 -0
  52. package/dist/core/namespaces.js +59 -0
  53. package/dist/core/namespaces.js.map +1 -0
  54. package/dist/core/paths.d.ts +31 -0
  55. package/dist/core/paths.js +65 -0
  56. package/dist/core/paths.js.map +1 -0
  57. package/dist/core/policy.d.ts +94 -0
  58. package/dist/core/policy.js +122 -0
  59. package/dist/core/policy.js.map +1 -0
  60. package/dist/core/ports.d.ts +52 -0
  61. package/dist/core/ports.js +94 -0
  62. package/dist/core/ports.js.map +1 -0
  63. package/dist/core/presets.d.ts +25 -0
  64. package/dist/core/presets.js +86 -0
  65. package/dist/core/presets.js.map +1 -0
  66. package/dist/core/procscan.d.ts +111 -0
  67. package/dist/core/procscan.js +342 -0
  68. package/dist/core/procscan.js.map +1 -0
  69. package/dist/core/retention.d.ts +97 -0
  70. package/dist/core/retention.js +322 -0
  71. package/dist/core/retention.js.map +1 -0
  72. package/dist/core/sleep.d.ts +51 -0
  73. package/dist/core/sleep.js +99 -0
  74. package/dist/core/sleep.js.map +1 -0
  75. package/dist/core/tree-ledger.d.ts +39 -0
  76. package/dist/core/tree-ledger.js +92 -0
  77. package/dist/core/tree-ledger.js.map +1 -0
  78. package/dist/core/types.d.ts +21 -0
  79. package/dist/core/types.js +6 -0
  80. package/dist/core/types.js.map +1 -0
  81. package/dist/core/units.d.ts +20 -0
  82. package/dist/core/units.js +69 -0
  83. package/dist/core/units.js.map +1 -0
  84. package/dist/core/upkeep.d.ts +90 -0
  85. package/dist/core/upkeep.js +176 -0
  86. package/dist/core/upkeep.js.map +1 -0
  87. package/dist/core/util.d.ts +46 -0
  88. package/dist/core/util.js +168 -0
  89. package/dist/core/util.js.map +1 -0
  90. package/dist/core/version.d.ts +39 -0
  91. package/dist/core/version.js +144 -0
  92. package/dist/core/version.js.map +1 -0
  93. package/dist/core/worktree.d.ts +51 -0
  94. package/dist/core/worktree.js +309 -0
  95. package/dist/core/worktree.js.map +1 -0
  96. package/dist/daemon/budget.d.ts +155 -0
  97. package/dist/daemon/budget.js +288 -0
  98. package/dist/daemon/budget.js.map +1 -0
  99. package/dist/daemon/election.d.ts +9 -0
  100. package/dist/daemon/election.js +140 -0
  101. package/dist/daemon/election.js.map +1 -0
  102. package/dist/daemon/engine.d.ts +1703 -0
  103. package/dist/daemon/engine.js +5305 -0
  104. package/dist/daemon/engine.js.map +1 -0
  105. package/dist/daemon/index.d.ts +2 -0
  106. package/dist/daemon/index.js +412 -0
  107. package/dist/daemon/index.js.map +1 -0
  108. package/dist/daemon/proxy.d.ts +108 -0
  109. package/dist/daemon/proxy.js +615 -0
  110. package/dist/daemon/proxy.js.map +1 -0
  111. package/dist/daemon/supervisor.d.ts +101 -0
  112. package/dist/daemon/supervisor.js +493 -0
  113. package/dist/daemon/supervisor.js.map +1 -0
  114. package/dist/drivers/appliances.d.ts +11 -0
  115. package/dist/drivers/appliances.js +174 -0
  116. package/dist/drivers/appliances.js.map +1 -0
  117. package/dist/drivers/datastores.d.ts +144 -0
  118. package/dist/drivers/datastores.js +775 -0
  119. package/dist/drivers/datastores.js.map +1 -0
  120. package/dist/drivers/preview.d.ts +69 -0
  121. package/dist/drivers/preview.js +605 -0
  122. package/dist/drivers/preview.js.map +1 -0
  123. package/dist/drivers/types.d.ts +70 -0
  124. package/dist/drivers/types.js +13 -0
  125. package/dist/drivers/types.js.map +1 -0
  126. package/package.json +63 -4
  127. package/schema/backlot.schema.json +549 -0
  128. package/schema/runly.schema.json +556 -0
@@ -0,0 +1,39 @@
1
+ export declare const VERSION: string;
2
+ export declare const BUILD: string;
3
+ /** True only when both builds are known and differ — a same-version rebuild or reinstall. */
4
+ export declare function rebuiltSince(cliBuild: string, daemonBuild: string | undefined): boolean;
5
+ /**
6
+ * Order two versions, or return undefined when they cannot be ordered.
7
+ *
8
+ * Deliberately not a full semver implementation: the only question asked of it
9
+ * is "is the daemon behind or ahead of this CLI", and the answer drives a
10
+ * message plus a refusal, never an install decision. A prerelease suffix makes
11
+ * two versions UNORDERED rather than guessing at precedence — a wrong guess
12
+ * would let `update` silently downgrade a daemon.
13
+ */
14
+ export declare function compareVersions(a: string, b: string): number | undefined;
15
+ export type SkewDirection = 'daemon-older' | 'daemon-newer' | 'daemon-unversioned' | 'unordered';
16
+ export interface VersionSkew {
17
+ cli: string;
18
+ daemon: string;
19
+ direction: SkewDirection;
20
+ /** Human sentence, ending in the remedy. Reused verbatim by CLI and doctor. */
21
+ message: string;
22
+ }
23
+ /**
24
+ * Compare this CLI against the daemon actually answering on the socket.
25
+ *
26
+ * This exists because `ensureDaemon` spawns the daemon from the CLI's own
27
+ * `dist/`, which means an upgrade replaces both files but does NOT replace the
28
+ * RUNNING daemon — it keeps serving old code for as long as it lives, and the
29
+ * socket carries no version. An old daemon does not reject arguments it has
30
+ * never heard of, it ignores them: a 0.8.0 daemon asked for `up --data-only`
31
+ * boots the whole application instead, and the caller is told nothing. That is
32
+ * the same failure shape as issue #41 — a wrong result that reads as a bug in
33
+ * a different subsystem — which is why skew is refused rather than warned
34
+ * about.
35
+ *
36
+ * `daemon` is undefined when the daemon predates version reporting (<= 0.8.0),
37
+ * which is itself proof of skew for any build that has this function.
38
+ */
39
+ export declare function versionSkew(cli: string, daemon: string | undefined): VersionSkew | null;
@@ -0,0 +1,144 @@
1
+ /**
2
+ * The one place that knows what version this build is, and the only comparison
3
+ * of a CLI against the daemon it is talking to.
4
+ *
5
+ * Read from package.json rather than baked into a constant: the MCP adapter
6
+ * carried a hand-maintained version that drifted (0.4.0 while the package
7
+ * shipped 0.5.0), so clients were told the wrong version of the tool they were
8
+ * driving. One path serves both installs — the npm tarball ships package.json
9
+ * beside `dist/`, and a git checkout has it at the same relative position.
10
+ */
11
+ import { readFileSync, statSync } from 'node:fs';
12
+ import { dirname, join } from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+ function readVersion() {
15
+ // A test cannot provoke version skew from a single build any other way: the
16
+ // CLI spawns the daemon from its OWN dist, so both sides always agree. This
17
+ // lets a test stand up a daemon that CLAIMS a different version than the CLI
18
+ // driving it. Never set it in real use.
19
+ const fake = process.env.BACKLOT_FAKE_VERSION;
20
+ if (fake)
21
+ return fake;
22
+ try {
23
+ const pkg = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'package.json');
24
+ const v = JSON.parse(readFileSync(pkg, 'utf8')).version;
25
+ if (typeof v === 'string' && v !== '')
26
+ return v;
27
+ }
28
+ catch {
29
+ /* fall through — see below */
30
+ }
31
+ // A build whose package.json is missing or unreadable still has to work: the
32
+ // version is a diagnostic, and failing every verb over an unreadable
33
+ // diagnostic would be worse than not knowing. `doctor` reports the gap, and
34
+ // an unknown version never counts as skew (it cannot be compared, and
35
+ // refusing on "I don't know" would wedge such an install completely).
36
+ return 'unknown';
37
+ }
38
+ export const VERSION = readVersion();
39
+ /**
40
+ * Which BUILD of this version is running — the modification time of this very
41
+ * module, as the process saw it when it loaded.
42
+ *
43
+ * The version alone cannot tell a rebuilt checkout from the build it replaced:
44
+ * `git pull && npm run build` on a checkout install keeps package.json at the
45
+ * same version while every file under dist/ changes, so `runly update` used to
46
+ * report "already runly 0.12.0 — nothing to do" and leave the old code serving.
47
+ * The daemon reads this once at start and keeps it; the CLI reads it fresh, so
48
+ * any rebuild or reinstall since the daemon started shows up as a difference.
49
+ * `unknown` (unreadable) never counts as a difference, for the same reason an
50
+ * unknown version never counts as skew.
51
+ */
52
+ function readBuild() {
53
+ const fake = process.env.BACKLOT_FAKE_BUILD;
54
+ if (fake)
55
+ return fake;
56
+ try {
57
+ return String(Math.trunc(statSync(fileURLToPath(import.meta.url)).mtimeMs));
58
+ }
59
+ catch {
60
+ return 'unknown';
61
+ }
62
+ }
63
+ export const BUILD = readBuild();
64
+ /** True only when both builds are known and differ — a same-version rebuild or reinstall. */
65
+ export function rebuiltSince(cliBuild, daemonBuild) {
66
+ if (daemonBuild === undefined || daemonBuild === 'unknown' || cliBuild === 'unknown')
67
+ return false;
68
+ return cliBuild !== daemonBuild;
69
+ }
70
+ /**
71
+ * Order two versions, or return undefined when they cannot be ordered.
72
+ *
73
+ * Deliberately not a full semver implementation: the only question asked of it
74
+ * is "is the daemon behind or ahead of this CLI", and the answer drives a
75
+ * message plus a refusal, never an install decision. A prerelease suffix makes
76
+ * two versions UNORDERED rather than guessing at precedence — a wrong guess
77
+ * would let `update` silently downgrade a daemon.
78
+ */
79
+ export function compareVersions(a, b) {
80
+ if (a === b)
81
+ return 0;
82
+ if (a === 'unknown' || b === 'unknown')
83
+ return undefined;
84
+ const parse = (v) => {
85
+ const m = /^(\d+)\.(\d+)\.(\d+)(.*)$/.exec(v.trim());
86
+ if (!m)
87
+ return undefined;
88
+ return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], suffix: m[4] ?? '' };
89
+ };
90
+ const pa = parse(a);
91
+ const pb = parse(b);
92
+ if (!pa || !pb)
93
+ return undefined;
94
+ for (let i = 0; i < 3; i++) {
95
+ if (pa.nums[i] !== pb.nums[i])
96
+ return pa.nums[i] < pb.nums[i] ? -1 : 1;
97
+ }
98
+ // Same release numbers, different text (a prerelease against its release, or
99
+ // two prereleases): different builds, no defensible order.
100
+ return undefined;
101
+ }
102
+ /**
103
+ * Compare this CLI against the daemon actually answering on the socket.
104
+ *
105
+ * This exists because `ensureDaemon` spawns the daemon from the CLI's own
106
+ * `dist/`, which means an upgrade replaces both files but does NOT replace the
107
+ * RUNNING daemon — it keeps serving old code for as long as it lives, and the
108
+ * socket carries no version. An old daemon does not reject arguments it has
109
+ * never heard of, it ignores them: a 0.8.0 daemon asked for `up --data-only`
110
+ * boots the whole application instead, and the caller is told nothing. That is
111
+ * the same failure shape as issue #41 — a wrong result that reads as a bug in
112
+ * a different subsystem — which is why skew is refused rather than warned
113
+ * about.
114
+ *
115
+ * `daemon` is undefined when the daemon predates version reporting (<= 0.8.0),
116
+ * which is itself proof of skew for any build that has this function.
117
+ */
118
+ export function versionSkew(cli, daemon) {
119
+ if (daemon === undefined) {
120
+ return {
121
+ cli,
122
+ daemon: 'pre-0.9.0',
123
+ direction: 'daemon-unversioned',
124
+ message: `the running daemon predates version reporting (runly <= 0.8.0) while this CLI is ${cli} — ` +
125
+ `it would serve your request with the old code, silently ignoring anything this version added. ` +
126
+ `Run 'runly update' to restart the daemon onto the installed version.`,
127
+ };
128
+ }
129
+ if (daemon === cli)
130
+ return null;
131
+ const order = compareVersions(daemon, cli);
132
+ const direction = order === undefined ? 'unordered' : order < 0 ? 'daemon-older' : 'daemon-newer';
133
+ const tail = direction === 'daemon-newer'
134
+ ? `You are running an OLDER CLI than the daemon; 'runly update' would DOWNGRADE it, so it refuses without --force. ` +
135
+ `Prefer invoking the newer CLI, or upgrade this one.`
136
+ : `Run 'runly update' to restart the daemon onto the installed version.`;
137
+ return {
138
+ cli,
139
+ daemon,
140
+ direction,
141
+ message: `the running daemon is runly ${daemon} but this CLI is ${cli} — it would serve your request with the other build's code. ${tail}`,
142
+ };
143
+ }
144
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { Manifest } from './manifest.js';
2
+ /**
3
+ * The literal directory (or file) each glob can only match under, as git
4
+ * pathspecs — or null when one of them can match anywhere (a bare name, a
5
+ * leading `**`), so the whole worktree has to be listed.
6
+ */
7
+ export declare function globPathspecs(globs: string[]): string[] | null;
8
+ /**
9
+ * The worktree's files that match `only` (globs relative to the stack root),
10
+ * or all of them when `only` is omitted.
11
+ *
12
+ * The listing is git's (one `ls-files`, limited to the globs' literal
13
+ * prefixes); the globs are applied BEFORE anything is stat'ed, so a bind
14
+ * whose upkeep rules name a lockfile and a migrations folder touches those
15
+ * files and nothing else of a 35k-file worktree.
16
+ *
17
+ * A submodule appears in ls-files only as its gitlink path, which stats as a
18
+ * directory, so a CHECKED-OUT submodule is enumerated recursively — a trigger
19
+ * inside it must fire like any other. An uninitialised submodule is an empty
20
+ * directory and contributes nothing. A repository without a .gitmodules has
21
+ * no gitlinks, and the second listing is skipped.
22
+ */
23
+ export declare function enumerateSource(stackRoot: string, manifest: Manifest, only?: string[]): Promise<string[]>;
24
+ /** Let the proxy and every other request run between two slices of a long loop. */
25
+ export declare const yieldLoop: () => Promise<void>;
26
+ /**
27
+ * `enumerateSource` with each file's size and mtime — ONE stat per file,
28
+ * which the build key needs anyway (decision 0038). The stats run in slices,
29
+ * yielding the event loop between them: on a 35k-file worktree the stat loop
30
+ * alone held every proxied connection of every environment for 100+ ms.
31
+ */
32
+ export declare function enumerateSourceStats(stackRoot: string, manifest: Manifest, only?: string[]): Promise<Array<{
33
+ path: string;
34
+ size: number;
35
+ mtimeMs: number;
36
+ }>>;
37
+ /**
38
+ * A snapshot of a service's declared build outputs: every file matching the
39
+ * globs, with its size and mtime, as one comparable string. `up` takes one
40
+ * before and one after the service's build and restarts the service only when
41
+ * they differ (decision 0032). Path + size + mtime is what a build tool
42
+ * changes when it writes; content is read only with `compare: content`
43
+ * (decision 0038).
44
+ *
45
+ * Each glob is walked from its literal prefix only, so `backend/host/bin/**`
46
+ * never walks the rest of the worktree. A glob that escapes the worktree is
47
+ * ignored.
48
+ */
49
+ export declare function snapshotOutputs(stackRoot: string, globs: string[], compare?: 'stat' | 'content'): Promise<string>;
50
+ /** sha256 of a file, streamed; null when it cannot be read. */
51
+ export declare function hashFile(path: string): Promise<string | null>;
@@ -0,0 +1,309 @@
1
+ /**
2
+ * What the worktree contains, for the few things runly still has to know about
3
+ * it (decision 0032). Environments run IN the caller's worktree and runly does
4
+ * not cache builds, so there is no whole-worktree source identity any more:
5
+ * MSBuild, pnpm and the Angular CLI decide their own incrementality.
6
+ *
7
+ * Two questions remain, and both are about DECLARED paths only:
8
+ *
9
+ * - which files match an upkeep rule's `when:` glob (and a datastore's
10
+ * @rebake-template trigger) — `enumerateSource` lists the candidates the
11
+ * globs are matched against; only the matching files are ever read;
12
+ * - whether a service's declared build `outputs:` changed across its build —
13
+ * `snapshotOutputs`, which decides whether `up` restarts the service.
14
+ *
15
+ * Git decides what belongs to the worktree (tracked + untracked-unignored
16
+ * under the stack root, plus sync.include and checked-out submodules, minus
17
+ * declared `caches:`).
18
+ */
19
+ import { execFile } from 'node:child_process';
20
+ import { createReadStream, existsSync, lstatSync, readdirSync } from 'node:fs';
21
+ import { createHash } from 'node:crypto';
22
+ import { dirname, join } from 'node:path';
23
+ import { promisify } from 'node:util';
24
+ import { isFile, matchesAny, safeJoin, statOf } from './util.js';
25
+ const execFileP = promisify(execFile);
26
+ /**
27
+ * git's file list under `root`, restricted to `pathspecs` when given (literal
28
+ * directory or file paths relative to `root`). Asynchronous: on a large
29
+ * worktree the listing takes long enough that a synchronous call stalled every
30
+ * proxied connection of every environment while it ran.
31
+ */
32
+ async function gitFiles(root, pathspecs = ['.']) {
33
+ try {
34
+ const { stdout } = await execFileP('git', ['-C', root, 'ls-files', '-z', '--cached', '--others', '--exclude-standard', '--', ...pathspecs], { encoding: 'utf8', maxBuffer: 256 * 1024 * 1024, env: { ...process.env, GIT_LITERAL_PATHSPECS: '1' } });
35
+ return stdout.split('\0').filter(Boolean);
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ /** Does the repository `root` belongs to declare submodules at all? (No .gitmodules, no gitlinks to look for.) */
42
+ function declaresSubmodules(root) {
43
+ for (let dir = root;; dir = dirname(dir)) {
44
+ if (existsSync(join(dir, '.gitmodules')))
45
+ return true;
46
+ if (existsSync(join(dir, '.git')) || dirname(dir) === dir)
47
+ return false;
48
+ }
49
+ }
50
+ /** Gitlink (submodule) paths under `root`, as `git ls-files -s` reports them. */
51
+ async function gitlinks(root) {
52
+ try {
53
+ const { stdout } = await execFileP('git', ['-C', root, 'ls-files', '-s', '-z', '--', '.'], {
54
+ encoding: 'utf8',
55
+ maxBuffer: 256 * 1024 * 1024,
56
+ });
57
+ return stdout
58
+ .split('\0')
59
+ .filter((l) => l.startsWith('160000 '))
60
+ .map((l) => l.split('\t')[1] ?? '')
61
+ .filter(Boolean);
62
+ }
63
+ catch {
64
+ return [];
65
+ }
66
+ }
67
+ /**
68
+ * The literal directory (or file) each glob can only match under, as git
69
+ * pathspecs — or null when one of them can match anywhere (a bare name, a
70
+ * leading `**`), so the whole worktree has to be listed.
71
+ */
72
+ export function globPathspecs(globs) {
73
+ const specs = new Set();
74
+ for (const g of globs) {
75
+ const p = g.replace(/^glob\((.*)\)$/, '$1').replace(/^\.\//, '');
76
+ const parts = p.split('/');
77
+ if (parts.length === 1 && !/[*?[]/.test(p))
78
+ return null; // a bare name matches a segment anywhere
79
+ const stop = parts.findIndex((seg) => /[*?[]/.test(seg));
80
+ const prefix = (stop === -1 ? parts : parts.slice(0, stop)).filter(Boolean).join('/');
81
+ if (!prefix || prefix.split('/').includes('..'))
82
+ return null;
83
+ specs.add(prefix);
84
+ }
85
+ return [...specs];
86
+ }
87
+ /**
88
+ * The worktree's files that match `only` (globs relative to the stack root),
89
+ * or all of them when `only` is omitted.
90
+ *
91
+ * The listing is git's (one `ls-files`, limited to the globs' literal
92
+ * prefixes); the globs are applied BEFORE anything is stat'ed, so a bind
93
+ * whose upkeep rules name a lockfile and a migrations folder touches those
94
+ * files and nothing else of a 35k-file worktree.
95
+ *
96
+ * A submodule appears in ls-files only as its gitlink path, which stats as a
97
+ * directory, so a CHECKED-OUT submodule is enumerated recursively — a trigger
98
+ * inside it must fire like any other. An uninitialised submodule is an empty
99
+ * directory and contributes nothing. A repository without a .gitmodules has
100
+ * no gitlinks, and the second listing is skipped.
101
+ */
102
+ export async function enumerateSource(stackRoot, manifest, only) {
103
+ return (await enumerateSourceStats(stackRoot, manifest, only)).map((f) => f.path);
104
+ }
105
+ /** Files between two yields to the event loop while a listing is stat'ed. */
106
+ const YIELD_EVERY = 2000;
107
+ /** Let the proxy and every other request run between two slices of a long loop. */
108
+ export const yieldLoop = () => new Promise((r) => setImmediate(r));
109
+ /**
110
+ * `enumerateSource` with each file's size and mtime — ONE stat per file,
111
+ * which the build key needs anyway (decision 0038). The stats run in slices,
112
+ * yielding the event loop between them: on a 35k-file worktree the stat loop
113
+ * alone held every proxied connection of every environment for 100+ ms.
114
+ */
115
+ export async function enumerateSourceStats(stackRoot, manifest, only) {
116
+ const pathspecs = only === undefined ? null : globPathspecs(only);
117
+ const narrowed = pathspecs !== null && pathspecs.length > 0;
118
+ const listed = (await gitFiles(stackRoot, narrowed ? pathspecs : undefined)) ?? walkAll(stackRoot);
119
+ const seen = new Set(listed);
120
+ const nested = async (root, prefix, depth) => {
121
+ if (depth > 8)
122
+ return; // a submodule cycle
123
+ if (!declaresSubmodules(root))
124
+ return;
125
+ for (const gl of await gitlinks(root)) {
126
+ const sub = join(root, gl);
127
+ if (!existsSync(join(sub, '.git')))
128
+ continue;
129
+ for (const f of (await gitFiles(sub)) ?? []) {
130
+ const rel = `${prefix}${gl}/${f}`;
131
+ if (!seen.has(rel)) {
132
+ seen.add(rel);
133
+ listed.push(rel);
134
+ }
135
+ }
136
+ await nested(sub, `${prefix}${gl}/`, depth + 1);
137
+ }
138
+ };
139
+ await nested(stackRoot, '', 0);
140
+ // sync.include: git-ignored files that still count as source (an .env.local
141
+ // an upkeep rule may name as its trigger).
142
+ for (const inc of manifest.sync?.include ?? []) {
143
+ safeJoin(stackRoot, inc, 'sync.include'); // reject ../ or absolute before use
144
+ if (isFile(join(stackRoot, inc)) && !seen.has(inc)) {
145
+ seen.add(inc);
146
+ listed.push(inc);
147
+ }
148
+ }
149
+ // `caches:` names what builds and installs write INTO the worktree. That is
150
+ // output, not source: a trigger glob like `**/package.json` must not fire
151
+ // because an install rewrote node_modules/. (git-ignored output is already
152
+ // absent; this covers a repo that has not ignored its own.) sync.include
153
+ // outranks it.
154
+ const caches = manifest.caches ?? [];
155
+ const included = new Set(manifest.sync?.include ?? []);
156
+ // Tracked-but-deleted files still appear in ls-files --cached.
157
+ const candidates = listed
158
+ .filter((f) => only === undefined || matchesAny(f, only))
159
+ .filter((f) => included.has(f) || caches.length === 0 || !matchesAny(f, caches))
160
+ .sort();
161
+ const out = [];
162
+ for (let i = 0; i < candidates.length; i++) {
163
+ if (i > 0 && i % YIELD_EVERY === 0)
164
+ await yieldLoop();
165
+ const f = candidates[i];
166
+ const st = statOf(join(stackRoot, f));
167
+ if (st?.isFile())
168
+ out.push({ path: f, size: st.size, mtimeMs: st.mtimeMs });
169
+ }
170
+ return out;
171
+ }
172
+ function walkAll(root, prefix = '') {
173
+ const out = [];
174
+ let names;
175
+ try {
176
+ names = readdirSync(join(root, prefix));
177
+ }
178
+ catch {
179
+ return out;
180
+ }
181
+ for (const name of names) {
182
+ if (name === '.git' || name === 'node_modules')
183
+ continue;
184
+ const rel = prefix ? `${prefix}/${name}` : name;
185
+ // lstat, not stat: a DANGLING symlink makes stat throw ENOENT, which took
186
+ // down the whole enumeration. Directory symlinks are listed but not
187
+ // followed, so a link pointing at an ancestor cannot recurse forever.
188
+ let st;
189
+ try {
190
+ st = lstatSync(join(root, rel));
191
+ }
192
+ catch {
193
+ continue; // vanished mid-walk
194
+ }
195
+ if (st.isSymbolicLink())
196
+ out.push(rel);
197
+ else if (st.isDirectory())
198
+ out.push(...walkAll(root, rel));
199
+ else
200
+ out.push(rel);
201
+ }
202
+ return out;
203
+ }
204
+ /**
205
+ * A snapshot of a service's declared build outputs: every file matching the
206
+ * globs, with its size and mtime, as one comparable string. `up` takes one
207
+ * before and one after the service's build and restarts the service only when
208
+ * they differ (decision 0032). Path + size + mtime is what a build tool
209
+ * changes when it writes; content is read only with `compare: content`
210
+ * (decision 0038).
211
+ *
212
+ * Each glob is walked from its literal prefix only, so `backend/host/bin/**`
213
+ * never walks the rest of the worktree. A glob that escapes the worktree is
214
+ * ignored.
215
+ */
216
+ export async function snapshotOutputs(stackRoot, globs, compare = 'stat') {
217
+ const seen = new Map();
218
+ let visited = 0;
219
+ // Sliced like the source listing: a bin/ of thousands of files must not
220
+ // hold the daemon's event loop (and every proxied connection) for its walk.
221
+ const walk = async (dir, rel, all = false) => {
222
+ let names;
223
+ try {
224
+ names = readdirSync(dir);
225
+ }
226
+ catch {
227
+ return;
228
+ }
229
+ for (const name of names) {
230
+ if (name === '.git')
231
+ continue;
232
+ if (++visited % YIELD_EVERY === 0)
233
+ await yieldLoop();
234
+ const childRel = rel ? `${rel}/${name}` : name;
235
+ const full = join(dir, name);
236
+ let st;
237
+ try {
238
+ st = lstatSync(full);
239
+ }
240
+ catch {
241
+ continue; // vanished mid-walk
242
+ }
243
+ if (st.isDirectory())
244
+ await walk(full, childRel, all);
245
+ else if (all || matchesAny(childRel, globs))
246
+ seen.set(childRel, { full, size: st.size, mtimeMs: st.mtimeMs });
247
+ }
248
+ };
249
+ for (const glob of globs) {
250
+ const parts = glob.replace(/^\.\//, '').split('/');
251
+ const stop = parts.findIndex((seg) => /[*?[]/.test(seg));
252
+ const base = (stop === -1 ? parts.slice(0, -1) : parts.slice(0, stop)).join('/');
253
+ let start;
254
+ try {
255
+ start = base ? safeJoin(stackRoot, base, 'outputs') : stackRoot;
256
+ }
257
+ catch {
258
+ continue;
259
+ }
260
+ if (stop === -1) {
261
+ // A literal file path: stat it directly.
262
+ try {
263
+ const literal = safeJoin(stackRoot, parts.join('/'), 'outputs');
264
+ const st = lstatSync(literal);
265
+ if (st.isFile())
266
+ seen.set(parts.join('/'), { full: literal, size: st.size, mtimeMs: st.mtimeMs });
267
+ else if (st.isDirectory())
268
+ await walk(join(stackRoot, parts.join('/')), parts.join('/'), true); // a directory: everything under it
269
+ }
270
+ catch {
271
+ /* absent */
272
+ }
273
+ continue;
274
+ }
275
+ await walk(start, base);
276
+ }
277
+ const entries = [...seen].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
278
+ // `content` (decision 0038): a build tool that rewrites identical files
279
+ // (the Angular CLI does) moves every mtime and would restart the service
280
+ // for nothing; hashing what is IN the files ignores that. Streamed, a few
281
+ // at a time, so a large bundle never blocks the daemon's event loop.
282
+ const prints = compare === 'stat'
283
+ ? entries.map(([, st]) => `${st.size}:${st.mtimeMs}`)
284
+ : await mapLimit(entries, 8, async ([, st]) => `${st.size}:${(await hashFile(st.full)) ?? 'unreadable'}`);
285
+ return entries.map(([f], i) => `${f}:${prints[i]}`).join('\n');
286
+ }
287
+ /** sha256 of a file, streamed; null when it cannot be read. */
288
+ export function hashFile(path) {
289
+ return new Promise((resolve) => {
290
+ const hash = createHash('sha256');
291
+ const stream = createReadStream(path);
292
+ stream.on('data', (chunk) => hash.update(chunk));
293
+ stream.on('end', () => resolve(hash.digest('hex')));
294
+ stream.on('error', () => resolve(null));
295
+ });
296
+ }
297
+ async function mapLimit(items, limit, fn) {
298
+ const out = new Array(items.length);
299
+ let next = 0;
300
+ const worker = async () => {
301
+ while (next < items.length) {
302
+ const i = next++;
303
+ out[i] = await fn(items[i]);
304
+ }
305
+ };
306
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
307
+ return out;
308
+ }
309
+ //# sourceMappingURL=worktree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/core/worktree.ts"],"names":[],"mappings":"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"}