@farmslot/recipe-cli 0.1.2 → 0.3.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 (99) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +14 -10
  3. package/dist/cli.js +3 -3
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands.js +2 -2
  6. package/dist/commands.js.map +1 -1
  7. package/dist/composition.d.ts +1 -1
  8. package/dist/composition.d.ts.map +1 -1
  9. package/dist/composition.js +1 -1
  10. package/dist/composition.js.map +1 -1
  11. package/dist/discovery-index.d.ts +1 -1
  12. package/dist/discovery-index.d.ts.map +1 -1
  13. package/dist/discovery-index.js +1 -1
  14. package/dist/discovery-index.js.map +1 -1
  15. package/dist/harness/adapters.d.ts +25 -0
  16. package/dist/harness/adapters.d.ts.map +1 -0
  17. package/dist/harness/adapters.js +87 -0
  18. package/dist/harness/adapters.js.map +1 -0
  19. package/dist/harness/artifact-files.d.ts +11 -0
  20. package/dist/harness/artifact-files.d.ts.map +1 -0
  21. package/dist/harness/artifact-files.js +75 -0
  22. package/dist/harness/artifact-files.js.map +1 -0
  23. package/dist/harness/checkout-lock.d.ts +13 -0
  24. package/dist/harness/checkout-lock.d.ts.map +1 -0
  25. package/dist/harness/checkout-lock.js +211 -0
  26. package/dist/harness/checkout-lock.js.map +1 -0
  27. package/dist/harness/cli-color.d.ts +25 -0
  28. package/dist/harness/cli-color.d.ts.map +1 -0
  29. package/dist/harness/cli-color.js +101 -0
  30. package/dist/harness/cli-color.js.map +1 -0
  31. package/dist/harness/command-journal.d.ts +25 -0
  32. package/dist/harness/command-journal.d.ts.map +1 -0
  33. package/dist/harness/command-journal.js +322 -0
  34. package/dist/harness/command-journal.js.map +1 -0
  35. package/dist/harness/commands/last.d.ts +3 -0
  36. package/dist/harness/commands/last.d.ts.map +1 -0
  37. package/dist/harness/commands/last.js +59 -0
  38. package/dist/harness/commands/last.js.map +1 -0
  39. package/dist/harness/commands/launch.d.ts +2 -0
  40. package/dist/harness/commands/launch.d.ts.map +1 -0
  41. package/dist/harness/commands/launch.js +105 -0
  42. package/dist/harness/commands/launch.js.map +1 -0
  43. package/dist/harness/commands/reload.d.ts +2 -0
  44. package/dist/harness/commands/reload.d.ts.map +1 -0
  45. package/dist/harness/commands/reload.js +24 -0
  46. package/dist/harness/commands/reload.js.map +1 -0
  47. package/dist/harness/commands/stop.d.ts +9 -0
  48. package/dist/harness/commands/stop.d.ts.map +1 -0
  49. package/dist/harness/commands/stop.js +60 -0
  50. package/dist/harness/commands/stop.js.map +1 -0
  51. package/dist/harness/heal-bounds.d.ts +15 -0
  52. package/dist/harness/heal-bounds.d.ts.map +1 -0
  53. package/dist/harness/heal-bounds.js +194 -0
  54. package/dist/harness/heal-bounds.js.map +1 -0
  55. package/dist/harness/host.d.ts +37 -0
  56. package/dist/harness/host.d.ts.map +1 -0
  57. package/dist/harness/host.js +48 -0
  58. package/dist/harness/host.js.map +1 -0
  59. package/dist/harness/index.d.ts +19 -0
  60. package/dist/harness/index.d.ts.map +1 -0
  61. package/dist/harness/index.js +22 -0
  62. package/dist/harness/index.js.map +1 -0
  63. package/dist/harness/json-stream.d.ts +19 -0
  64. package/dist/harness/json-stream.d.ts.map +1 -0
  65. package/dist/harness/json-stream.js +59 -0
  66. package/dist/harness/json-stream.js.map +1 -0
  67. package/dist/harness/leaf-invoke.d.ts +8 -0
  68. package/dist/harness/leaf-invoke.d.ts.map +1 -0
  69. package/dist/harness/leaf-invoke.js +31 -0
  70. package/dist/harness/leaf-invoke.js.map +1 -0
  71. package/dist/harness/library-provenance.d.ts +3 -0
  72. package/dist/harness/library-provenance.d.ts.map +1 -0
  73. package/dist/harness/library-provenance.js +31 -0
  74. package/dist/harness/library-provenance.js.map +1 -0
  75. package/dist/harness/overlay.d.ts +17 -0
  76. package/dist/harness/overlay.d.ts.map +1 -0
  77. package/dist/harness/overlay.js +415 -0
  78. package/dist/harness/overlay.js.map +1 -0
  79. package/dist/harness/parse-args.d.ts +35 -0
  80. package/dist/harness/parse-args.d.ts.map +1 -0
  81. package/dist/harness/parse-args.js +249 -0
  82. package/dist/harness/parse-args.js.map +1 -0
  83. package/dist/harness/paths.d.ts +15 -0
  84. package/dist/harness/paths.d.ts.map +1 -0
  85. package/dist/harness/paths.js +29 -0
  86. package/dist/harness/paths.js.map +1 -0
  87. package/dist/harness/shared.d.ts +43 -0
  88. package/dist/harness/shared.d.ts.map +1 -0
  89. package/dist/harness/shared.js +414 -0
  90. package/dist/harness/shared.js.map +1 -0
  91. package/dist/libraries.d.ts +1 -1
  92. package/dist/libraries.d.ts.map +1 -1
  93. package/dist/libraries.js +1 -1
  94. package/dist/libraries.js.map +1 -1
  95. package/dist/search.d.ts +1 -1
  96. package/dist/search.d.ts.map +1 -1
  97. package/dist/types.d.ts +1 -1
  98. package/dist/types.d.ts.map +1 -1
  99. package/package.json +10 -3
@@ -0,0 +1,75 @@
1
+ import { constants as fsConstants } from 'node:fs';
2
+ import { lstat, mkdir, open, readFile, realpath, writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ export async function writeContainedArtifact(artifactsDir, relativePath, value, label) {
5
+ const file = resolveContainedArtifact(artifactsDir, relativePath, label);
6
+ await mkdir(path.dirname(file), { recursive: true });
7
+ await assertContainedParent(artifactsDir, file, label);
8
+ await assertNotSymlink(file, label);
9
+ const handle = await open(file, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_TRUNC | fsConstants.O_NOFOLLOW, 0o600);
10
+ try {
11
+ await handle.writeFile(value);
12
+ }
13
+ finally {
14
+ await handle.close();
15
+ }
16
+ }
17
+ export async function readContainedJsonArtifact(artifactsDir, relativePath, maxBytes, label) {
18
+ const file = resolveContainedArtifact(artifactsDir, relativePath, label);
19
+ await assertContainedParent(artifactsDir, file, label);
20
+ await assertNotSymlink(file, label);
21
+ const handle = await open(file, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
22
+ try {
23
+ const info = await handle.stat();
24
+ if (!info.isFile() || info.size > maxBytes) {
25
+ throw new Error(`${label} is not a bounded regular file.`);
26
+ }
27
+ return JSON.parse(await handle.readFile('utf8'));
28
+ }
29
+ finally {
30
+ await handle.close();
31
+ }
32
+ }
33
+ export async function indexArtifactManifest(manifestPath, entries) {
34
+ const parsed = JSON.parse(await readFile(manifestPath, 'utf8'));
35
+ const manifest = asRecord(parsed);
36
+ const replacementPaths = new Set(entries.map((entry) => entry.path));
37
+ const current = Array.isArray(manifest.artifacts)
38
+ ? manifest.artifacts.filter((artifact) => !replacementPaths.has(String(asRecord(artifact).path ?? '')))
39
+ : [];
40
+ await writeFile(manifestPath, `${JSON.stringify({ ...manifest, artifacts: [...current, ...entries] }, null, 2)}\n`);
41
+ }
42
+ function resolveContainedArtifact(artifactsDir, relativePath, label) {
43
+ const root = path.resolve(artifactsDir);
44
+ const file = path.resolve(root, relativePath);
45
+ const relative = path.relative(root, file);
46
+ if (relative.startsWith('..') || path.isAbsolute(relative)) {
47
+ throw new Error(`${label} path must stay inside artifactsDir.`);
48
+ }
49
+ return file;
50
+ }
51
+ async function assertContainedParent(artifactsDir, file, label) {
52
+ const root = await realpath(artifactsDir);
53
+ const parent = await realpath(path.dirname(file));
54
+ const relative = path.relative(root, parent);
55
+ if (relative.startsWith('..') || path.isAbsolute(relative)) {
56
+ throw new Error(`${label} parent must stay inside artifactsDir.`);
57
+ }
58
+ }
59
+ async function assertNotSymlink(file, label) {
60
+ try {
61
+ if ((await lstat(file)).isSymbolicLink()) {
62
+ throw new Error(`${label} must not be a symbolic link.`);
63
+ }
64
+ }
65
+ catch (error) {
66
+ if (error.code !== 'ENOENT')
67
+ throw error;
68
+ }
69
+ }
70
+ function asRecord(value) {
71
+ return value && typeof value === 'object' && !Array.isArray(value)
72
+ ? value
73
+ : {};
74
+ }
75
+ //# sourceMappingURL=artifact-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-files.js","sourceRoot":"","sources":["../../src/harness/artifact-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface CheckoutLock {
2
+ path: string;
3
+ release: () => void;
4
+ }
5
+ export interface CheckoutLockFailure {
6
+ path: string;
7
+ owner: Record<string, unknown> | null;
8
+ message: string;
9
+ }
10
+ export declare function acquireCheckoutLock(target: string, operation: string): CheckoutLock | CheckoutLockFailure;
11
+ /** Each nested launcher records its child independently under the inherited lock. */
12
+ export declare function trackCheckoutChild(target: string, pid: number, ownsProcessGroup?: boolean): () => void;
13
+ //# sourceMappingURL=checkout-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout-lock.d.ts","sourceRoot":"","sources":["../../src/harness/checkout-lock.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,YAAY,GAAG,mBAAmB,CA8EpC;AA+FD,qFAAqF;AACrF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,gBAAgB,UAAQ,GACvB,MAAM,IAAI,CAiCZ"}
@@ -0,0 +1,211 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { ensureRuntimeDirectory, processIdentity } from '@farmslot/recipe-runner/runtime/operation';
5
+ import { hostEnvName } from './host.js';
6
+ import { recipeRuntimeDir } from './paths.js';
7
+ const lockTokenEnv = () => hostEnvName('CHECKOUT_LOCK_TOKEN');
8
+ export function acquireCheckoutLock(target, operation) {
9
+ const repoRoot = fs.realpathSync(target);
10
+ const lockPath = path.join(repoRoot, recipeRuntimeDir(), 'sandbox.lock');
11
+ ensureRuntimeDirectory(path.dirname(lockPath));
12
+ const tokenEnv = lockTokenEnv();
13
+ const inheritedToken = process.env[tokenEnv];
14
+ for (let attempt = 0; attempt < 4; attempt += 1) {
15
+ try {
16
+ const identity = processIdentity(process.pid);
17
+ if (!identity)
18
+ throw new Error('Cannot identify checkout lock owner process');
19
+ const fd = fs.openSync(lockPath, 'wx', 0o600);
20
+ fs.fchmodSync(fd, 0o600);
21
+ const token = randomUUID();
22
+ const owner = {
23
+ schemaVersion: 1,
24
+ pid: process.pid,
25
+ processStartedAt: identity,
26
+ operation,
27
+ repoRoot,
28
+ token,
29
+ startedAt: new Date().toISOString(),
30
+ };
31
+ fs.writeFileSync(fd, `${JSON.stringify(owner, null, 2)}\n`);
32
+ fs.closeSync(fd);
33
+ process.env[tokenEnv] = token;
34
+ return {
35
+ path: lockPath,
36
+ release: () => {
37
+ const current = readOwner(lockPath);
38
+ if (current?.pid === process.pid && current.token === token && !childrenAlive(current)) {
39
+ fs.rmSync(lockPath, { force: true });
40
+ fs.rmSync(path.join(path.dirname(lockPath), 'lock-members', token), {
41
+ recursive: true,
42
+ force: true,
43
+ });
44
+ }
45
+ if (process.env[tokenEnv] === token) {
46
+ if (inheritedToken)
47
+ process.env[tokenEnv] = inheritedToken;
48
+ else
49
+ delete process.env[tokenEnv];
50
+ }
51
+ },
52
+ };
53
+ }
54
+ catch (error) {
55
+ if (error.code !== 'EEXIST')
56
+ throw error;
57
+ const owner = readOwner(lockPath);
58
+ if (inheritedToken &&
59
+ owner?.token === inheritedToken &&
60
+ owner.repoRoot === repoRoot &&
61
+ typeof owner.pid === 'number' &&
62
+ ownerAlive(owner)) {
63
+ trackCheckoutChild(repoRoot, process.pid);
64
+ return { path: lockPath, release: () => undefined };
65
+ }
66
+ if (owner && typeof owner.pid === 'number' && ownerAlive(owner)) {
67
+ return {
68
+ path: lockPath,
69
+ owner,
70
+ message: `checkout sandbox is busy with ${String(owner.operation ?? 'another operation')} (pid ${owner.pid}).`,
71
+ };
72
+ }
73
+ if (!owner || childrenAlive(owner)) {
74
+ return {
75
+ path: lockPath,
76
+ owner,
77
+ message: `checkout sandbox owner is unreadable or has surviving child processes. Inspect ${lockPath} and its lock-members directory; remove the lock only after verifying all recorded processes have exited.`,
78
+ };
79
+ }
80
+ claimStaleLock(lockPath, owner);
81
+ }
82
+ }
83
+ return {
84
+ path: lockPath,
85
+ owner: readOwner(lockPath),
86
+ message: 'checkout sandbox lock could not be acquired.',
87
+ };
88
+ }
89
+ function claimStaleLock(lockPath, expectedOwner) {
90
+ const claimedPath = `${lockPath}.stale-${process.pid}-${randomUUID()}`;
91
+ try {
92
+ fs.renameSync(lockPath, claimedPath);
93
+ }
94
+ catch (error) {
95
+ if (error.code === 'ENOENT')
96
+ return;
97
+ throw error;
98
+ }
99
+ try {
100
+ const claimedOwner = readOwner(claimedPath);
101
+ if (!sameOwner(claimedOwner, expectedOwner) ||
102
+ (claimedOwner &&
103
+ typeof claimedOwner.pid === 'number' &&
104
+ (ownerAlive(claimedOwner) || childrenAlive(claimedOwner)))) {
105
+ try {
106
+ // Restore only when nobody else acquired the now-empty canonical path.
107
+ // linkSync is atomic and never overwrites a newer owner's lock.
108
+ fs.linkSync(claimedPath, lockPath);
109
+ }
110
+ catch (error) {
111
+ if (error.code !== 'EEXIST')
112
+ throw error;
113
+ }
114
+ }
115
+ else if (typeof claimedOwner?.token === 'string' &&
116
+ /^[a-f0-9-]{36}$/.test(claimedOwner.token)) {
117
+ fs.rmSync(path.join(path.dirname(lockPath), 'lock-members', claimedOwner.token), {
118
+ recursive: true,
119
+ force: true,
120
+ });
121
+ }
122
+ }
123
+ finally {
124
+ fs.rmSync(claimedPath, { force: true });
125
+ }
126
+ }
127
+ function sameOwner(left, right) {
128
+ if (!left || !right)
129
+ return left === right;
130
+ return (left.pid === right.pid &&
131
+ left.token === right.token &&
132
+ left.startedAt === right.startedAt &&
133
+ left.repoRoot === right.repoRoot);
134
+ }
135
+ function readOwner(lockPath) {
136
+ try {
137
+ const value = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
138
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
139
+ }
140
+ catch {
141
+ return null;
142
+ }
143
+ }
144
+ function ownerAlive(owner) {
145
+ if (typeof owner.pid !== 'number')
146
+ return true;
147
+ if (typeof owner.processStartedAt === 'string')
148
+ return processIdentity(owner.pid) === owner.processStartedAt;
149
+ return processAlive(owner.pid);
150
+ }
151
+ function childrenAlive(owner) {
152
+ if (typeof owner.repoRoot !== 'string' ||
153
+ typeof owner.token !== 'string' ||
154
+ !/^[a-f0-9-]{36}$/.test(owner.token))
155
+ return false;
156
+ const directory = path.join(owner.repoRoot, recipeRuntimeDir(), 'lock-members', owner.token);
157
+ if (!fs.existsSync(directory))
158
+ return false;
159
+ return fs
160
+ .readdirSync(directory)
161
+ .filter((name) => name.endsWith('.json'))
162
+ .some((name) => {
163
+ const child = readOwner(path.join(directory, name));
164
+ if (child && typeof child.processGroupId === 'number' && child.processGroupId > 0)
165
+ return processAlive(-child.processGroupId);
166
+ // An incomplete membership write is uncertain ownership, never permission to reclaim.
167
+ return (!child ||
168
+ typeof child.pid !== 'number' ||
169
+ typeof child.processStartedAt !== 'string' ||
170
+ (child.pid !== owner.pid && processIdentity(child.pid) === child.processStartedAt));
171
+ });
172
+ }
173
+ /** Each nested launcher records its child independently under the inherited lock. */
174
+ export function trackCheckoutChild(target, pid, ownsProcessGroup = false) {
175
+ const repoRoot = fs.realpathSync(target);
176
+ const lockPath = path.join(repoRoot, recipeRuntimeDir(), 'sandbox.lock');
177
+ const owner = readOwner(lockPath);
178
+ if (!owner ||
179
+ owner.token !== process.env[lockTokenEnv()] ||
180
+ typeof owner.token !== 'string' ||
181
+ !/^[a-f0-9-]{36}$/.test(owner.token))
182
+ return () => undefined;
183
+ const identity = processIdentity(pid);
184
+ if (!identity && !ownsProcessGroup)
185
+ return () => undefined;
186
+ const directory = path.join(repoRoot, recipeRuntimeDir(), 'lock-members', owner.token);
187
+ ensureRuntimeDirectory(directory);
188
+ const file = path.join(directory, `${pid}.json`);
189
+ const previous = readOwner(file);
190
+ const group = ownsProcessGroup ||
191
+ (previous?.processStartedAt === identity && previous?.processGroupId === pid);
192
+ const temporary = `${file}.${process.pid}.${randomUUID()}.tmp`;
193
+ fs.writeFileSync(temporary, JSON.stringify({ pid, processStartedAt: identity, ...(group ? { processGroupId: pid } : {}) }), { mode: 0o600 });
194
+ fs.chmodSync(temporary, 0o600);
195
+ fs.renameSync(temporary, file);
196
+ return () => {
197
+ const current = readOwner(file);
198
+ if (current?.pid === pid && current.processStartedAt === identity)
199
+ fs.rmSync(file, { force: true });
200
+ };
201
+ }
202
+ function processAlive(pid) {
203
+ try {
204
+ process.kill(pid, 0);
205
+ return true;
206
+ }
207
+ catch (error) {
208
+ return error.code === 'EPERM';
209
+ }
210
+ }
211
+ //# sourceMappingURL=checkout-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout-lock.js","sourceRoot":"","sources":["../../src/harness/checkout-lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * cli-color.ts — shared ANSI styling for recipe CLI human output.
3
+ *
4
+ * Honors NO_COLOR, RECIPE_NO_COLOR, FORCE_COLOR, RECIPE_COLOR, and TTY detection.
5
+ */
6
+ export declare function colorEnabled(stream?: NodeJS.WriteStream): boolean;
7
+ export declare function stripAnsi(text: string): string;
8
+ export declare function color(style: string, text: string, { stream }?: {
9
+ stream?: NodeJS.WriteStream;
10
+ }): string;
11
+ export declare function colorKv(label: string, value: string, valueStyle?: string, { stream }?: {
12
+ stream?: NodeJS.WriteStream;
13
+ }): string;
14
+ export declare function colorStatusWord(word: string, { stream }?: {
15
+ stream?: NodeJS.WriteStream;
16
+ }): string;
17
+ export declare function colorHumanMessage(message: string, { stream }?: {
18
+ stream?: NodeJS.WriteStream;
19
+ }): string;
20
+ export declare function classifyLogEvent(event: string): string;
21
+ export declare function colorLogEvent(event: string, { latest, stream, }?: {
22
+ latest?: boolean;
23
+ stream?: NodeJS.WriteStream;
24
+ }): string;
25
+ //# sourceMappingURL=cli-color.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-color.d.ts","sourceRoot":"","sources":["../../src/harness/cli-color.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAiCH,wBAAgB,YAAY,CAAC,MAAM,GAAE,MAAM,CAAC,WAA4B,GAAG,OAAO,CAOjF;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,KAAK,CACnB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,EAAE,MAAuB,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAA;CAAO,GAChE,MAAM,CAMR;AAED,wBAAgB,OAAO,CACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,UAAU,SAAS,EACnB,EAAE,MAAuB,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAA;CAAO,GAChE,MAAM,CAER;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,EAAE,MAAuB,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAA;CAAO,GAChE,MAAM,CAgBR;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,EAAE,MAAuB,EAAE,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAA;CAAO,GAChE,MAAM,CAoBR;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqBtD;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,EACE,MAAc,EACd,MAAuB,GACxB,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAA;CAAO,GACxD,MAAM,CAIR"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * cli-color.ts — shared ANSI styling for recipe CLI human output.
3
+ *
4
+ * Honors NO_COLOR, RECIPE_NO_COLOR, FORCE_COLOR, RECIPE_COLOR, and TTY detection.
5
+ */
6
+ const STYLES = {
7
+ reset: '\x1b[0m',
8
+ bold: '\x1b[1m',
9
+ dim: '\x1b[2m',
10
+ label: '\x1b[1;36m',
11
+ cmd: '\x1b[1m',
12
+ ok: '\x1b[1;32m',
13
+ warn: '\x1b[1;33m',
14
+ err: '\x1b[1;31m',
15
+ info: '\x1b[0;37m',
16
+ accent: '\x1b[1;35m',
17
+ path: '\x1b[2m',
18
+ comment: '\x1b[2m',
19
+ active: '\x1b[1m',
20
+ };
21
+ export function colorEnabled(stream = process.stderr) {
22
+ if (process.env.NO_COLOR != null && process.env.NO_COLOR !== '' && process.env.NO_COLOR !== '0') {
23
+ return false;
24
+ }
25
+ if (process.env.RECIPE_NO_COLOR === '1')
26
+ return false;
27
+ if (process.env.RECIPE_COLOR === '1' || process.env.FORCE_COLOR === '1')
28
+ return true;
29
+ return Boolean(stream?.isTTY);
30
+ }
31
+ export function stripAnsi(text) {
32
+ return String(text).replace(/\x1b\[[0-9;]*m/gu, '');
33
+ }
34
+ export function color(style, text, { stream = process.stderr } = {}) {
35
+ const value = String(text);
36
+ if (!colorEnabled(stream))
37
+ return value;
38
+ const code = STYLES[style];
39
+ if (!code)
40
+ return value;
41
+ return `${code}${value}${STYLES.reset}`;
42
+ }
43
+ export function colorKv(label, value, valueStyle = 'info', { stream = process.stderr } = {}) {
44
+ return `${color('label', `${label}:`, { stream })} ${color(valueStyle, value, { stream })}`;
45
+ }
46
+ export function colorStatusWord(word, { stream = process.stderr } = {}) {
47
+ const normalized = String(word || '').toLowerCase();
48
+ if (['up', 'ready', 'yes', 'pass', 'running', 'present', 'healthy'].includes(normalized)) {
49
+ return color('ok', word, { stream });
50
+ }
51
+ if (['down', 'stopped', 'no', 'fail', 'failed', 'missing', 'blocked', 'unknown'].includes(normalized)) {
52
+ return color('err', word, { stream });
53
+ }
54
+ if (['compiling', 'bundling', 'starting', 'waiting', 'warn'].includes(normalized)) {
55
+ return color('warn', word, { stream });
56
+ }
57
+ return color('info', word, { stream });
58
+ }
59
+ export function colorHumanMessage(message, { stream = process.stderr } = {}) {
60
+ return String(message)
61
+ .split('\n')
62
+ .map((line) => {
63
+ const marker = /^(\s*)(→|✓|✗)\s?(.*)$/u.exec(line);
64
+ if (marker) {
65
+ const style = marker[2] === '✓' ? 'ok' : marker[2] === '✗' ? 'err' : 'accent';
66
+ return `${marker[1]}${color(style, marker[2], { stream })}${marker[3] ? ` ${marker[3]}` : ''}`;
67
+ }
68
+ const next = /^(\s*)Next:\s*(.*)$/u.exec(line);
69
+ if (next) {
70
+ return `${next[1]}${color('label', 'Next:', { stream })}${next[2] ? ` ${color('cmd', next[2], { stream })}` : ''}`;
71
+ }
72
+ const warning = /^(\s*)(WARN(?:ING)?)(:?)[ \t]*(.*)$/iu.exec(line);
73
+ if (warning) {
74
+ return `${warning[1]}${color('warn', `${warning[2]}${warning[3]}`, { stream })}${warning[4] ? ` ${warning[4]}` : ''}`;
75
+ }
76
+ return line;
77
+ })
78
+ .join('\n');
79
+ }
80
+ export function classifyLogEvent(event) {
81
+ const text = String(event || '');
82
+ if (/Module build failed|^ERROR in |BUILD FAILED|compiled with [1-9][0-9]* error/iu.test(text)) {
83
+ return 'err';
84
+ }
85
+ if (/compiled successfully|compiled with [0-9]+ warning|PASS runtime-launch|launch pass|verify pass|build complete/iu.test(text)) {
86
+ return 'ok';
87
+ }
88
+ if (/phase [0-9]\/[0-9]|prepare pipeline|snapshot|fixture:|dist-freshness|build-health/iu.test(text)) {
89
+ return 'label';
90
+ }
91
+ if (/webpack [0-9]+%|Bundl(ed|ing)|(iOS|Android).*%/iu.test(text)) {
92
+ return 'warn';
93
+ }
94
+ return 'info';
95
+ }
96
+ export function colorLogEvent(event, { latest = false, stream = process.stderr, } = {}) {
97
+ const style = classifyLogEvent(event);
98
+ const painted = color(style === 'label' ? 'label' : style, event, { stream });
99
+ return latest ? color('active', painted, { stream }) : painted;
100
+ }
101
+ //# sourceMappingURL=cli-color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-color.js","sourceRoot":"","sources":["../../src/harness/cli-color.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAiBH,MAAM,MAAM,GAA8B;IACxC,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,SAAS;IACf,GAAG,EAAE,SAAS;IACd,KAAK,EAAE,YAAY;IACnB,GAAG,EAAE,SAAS;IACd,EAAE,EAAE,YAAY;IAChB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,YAAY;IACjB,IAAI,EAAE,YAAY;IAClB,MAAM,EAAE,YAAY;IACpB,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,SAAS;CAClB,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,SAA6B,OAAO,CAAC,MAAM;IACtE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;QAChG,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IACtD,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACrF,OAAO,OAAO,CAAE,MAAmD,EAAE,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,KAAK,CACnB,KAAa,EACb,IAAY,EACZ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,KAAsC,EAAE;IAEjE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAkB,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,OAAO,GAAG,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,KAAa,EACb,KAAa,EACb,UAAU,GAAG,MAAM,EACnB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,KAAsC,EAAE;IAEjE,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,KAAK,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,IAAI,KAAK,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AAC9F,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,KAAsC,EAAE;IAEjE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACpD,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACzF,OAAO,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,IACE,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CACnF,UAAU,CACX,EACD,CAAC;QACD,OAAO,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAClF,OAAO,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,OAAe,EACf,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,KAAsC,EAAE;IAEjE,OAAO,MAAM,CAAC,OAAO,CAAC;SACnB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,MAAM,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC9E,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACjG,CAAC;QACD,MAAM,IAAI,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACrH,CAAC;QACD,MAAM,OAAO,GAAG,uCAAuC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACxH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACjC,IAAI,+EAA+E,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/F,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IACE,iHAAiH,CAAC,IAAI,CACpH,IAAI,CACL,EACD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IACE,qFAAqF,CAAC,IAAI,CAAC,IAAI,CAAC,EAChG,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,kDAAkD,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAClE,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,KAAa,EACb,EACE,MAAM,GAAG,KAAK,EACd,MAAM,GAAG,OAAO,CAAC,MAAM,MAC8B,EAAE;IAEzD,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9E,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AACjE,CAAC"}
@@ -0,0 +1,25 @@
1
+ export declare const COMMAND_JOURNAL_FILE = "last-command.json";
2
+ export interface CommandJournalRecord {
3
+ schemaVersion: 1;
4
+ command: string;
5
+ args: string[];
6
+ target: string;
7
+ verdict: 'running' | 'pass' | 'fail';
8
+ exitCode: number | null;
9
+ evidencePaths: string[];
10
+ startedAt: string;
11
+ finishedAt: string | null;
12
+ }
13
+ export declare function withCommandJournal(command: string, argv: readonly string[], execute: () => Promise<number>): Promise<number>;
14
+ export declare function recordCommandStage(stage: string): void;
15
+ export declare function recordCommandOutput(chunk: string | Uint8Array): void;
16
+ export declare function recordCommandEvidence(...evidence: string[]): void;
17
+ export declare function commandJournalPath(target: string, runtimeDir?: string): string;
18
+ export declare function readCommandJournal(target: string, runtimeDir?: string): {
19
+ file: string;
20
+ record: CommandJournalRecord | null;
21
+ };
22
+ export declare function redactCommandArgs(argv: readonly string[]): string[];
23
+ export declare function isSensitiveKey(key: string): boolean;
24
+ export declare function redactStructuredValue(value: unknown): unknown;
25
+ //# sourceMappingURL=command-journal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-journal.d.ts","sourceRoot":"","sources":["../../src/harness/command-journal.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AAQxD,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,CAAC,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IACrC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAUD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAC7B,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEtD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAEpE;AAED,wBAAgB,qBAAqB,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAajE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAI9E;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACrC,CA4CA;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAmCnE;AAiKD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAS7D"}