@dreamboard-games/cli 0.1.30-alpha.2 → 0.1.30-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-verifier/agent-workspace-verifier.mjs +227 -0
- package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -0
- package/dist/agent-verifier/chunk-27EEIZCI.mjs +185 -0
- package/dist/agent-verifier/chunk-27EEIZCI.mjs.map +1 -0
- package/dist/agent-verifier/chunk-5NYBTZB4.mjs +226 -0
- package/dist/agent-verifier/chunk-5NYBTZB4.mjs.map +1 -0
- package/dist/agent-verifier/chunk-776W3UGV.mjs +167 -0
- package/dist/agent-verifier/chunk-776W3UGV.mjs.map +1 -0
- package/dist/agent-verifier/chunk-C3VW3DTA.mjs +2909 -0
- package/dist/agent-verifier/chunk-C3VW3DTA.mjs.map +1 -0
- package/dist/agent-verifier/chunk-F2DIOJJZ.mjs +302 -0
- package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +1 -0
- package/dist/agent-verifier/chunk-G42BGGG2.mjs +70 -0
- package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +1 -0
- package/dist/agent-verifier/chunk-H6XDQJ3N.mjs +11 -0
- package/dist/agent-verifier/chunk-H76MT5UR.mjs +57 -0
- package/dist/agent-verifier/chunk-H76MT5UR.mjs.map +1 -0
- package/dist/agent-verifier/chunk-IAYRNVUC.mjs +49 -0
- package/dist/agent-verifier/chunk-IAYRNVUC.mjs.map +1 -0
- package/dist/agent-verifier/chunk-IDVQXGAO.mjs +222 -0
- package/dist/agent-verifier/chunk-IDVQXGAO.mjs.map +1 -0
- package/dist/agent-verifier/chunk-JO5AMVZU.mjs +1744 -0
- package/dist/agent-verifier/chunk-JO5AMVZU.mjs.map +1 -0
- package/dist/agent-verifier/chunk-JZTH3EMV.mjs +14523 -0
- package/dist/agent-verifier/chunk-JZTH3EMV.mjs.map +1 -0
- package/dist/agent-verifier/chunk-KDBSVLCF.mjs +624 -0
- package/dist/agent-verifier/chunk-KDBSVLCF.mjs.map +1 -0
- package/dist/agent-verifier/chunk-MW2QIWWA.mjs +729 -0
- package/dist/agent-verifier/chunk-MW2QIWWA.mjs.map +1 -0
- package/dist/agent-verifier/chunk-NAK77WXW.mjs +767 -0
- package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +1 -0
- package/dist/agent-verifier/chunk-ON62IGWK.mjs +3137 -0
- package/dist/agent-verifier/chunk-ON62IGWK.mjs.map +1 -0
- package/dist/agent-verifier/chunk-QBAF7EYR.mjs +214 -0
- package/dist/agent-verifier/chunk-QBAF7EYR.mjs.map +1 -0
- package/dist/agent-verifier/chunk-QZH6IEZS.mjs +39 -0
- package/dist/agent-verifier/chunk-QZH6IEZS.mjs.map +1 -0
- package/dist/agent-verifier/chunk-TAEQKBJB.mjs +107 -0
- package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +1 -0
- package/dist/agent-verifier/chunk-UIOLGH4A.mjs +150 -0
- package/dist/agent-verifier/chunk-UIOLGH4A.mjs.map +1 -0
- package/dist/agent-verifier/chunk-XKCJBIRY.mjs +75 -0
- package/dist/agent-verifier/chunk-XKCJBIRY.mjs.map +1 -0
- package/dist/agent-verifier/chunk-XQXDOBYB.mjs +382 -0
- package/dist/agent-verifier/chunk-XQXDOBYB.mjs.map +1 -0
- package/dist/agent-verifier/chunk-YDIOW2BO.mjs +45 -0
- package/dist/agent-verifier/chunk-YDIOW2BO.mjs.map +1 -0
- package/dist/agent-verifier/chunk-YE7UAO3T.mjs +129 -0
- package/dist/agent-verifier/chunk-YE7UAO3T.mjs.map +1 -0
- package/dist/agent-verifier/chunk-Z6OZWUIZ.mjs +261 -0
- package/dist/agent-verifier/chunk-Z6OZWUIZ.mjs.map +1 -0
- package/dist/agent-verifier/chunk-ZEELHSY3.mjs +20 -0
- package/dist/agent-verifier/chunk-ZEELHSY3.mjs.map +1 -0
- package/dist/agent-verifier/compile-576O7TYP.mjs +312 -0
- package/dist/agent-verifier/compile-576O7TYP.mjs.map +1 -0
- package/dist/agent-verifier/global-config-NYCSCAUI.mjs +18 -0
- package/dist/agent-verifier/keychain-backend-A3MRWLPF.mjs +135 -0
- package/dist/agent-verifier/keychain-backend-A3MRWLPF.mjs.map +1 -0
- package/dist/agent-verifier/local-files-QVJ2H3MH.mjs +45 -0
- package/dist/agent-verifier/local-files-QVJ2H3MH.mjs.map +1 -0
- package/dist/agent-verifier/local-typecheck-2JWG5IGL.mjs +10 -0
- package/dist/agent-verifier/local-typecheck-2JWG5IGL.mjs.map +1 -0
- package/dist/agent-verifier/materialize-workspace-OZKOQCSQ.mjs +89 -0
- package/dist/agent-verifier/materialize-workspace-OZKOQCSQ.mjs.map +1 -0
- package/dist/agent-verifier/project-state-XKUSCFSV.mjs +33 -0
- package/dist/agent-verifier/project-state-XKUSCFSV.mjs.map +1 -0
- package/dist/agent-verifier/prompt-VKHMCQT6.mjs +756 -0
- package/dist/agent-verifier/prompt-VKHMCQT6.mjs.map +1 -0
- package/dist/agent-verifier/reducer-bundle-preflight-7NYZF5ZT.mjs +20 -0
- package/dist/agent-verifier/reducer-bundle-preflight-7NYZF5ZT.mjs.map +1 -0
- package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs +11 -0
- package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs.map +1 -0
- package/dist/agent-verifier/reducer-native-test-harness-QC7HZUK4.mjs +50 -0
- package/dist/agent-verifier/reducer-native-test-harness-QC7HZUK4.mjs.map +1 -0
- package/dist/agent-verifier/static-scaffold-JBUE3ROP.mjs +27 -0
- package/dist/agent-verifier/static-scaffold-JBUE3ROP.mjs.map +1 -0
- package/dist/agent-verifier/sync-C6S3OGCD.mjs +588 -0
- package/dist/agent-verifier/sync-C6S3OGCD.mjs.map +1 -0
- package/dist/agent-verifier/test-Y5UGQV7J.mjs +353 -0
- package/dist/agent-verifier/test-Y5UGQV7J.mjs.map +1 -0
- package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs +10 -0
- package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs.map +1 -0
- package/dist/agent-verifier/workspace-dependencies-B6A2ZX55.mjs +15 -0
- package/dist/agent-verifier/workspace-dependencies-B6A2ZX55.mjs.map +1 -0
- package/dist/chunk-2H7UOFLK.js +11 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/{chunk-N7XPNNUI.js → chunk-3NRROR4P.js} +3 -3
- package/dist/{chunk-TAQKH67O.js → chunk-M4SCKH5M.js} +8 -2224
- package/dist/chunk-M4SCKH5M.js.map +1 -0
- package/dist/{global-config-S4ZIPECE.js → global-config-YBFEGJQG.js} +3 -3
- package/dist/global-config-YBFEGJQG.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/internal.js +3 -3
- package/dist/{keychain-backend-HDF4TZDL.js → keychain-backend-JHTXAKWC.js} +2 -2
- package/dist/{prompt-NDV3AE5L.js → prompt-GMZABCJC.js} +2 -2
- package/package.json +3 -2
- package/dist/chunk-SEGVTWSK.js +0 -44
- package/dist/chunk-TAQKH67O.js.map +0 -1
- /package/dist/{chunk-SEGVTWSK.js.map → agent-verifier/chunk-H6XDQJ3N.mjs.map} +0 -0
- /package/dist/{global-config-S4ZIPECE.js.map → agent-verifier/global-config-NYCSCAUI.mjs.map} +0 -0
- /package/dist/{chunk-N7XPNNUI.js.map → chunk-3NRROR4P.js.map} +0 -0
- /package/dist/{keychain-backend-HDF4TZDL.js.map → keychain-backend-JHTXAKWC.js.map} +0 -0
- /package/dist/{prompt-NDV3AE5L.js.map → prompt-GMZABCJC.js.map} +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
assertCompilerPortableDependencies,
|
|
4
|
+
consola,
|
|
5
|
+
resolveProjectContext
|
|
6
|
+
} from "./chunk-JO5AMVZU.mjs";
|
|
7
|
+
import "./chunk-5NYBTZB4.mjs";
|
|
8
|
+
import "./chunk-776W3UGV.mjs";
|
|
9
|
+
import "./chunk-NAK77WXW.mjs";
|
|
10
|
+
import "./chunk-TAEQKBJB.mjs";
|
|
11
|
+
import "./chunk-IAYRNVUC.mjs";
|
|
12
|
+
import "./chunk-H76MT5UR.mjs";
|
|
13
|
+
import "./chunk-H6XDQJ3N.mjs";
|
|
14
|
+
|
|
15
|
+
// src/agent-verifier/agent-workspace-verifier.ts
|
|
16
|
+
import { readFile } from "fs/promises";
|
|
17
|
+
main().catch((error) => {
|
|
18
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
});
|
|
21
|
+
async function main() {
|
|
22
|
+
const [command, ...args] = process.argv.slice(2);
|
|
23
|
+
if (command === "--help" || command === "help") {
|
|
24
|
+
printHelp();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (command === "materialize-prepared-workspace") {
|
|
28
|
+
if (args.includes("--help")) {
|
|
29
|
+
printHelp();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
await materializePreparedWorkspace(args);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
await verifyAgentWorkspace(command ?? "verify", args);
|
|
36
|
+
}
|
|
37
|
+
function printHelp() {
|
|
38
|
+
console.log(`Dreamboard agent workspace verifier
|
|
39
|
+
|
|
40
|
+
Usage:
|
|
41
|
+
agent-workspace-verifier materialize-prepared-workspace --input <path>
|
|
42
|
+
agent-workspace-verifier preflight [--env local|staging|prod]
|
|
43
|
+
agent-workspace-verifier verify [--env local|staging|prod]
|
|
44
|
+
agent-workspace-verifier fin [--env local|staging|prod]
|
|
45
|
+
`);
|
|
46
|
+
}
|
|
47
|
+
async function materializePreparedWorkspace(args) {
|
|
48
|
+
const inputPath = readRequiredOption(args, "--input");
|
|
49
|
+
const { materializeWorkspaceProject } = await import("./materialize-workspace-OZKOQCSQ.mjs");
|
|
50
|
+
const input = JSON.parse(
|
|
51
|
+
await readFile(inputPath, "utf8")
|
|
52
|
+
);
|
|
53
|
+
await materializeWorkspaceProject({
|
|
54
|
+
...input,
|
|
55
|
+
agentManaged: true,
|
|
56
|
+
workspacePrepared: true,
|
|
57
|
+
allowCreateGame: false,
|
|
58
|
+
installDependencies: false
|
|
59
|
+
});
|
|
60
|
+
consola.success(`Prepared workspace in ${input.targetDir}`);
|
|
61
|
+
}
|
|
62
|
+
async function verifyAgentWorkspace(rawMode, args) {
|
|
63
|
+
const requestedMode = parseVerificationMode(rawMode);
|
|
64
|
+
const parsedFlags = parseConfigArgs(args);
|
|
65
|
+
const { projectRoot, projectConfig, config } = await resolveProjectContext(parsedFlags);
|
|
66
|
+
await assertCompilerPortableDependencies({ projectRoot, projectConfig });
|
|
67
|
+
if (requestedMode === "preflight") {
|
|
68
|
+
consola.success("Agent workspace preflight passed.");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (process.env.DREAMBOARD_AGENT_FINAL_SYNC_VERIFY === "1") {
|
|
72
|
+
await runFullBackendConnectedVerification(parsedFlags);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (requestedMode === "cloud-local" || requestedMode === "verify" || requestedMode === "fin") {
|
|
76
|
+
await runCloudLocalVerification(projectRoot, projectConfig, config);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
throw new Error(
|
|
80
|
+
"Agent workspaces now run cloud-local verification directly. Use `fin` only through the generated wrapper."
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
async function runFullBackendConnectedVerification(parsedFlags) {
|
|
84
|
+
const [{ default: cmdSync }, { default: cmdCompile }, { default: cmdTest }] = await Promise.all([
|
|
85
|
+
import("./sync-C6S3OGCD.mjs"),
|
|
86
|
+
import("./compile-576O7TYP.mjs"),
|
|
87
|
+
import("./test-Y5UGQV7J.mjs")
|
|
88
|
+
]);
|
|
89
|
+
await runCommandDefinition(cmdSync, { ...parsedFlags, force: true });
|
|
90
|
+
await runCommandDefinition(cmdCompile, parsedFlags);
|
|
91
|
+
await runCommandDefinition(
|
|
92
|
+
resolveTestSubCommand(cmdTest, "generate"),
|
|
93
|
+
parsedFlags
|
|
94
|
+
);
|
|
95
|
+
await runCommandDefinition(resolveTestSubCommand(cmdTest, "run"), parsedFlags);
|
|
96
|
+
}
|
|
97
|
+
async function runCommandDefinition(command, args) {
|
|
98
|
+
if (!command.run) {
|
|
99
|
+
throw new Error("Verifier command is missing a runnable step.");
|
|
100
|
+
}
|
|
101
|
+
await command.run({ args, rawArgs: [], cmd: command });
|
|
102
|
+
}
|
|
103
|
+
function resolveTestSubCommand(cmdTest, name) {
|
|
104
|
+
const subCommands = cmdTest.subCommands;
|
|
105
|
+
const command = subCommands?.[name];
|
|
106
|
+
if (!command) {
|
|
107
|
+
throw new Error(`Verifier command is missing test ${name}.`);
|
|
108
|
+
}
|
|
109
|
+
return command;
|
|
110
|
+
}
|
|
111
|
+
function parseVerificationMode(value) {
|
|
112
|
+
if (value === "preflight" || value === "verify" || value === "fin" || value === "cloud-local") {
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Expected mode to be one of preflight, verify, fin, or cloud-local. Received: ${String(value)}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
function parseConfigArgs(args) {
|
|
120
|
+
const flags = {};
|
|
121
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
122
|
+
const arg = args[index];
|
|
123
|
+
if (arg === "--env") {
|
|
124
|
+
const value = args[++index];
|
|
125
|
+
if (value === "local" || value === "staging" || value === "prod") {
|
|
126
|
+
flags.env = value;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
throw new Error(`Invalid --env value: ${String(value)}`);
|
|
130
|
+
}
|
|
131
|
+
if (arg === "--token") {
|
|
132
|
+
flags.token = args[++index];
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (arg === "--skip-install") {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
throw new Error(`Unknown verifier argument: ${arg}`);
|
|
139
|
+
}
|
|
140
|
+
return flags;
|
|
141
|
+
}
|
|
142
|
+
async function runCloudLocalVerification(projectRoot, projectConfig, config) {
|
|
143
|
+
const [
|
|
144
|
+
{ scaffoldStaticWorkspace },
|
|
145
|
+
{ loadManifest },
|
|
146
|
+
{ applyWorkspaceCodegen },
|
|
147
|
+
{ reconcileWorkspaceDependencies },
|
|
148
|
+
{ assertReducerContractPreflight },
|
|
149
|
+
{ getProjectLocalMaintainerRegistry }
|
|
150
|
+
] = await Promise.all([
|
|
151
|
+
import("./static-scaffold-JBUE3ROP.mjs"),
|
|
152
|
+
import("./local-files-QVJ2H3MH.mjs"),
|
|
153
|
+
import("./workspace-codegen-WPZHMATU.mjs"),
|
|
154
|
+
import("./workspace-dependencies-B6A2ZX55.mjs"),
|
|
155
|
+
import("./reducer-contract-preflight-COD2CO22.mjs"),
|
|
156
|
+
import("./project-state-XKUSCFSV.mjs")
|
|
157
|
+
]);
|
|
158
|
+
consola.start("Refreshing static scaffold...");
|
|
159
|
+
await scaffoldStaticWorkspace(projectRoot, "update", {
|
|
160
|
+
localMaintainerRegistry: getProjectLocalMaintainerRegistry(projectConfig)
|
|
161
|
+
});
|
|
162
|
+
consola.start("Applying workspace codegen...");
|
|
163
|
+
const manifest = await loadManifest(projectRoot);
|
|
164
|
+
await applyWorkspaceCodegen({ projectRoot, manifest });
|
|
165
|
+
consola.start("Reconciling workspace dependencies...");
|
|
166
|
+
await reconcileWorkspaceDependencies(projectRoot);
|
|
167
|
+
consola.start("Validating reducer contract...");
|
|
168
|
+
await assertReducerContractPreflight(projectRoot);
|
|
169
|
+
const [{ runLocalTypecheck }, { assertReducerBundleSmoke }] = await Promise.all([
|
|
170
|
+
import("./local-typecheck-2JWG5IGL.mjs"),
|
|
171
|
+
import("./reducer-bundle-preflight-7NYZF5ZT.mjs")
|
|
172
|
+
]);
|
|
173
|
+
consola.start("Running local typecheck...");
|
|
174
|
+
const typecheckResult = await runLocalTypecheck(projectRoot);
|
|
175
|
+
if (typecheckResult.skipped) {
|
|
176
|
+
if (typecheckResult.output) consola.warn(typecheckResult.output);
|
|
177
|
+
} else if (!typecheckResult.success) {
|
|
178
|
+
if (typecheckResult.output) consola.error(typecheckResult.output);
|
|
179
|
+
throw new Error(
|
|
180
|
+
"Local typecheck failed. Fix the diagnostics before syncing."
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
consola.start("Smoke-testing reducer bundle...");
|
|
184
|
+
await assertReducerBundleSmoke({ projectRoot, manifest });
|
|
185
|
+
const {
|
|
186
|
+
generateReducerNativeArtifacts,
|
|
187
|
+
isReducerNativeTestingWorkspace,
|
|
188
|
+
runReducerNativeScenarios
|
|
189
|
+
} = await import("./reducer-native-test-harness-QC7HZUK4.mjs");
|
|
190
|
+
if (await isReducerNativeTestingWorkspace(projectRoot)) {
|
|
191
|
+
const { bases } = await generateReducerNativeArtifacts({
|
|
192
|
+
projectRoot,
|
|
193
|
+
gameId: projectConfig.gameId,
|
|
194
|
+
compiledResultId: projectConfig.compile?.latestSuccessful?.resultId
|
|
195
|
+
});
|
|
196
|
+
const summary = await runReducerNativeScenarios({
|
|
197
|
+
projectRoot,
|
|
198
|
+
projectConfig,
|
|
199
|
+
resolvedConfig: config,
|
|
200
|
+
runner: "reducer",
|
|
201
|
+
gameId: projectConfig.gameId,
|
|
202
|
+
compiledResultId: projectConfig.compile?.latestSuccessful?.resultId
|
|
203
|
+
});
|
|
204
|
+
if (summary.failed > 0) {
|
|
205
|
+
const failures = summary.results.filter((result) => !result.success).map(
|
|
206
|
+
(result) => result.error ? `FAIL ${result.id}: ${result.error}` : `FAIL ${result.id}`
|
|
207
|
+
);
|
|
208
|
+
throw new Error(
|
|
209
|
+
[
|
|
210
|
+
`Reducer-native verification failed: ${summary.failed} failed, ${summary.passed} passed.`,
|
|
211
|
+
...failures
|
|
212
|
+
].join("\n")
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
consola.success(`Generated ${bases.length} reducer-native base state(s).`);
|
|
216
|
+
}
|
|
217
|
+
consola.success("Agent workspace cloud-local verification passed.");
|
|
218
|
+
}
|
|
219
|
+
function readRequiredOption(args, name) {
|
|
220
|
+
const index = args.indexOf(name);
|
|
221
|
+
const value = index >= 0 ? args[index + 1] : void 0;
|
|
222
|
+
if (!value) {
|
|
223
|
+
throw new Error(`${name} is required.`);
|
|
224
|
+
}
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=agent-workspace-verifier.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/agent-verifier/agent-workspace-verifier.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport consola from \"consola\";\nimport type { CommandDef } from \"citty\";\nimport type { ConfigFlags } from \"../flags.js\";\nimport type { ProjectConfig, ResolvedConfig } from \"../types.js\";\nimport { resolveProjectContext } from \"../config/resolve.js\";\nimport { assertCompilerPortableDependencies } from \"../services/project/dependency-portability.js\";\n\ntype VerificationMode = \"preflight\" | \"verify\" | \"fin\" | \"cloud-local\";\n\nmain().catch((error) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n});\n\nasync function main() {\n const [command, ...args] = process.argv.slice(2);\n if (command === \"--help\" || command === \"help\") {\n printHelp();\n return;\n }\n if (command === \"materialize-prepared-workspace\") {\n if (args.includes(\"--help\")) {\n printHelp();\n return;\n }\n await materializePreparedWorkspace(args);\n return;\n }\n await verifyAgentWorkspace(command ?? \"verify\", args);\n}\n\nfunction printHelp() {\n console.log(`Dreamboard agent workspace verifier\n\nUsage:\n agent-workspace-verifier materialize-prepared-workspace --input <path>\n agent-workspace-verifier preflight [--env local|staging|prod]\n agent-workspace-verifier verify [--env local|staging|prod]\n agent-workspace-verifier fin [--env local|staging|prod]\n`);\n}\n\nasync function materializePreparedWorkspace(args: string[]) {\n const inputPath = readRequiredOption(args, \"--input\");\n const { materializeWorkspaceProject } = await import(\n \"../services/project/materialize-workspace.js\"\n );\n const input = JSON.parse(\n await readFile(inputPath, \"utf8\"),\n ) as Parameters<typeof materializeWorkspaceProject>[0];\n await materializeWorkspaceProject({\n ...input,\n agentManaged: true,\n workspacePrepared: true,\n allowCreateGame: false,\n installDependencies: false,\n });\n consola.success(`Prepared workspace in ${input.targetDir}`);\n}\n\nasync function verifyAgentWorkspace(rawMode: string, args: string[]) {\n const requestedMode = parseVerificationMode(rawMode);\n const parsedFlags = parseConfigArgs(args);\n const { projectRoot, projectConfig, config } =\n await resolveProjectContext(parsedFlags);\n await assertCompilerPortableDependencies({ projectRoot, projectConfig });\n\n if (requestedMode === \"preflight\") {\n consola.success(\"Agent workspace preflight passed.\");\n return;\n }\n\n if (process.env.DREAMBOARD_AGENT_FINAL_SYNC_VERIFY === \"1\") {\n await runFullBackendConnectedVerification(parsedFlags);\n return;\n }\n\n if (\n requestedMode === \"cloud-local\" ||\n requestedMode === \"verify\" ||\n requestedMode === \"fin\"\n ) {\n await runCloudLocalVerification(projectRoot, projectConfig, config);\n return;\n }\n\n throw new Error(\n \"Agent workspaces now run cloud-local verification directly. Use `fin` only through the generated wrapper.\",\n );\n}\n\nasync function runFullBackendConnectedVerification(\n parsedFlags: ConfigFlags,\n): Promise<void> {\n const [{ default: cmdSync }, { default: cmdCompile }, { default: cmdTest }] =\n await Promise.all([\n import(\"../commands/sync.js\"),\n import(\"../commands/compile.js\"),\n import(\"../commands/test.js\"),\n ]);\n await runCommandDefinition(cmdSync, { ...parsedFlags, force: true });\n await runCommandDefinition(cmdCompile, parsedFlags);\n await runCommandDefinition(\n resolveTestSubCommand(cmdTest, \"generate\"),\n parsedFlags,\n );\n await runCommandDefinition(resolveTestSubCommand(cmdTest, \"run\"), parsedFlags);\n}\n\nasync function runCommandDefinition(\n command: CommandDef<any>,\n args: Record<string, unknown>,\n) {\n if (!command.run) {\n throw new Error(\"Verifier command is missing a runnable step.\");\n }\n await command.run({ args, rawArgs: [], cmd: command } as any);\n}\n\nfunction resolveTestSubCommand(\n cmdTest: CommandDef<any>,\n name: \"generate\" | \"run\",\n): CommandDef<any> {\n const subCommands = cmdTest.subCommands as\n | Record<string, CommandDef<any>>\n | undefined;\n const command = subCommands?.[name];\n if (!command) {\n throw new Error(`Verifier command is missing test ${name}.`);\n }\n return command;\n}\n\nfunction parseVerificationMode(value: unknown): VerificationMode {\n if (\n value === \"preflight\" ||\n value === \"verify\" ||\n value === \"fin\" ||\n value === \"cloud-local\"\n ) {\n return value;\n }\n throw new Error(\n `Expected mode to be one of preflight, verify, fin, or cloud-local. Received: ${String(value)}`,\n );\n}\n\nfunction parseConfigArgs(args: string[]): ConfigFlags {\n const flags: ConfigFlags = {};\n for (let index = 0; index < args.length; index += 1) {\n const arg = args[index];\n if (arg === \"--env\") {\n const value = args[++index];\n if (value === \"local\" || value === \"staging\" || value === \"prod\") {\n flags.env = value;\n continue;\n }\n throw new Error(`Invalid --env value: ${String(value)}`);\n }\n if (arg === \"--token\") {\n flags.token = args[++index];\n continue;\n }\n if (arg === \"--skip-install\") {\n continue;\n }\n throw new Error(`Unknown verifier argument: ${arg}`);\n }\n return flags;\n}\n\nasync function runCloudLocalVerification(\n projectRoot: string,\n projectConfig: ProjectConfig,\n config: ResolvedConfig,\n): Promise<void> {\n const [\n { scaffoldStaticWorkspace },\n { loadManifest },\n { applyWorkspaceCodegen },\n { reconcileWorkspaceDependencies },\n { assertReducerContractPreflight },\n { getProjectLocalMaintainerRegistry },\n ] = await Promise.all([\n import(\"../services/project/static-scaffold.js\"),\n import(\"../services/project/local-files.js\"),\n import(\"../services/project/workspace-codegen.js\"),\n import(\"../services/project/workspace-dependencies.js\"),\n import(\"../services/project/reducer-contract-preflight.js\"),\n import(\"../services/project/project-state.js\"),\n ]);\n\n consola.start(\"Refreshing static scaffold...\");\n await scaffoldStaticWorkspace(projectRoot, \"update\", {\n localMaintainerRegistry: getProjectLocalMaintainerRegistry(projectConfig),\n });\n\n consola.start(\"Applying workspace codegen...\");\n const manifest = await loadManifest(projectRoot);\n await applyWorkspaceCodegen({ projectRoot, manifest });\n\n consola.start(\"Reconciling workspace dependencies...\");\n await reconcileWorkspaceDependencies(projectRoot);\n\n consola.start(\"Validating reducer contract...\");\n await assertReducerContractPreflight(projectRoot);\n\n const [{ runLocalTypecheck }, { assertReducerBundleSmoke }] =\n await Promise.all([\n import(\"../services/project/local-typecheck.js\"),\n import(\"../services/project/reducer-bundle-preflight.js\"),\n ]);\n\n consola.start(\"Running local typecheck...\");\n const typecheckResult = await runLocalTypecheck(projectRoot);\n if (typecheckResult.skipped) {\n if (typecheckResult.output) consola.warn(typecheckResult.output);\n } else if (!typecheckResult.success) {\n if (typecheckResult.output) consola.error(typecheckResult.output);\n throw new Error(\n \"Local typecheck failed. Fix the diagnostics before syncing.\",\n );\n }\n\n consola.start(\"Smoke-testing reducer bundle...\");\n await assertReducerBundleSmoke({ projectRoot, manifest });\n\n const {\n generateReducerNativeArtifacts,\n isReducerNativeTestingWorkspace,\n runReducerNativeScenarios,\n } = await import(\n \"../services/testing/reducer-native-test-harness.js\"\n );\n\n if (await isReducerNativeTestingWorkspace(projectRoot)) {\n const { bases } = await generateReducerNativeArtifacts({\n projectRoot,\n gameId: projectConfig.gameId,\n compiledResultId: projectConfig.compile?.latestSuccessful?.resultId,\n });\n const summary = await runReducerNativeScenarios({\n projectRoot,\n projectConfig,\n resolvedConfig: config,\n runner: \"reducer\",\n gameId: projectConfig.gameId,\n compiledResultId: projectConfig.compile?.latestSuccessful?.resultId,\n });\n if (summary.failed > 0) {\n const failures = summary.results\n .filter((result) => !result.success)\n .map((result) =>\n result.error\n ? `FAIL ${result.id}: ${result.error}`\n : `FAIL ${result.id}`,\n );\n throw new Error(\n [\n `Reducer-native verification failed: ${summary.failed} failed, ${summary.passed} passed.`,\n ...failures,\n ].join(\"\\n\"),\n );\n }\n consola.success(`Generated ${bases.length} reducer-native base state(s).`);\n }\n\n consola.success(\"Agent workspace cloud-local verification passed.\");\n}\n\nfunction readRequiredOption(args: string[], name: string): string {\n const index = args.indexOf(name);\n const value = index >= 0 ? args[index + 1] : undefined;\n if (!value) {\n throw new Error(`${name} is required.`);\n }\n return value;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
hashContent,
|
|
4
|
+
isAllowedGamePath,
|
|
5
|
+
isDynamicGeneratedPath,
|
|
6
|
+
isLibraryPath,
|
|
7
|
+
materializeManifest,
|
|
8
|
+
writeManifestSource
|
|
9
|
+
} from "./chunk-C3VW3DTA.mjs";
|
|
10
|
+
import {
|
|
11
|
+
atomicWriteFile
|
|
12
|
+
} from "./chunk-TAEQKBJB.mjs";
|
|
13
|
+
import {
|
|
14
|
+
readJsonFile,
|
|
15
|
+
readTextFile,
|
|
16
|
+
readTextFileIfExists,
|
|
17
|
+
writeTextFile
|
|
18
|
+
} from "./chunk-IAYRNVUC.mjs";
|
|
19
|
+
import {
|
|
20
|
+
LOCAL_IGNORE_DIRS,
|
|
21
|
+
MANIFEST_FILE,
|
|
22
|
+
PROJECT_DIR_NAME,
|
|
23
|
+
RULE_FILE,
|
|
24
|
+
SNAPSHOT_FILE
|
|
25
|
+
} from "./chunk-H76MT5UR.mjs";
|
|
26
|
+
|
|
27
|
+
// src/services/project/local-files.ts
|
|
28
|
+
import { readdir, unlink } from "fs/promises";
|
|
29
|
+
import path from "path";
|
|
30
|
+
function isAllowedGamePath2(filePath) {
|
|
31
|
+
return isAllowedGamePath(filePath);
|
|
32
|
+
}
|
|
33
|
+
function isLibraryPath2(filePath) {
|
|
34
|
+
return isLibraryPath(filePath);
|
|
35
|
+
}
|
|
36
|
+
function shouldWriteScaffoldFile(filePath, existingContent) {
|
|
37
|
+
if (isLibraryPath2(filePath)) return "write";
|
|
38
|
+
const hasContent = existingContent !== null && existingContent.trim().length > 0;
|
|
39
|
+
return hasContent ? "skip" : "write";
|
|
40
|
+
}
|
|
41
|
+
async function writeSourceFiles(rootDir, files) {
|
|
42
|
+
for (const [relativePath, content] of Object.entries(files)) {
|
|
43
|
+
if (content === null || content === void 0) continue;
|
|
44
|
+
const filePath = path.join(rootDir, relativePath);
|
|
45
|
+
await writeTextFile(filePath, content);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function writeScaffoldFiles(rootDir, files) {
|
|
49
|
+
const written = [];
|
|
50
|
+
const skipped = [];
|
|
51
|
+
for (const [relativePath, content] of Object.entries(files)) {
|
|
52
|
+
if (content === null || content === void 0) continue;
|
|
53
|
+
const fullPath = path.join(rootDir, relativePath);
|
|
54
|
+
const existingContent = await readTextFileIfExists(fullPath);
|
|
55
|
+
const decision = shouldWriteScaffoldFile(relativePath, existingContent);
|
|
56
|
+
if (decision === "skip") {
|
|
57
|
+
skipped.push(relativePath);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
await writeTextFile(fullPath, content);
|
|
61
|
+
if (existingContent !== content) {
|
|
62
|
+
written.push(relativePath);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
written.sort();
|
|
66
|
+
skipped.sort();
|
|
67
|
+
return { written, skipped };
|
|
68
|
+
}
|
|
69
|
+
async function removeExtraneousFiles(rootDir, keep) {
|
|
70
|
+
const localFiles = await collectLocalFiles(rootDir);
|
|
71
|
+
for (const filePath of Object.keys(localFiles)) {
|
|
72
|
+
if (filePath === MANIFEST_FILE || filePath === RULE_FILE) continue;
|
|
73
|
+
if (!keep.has(filePath)) {
|
|
74
|
+
await unlink(path.join(rootDir, filePath));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function collectLocalFiles(rootDir) {
|
|
79
|
+
const result = {};
|
|
80
|
+
await walkDir(rootDir, rootDir, result);
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
async function walkDir(rootDir, currentDir, result) {
|
|
84
|
+
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
if (entry.isDirectory()) {
|
|
87
|
+
if (LOCAL_IGNORE_DIRS.has(entry.name)) continue;
|
|
88
|
+
await walkDir(rootDir, path.join(currentDir, entry.name), result);
|
|
89
|
+
} else if (entry.isFile()) {
|
|
90
|
+
const filePath = path.join(currentDir, entry.name);
|
|
91
|
+
const relativePath = path.relative(rootDir, filePath);
|
|
92
|
+
result[relativePath] = await readTextFile(filePath);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function writeManifest(rootDir, manifest) {
|
|
97
|
+
await writeManifestSource(rootDir, manifest);
|
|
98
|
+
}
|
|
99
|
+
async function writeRule(rootDir, ruleText) {
|
|
100
|
+
const filePath = path.join(rootDir, RULE_FILE);
|
|
101
|
+
await writeTextFile(filePath, ruleText);
|
|
102
|
+
}
|
|
103
|
+
async function loadManifest(rootDir) {
|
|
104
|
+
return materializeManifest(rootDir);
|
|
105
|
+
}
|
|
106
|
+
async function loadRule(rootDir) {
|
|
107
|
+
const filePath = path.join(rootDir, RULE_FILE);
|
|
108
|
+
return readTextFile(filePath);
|
|
109
|
+
}
|
|
110
|
+
async function writeSnapshot(rootDir) {
|
|
111
|
+
const files = await collectLocalFiles(rootDir);
|
|
112
|
+
await writeSnapshotFromFiles(rootDir, files);
|
|
113
|
+
}
|
|
114
|
+
async function writeSnapshotFromFiles(rootDir, files) {
|
|
115
|
+
const snapshot = {
|
|
116
|
+
files: {}
|
|
117
|
+
};
|
|
118
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
119
|
+
if (filePath.startsWith(`${PROJECT_DIR_NAME}/`)) continue;
|
|
120
|
+
if (isDynamicGeneratedPath(filePath)) continue;
|
|
121
|
+
snapshot.files[filePath] = hashContent(content);
|
|
122
|
+
}
|
|
123
|
+
const snapshotPath = path.join(rootDir, PROJECT_DIR_NAME, SNAPSHOT_FILE);
|
|
124
|
+
await atomicWriteFile(
|
|
125
|
+
snapshotPath,
|
|
126
|
+
`${JSON.stringify(snapshot, null, 2)}
|
|
127
|
+
`,
|
|
128
|
+
{ mode: 420 }
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
function isIgnorableLocalDiffPath(filePath) {
|
|
132
|
+
return filePath.startsWith("test/generated/") || filePath.startsWith(".playwright-cli/");
|
|
133
|
+
}
|
|
134
|
+
async function getLocalDiff(rootDir) {
|
|
135
|
+
const snapshotPath = path.join(rootDir, PROJECT_DIR_NAME, SNAPSHOT_FILE);
|
|
136
|
+
const snapshot = await readJsonFile(snapshotPath).catch(() => null);
|
|
137
|
+
if (!snapshot) {
|
|
138
|
+
return { modified: [], added: [], deleted: [] };
|
|
139
|
+
}
|
|
140
|
+
const files = await collectLocalFiles(rootDir);
|
|
141
|
+
const currentHashes = {};
|
|
142
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
143
|
+
if (filePath.startsWith(`${PROJECT_DIR_NAME}/`)) continue;
|
|
144
|
+
if (isIgnorableLocalDiffPath(filePath)) continue;
|
|
145
|
+
if (isDynamicGeneratedPath(filePath)) continue;
|
|
146
|
+
currentHashes[filePath] = hashContent(content);
|
|
147
|
+
}
|
|
148
|
+
const modified = [];
|
|
149
|
+
const added = [];
|
|
150
|
+
const deleted = [];
|
|
151
|
+
for (const [filePath, hash] of Object.entries(currentHashes)) {
|
|
152
|
+
const prevHash = snapshot.files[filePath];
|
|
153
|
+
if (!prevHash) {
|
|
154
|
+
added.push(filePath);
|
|
155
|
+
} else if (prevHash !== hash) {
|
|
156
|
+
modified.push(filePath);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (const filePath of Object.keys(snapshot.files)) {
|
|
160
|
+
if (isIgnorableLocalDiffPath(filePath)) continue;
|
|
161
|
+
if (isDynamicGeneratedPath(filePath)) continue;
|
|
162
|
+
if (!currentHashes[filePath]) {
|
|
163
|
+
deleted.push(filePath);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { modified, added, deleted };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
isAllowedGamePath2 as isAllowedGamePath,
|
|
171
|
+
isLibraryPath2 as isLibraryPath,
|
|
172
|
+
writeSourceFiles,
|
|
173
|
+
writeScaffoldFiles,
|
|
174
|
+
removeExtraneousFiles,
|
|
175
|
+
collectLocalFiles,
|
|
176
|
+
walkDir,
|
|
177
|
+
writeManifest,
|
|
178
|
+
writeRule,
|
|
179
|
+
loadManifest,
|
|
180
|
+
loadRule,
|
|
181
|
+
writeSnapshot,
|
|
182
|
+
writeSnapshotFromFiles,
|
|
183
|
+
getLocalDiff
|
|
184
|
+
};
|
|
185
|
+
//# sourceMappingURL=chunk-27EEIZCI.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/project/local-files.ts"],"sourcesContent":["import { readdir, unlink } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { GameTopologyManifest } from \"@dreamboard-games/sdk/types\";\nimport {\n PROJECT_DIR_NAME,\n MANIFEST_FILE,\n RULE_FILE,\n SNAPSHOT_FILE,\n LOCAL_IGNORE_DIRS,\n} from \"../../constants.js\";\nimport type { Snapshot } from \"../../types.js\";\nimport {\n readJsonFile,\n readTextFile,\n readTextFileIfExists,\n writeTextFile,\n} from \"../../utils/fs.js\";\nimport { atomicWriteFile } from \"../../utils/atomic-file.js\";\nimport { hashContent } from \"../../utils/crypto.js\";\nimport {\n computeManifestHash,\n materializeManifest,\n writeManifestSource,\n} from \"./manifest-authoring.js\";\nimport {\n isAllowedGamePath as isAllowedPathFromOwnership,\n isDynamicGeneratedPath,\n isLibraryPath as isLibraryPathFromOwnership,\n} from \"./scaffold-ownership.js\";\n\n/**\n * Returns true when a path is inside the canonical game project structure.\n * Anything outside app/, ui/, shared/, or the known root files is rejected.\n */\nexport function isAllowedGamePath(filePath: string): boolean {\n return isAllowedPathFromOwnership(filePath);\n}\n\nexport function isLibraryPath(filePath: string): boolean {\n return isLibraryPathFromOwnership(filePath);\n}\n\ntype WriteDecision = \"write\" | \"skip\";\n\n/**\n * Decide whether a file from the scaffold response should be written to disk.\n *\n * - Library files → always overwrite (they are fully generated).\n * - Everything else → only write if the file does not yet exist or is empty\n * locally (first-time seeding; preserves user edits).\n */\nfunction shouldWriteScaffoldFile(\n filePath: string,\n existingContent: string | null,\n): WriteDecision {\n if (isLibraryPath(filePath)) return \"write\";\n const hasContent =\n existingContent !== null && existingContent.trim().length > 0;\n return hasContent ? \"skip\" : \"write\";\n}\n\nexport async function writeSourceFiles(\n rootDir: string,\n files: Record<string, string | null>,\n): Promise<void> {\n for (const [relativePath, content] of Object.entries(files)) {\n if (content === null || content === undefined) continue;\n const filePath = path.join(rootDir, relativePath);\n await writeTextFile(filePath, content);\n }\n}\n\nexport interface ScaffoldWriteResult {\n written: string[];\n skipped: string[];\n}\n\n/**\n * Write scaffold files using local-merge logic.\n *\n * - Library files are always overwritten.\n * - User-owned files (phases, components, etc.) are only written when the\n * local file does not exist or is empty. Files with content are skipped\n * so user edits are never lost.\n *\n * Returns lists of files that were written (and actually changed) and files\n * that were skipped because a non-empty local copy already exists.\n */\nexport async function writeScaffoldFiles(\n rootDir: string,\n files: Record<string, string | null>,\n): Promise<ScaffoldWriteResult> {\n const written: string[] = [];\n const skipped: string[] = [];\n\n for (const [relativePath, content] of Object.entries(files)) {\n if (content === null || content === undefined) continue;\n\n const fullPath = path.join(rootDir, relativePath);\n const existingContent = await readTextFileIfExists(fullPath);\n\n const decision = shouldWriteScaffoldFile(relativePath, existingContent);\n\n if (decision === \"skip\") {\n skipped.push(relativePath);\n continue;\n }\n\n await writeTextFile(fullPath, content);\n\n if (existingContent !== content) {\n written.push(relativePath);\n }\n }\n\n written.sort();\n skipped.sort();\n return { written, skipped };\n}\n\nexport async function removeExtraneousFiles(\n rootDir: string,\n keep: Set<string>,\n): Promise<void> {\n const localFiles = await collectLocalFiles(rootDir);\n for (const filePath of Object.keys(localFiles)) {\n if (filePath === MANIFEST_FILE || filePath === RULE_FILE) continue;\n if (!keep.has(filePath)) {\n await unlink(path.join(rootDir, filePath));\n }\n }\n}\n\nexport async function collectLocalFiles(\n rootDir: string,\n): Promise<Record<string, string>> {\n const result: Record<string, string> = {};\n await walkDir(rootDir, rootDir, result);\n return result;\n}\n\nexport async function walkDir(\n rootDir: string,\n currentDir: string,\n result: Record<string, string>,\n): Promise<void> {\n const entries = await readdir(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (LOCAL_IGNORE_DIRS.has(entry.name)) continue;\n await walkDir(rootDir, path.join(currentDir, entry.name), result);\n } else if (entry.isFile()) {\n const filePath = path.join(currentDir, entry.name);\n const relativePath = path.relative(rootDir, filePath);\n result[relativePath] = await readTextFile(filePath);\n }\n }\n}\n\nexport async function writeManifest(\n rootDir: string,\n manifest: GameTopologyManifest,\n): Promise<void> {\n await writeManifestSource(rootDir, manifest);\n}\n\nexport async function writeRule(\n rootDir: string,\n ruleText: string,\n): Promise<void> {\n const filePath = path.join(rootDir, RULE_FILE);\n await writeTextFile(filePath, ruleText);\n}\n\nexport async function loadManifest(\n rootDir: string,\n): Promise<GameTopologyManifest> {\n return materializeManifest(rootDir);\n}\n\nexport { computeManifestHash };\n\nexport async function loadRule(rootDir: string): Promise<string> {\n const filePath = path.join(rootDir, RULE_FILE);\n return readTextFile(filePath);\n}\n\nexport async function writeSnapshot(rootDir: string): Promise<void> {\n const files = await collectLocalFiles(rootDir);\n await writeSnapshotFromFiles(rootDir, files);\n}\n\nexport async function writeSnapshotFromFiles(\n rootDir: string,\n files: Record<string, string>,\n): Promise<void> {\n const snapshot: Snapshot = {\n files: {},\n };\n\n for (const [filePath, content] of Object.entries(files)) {\n if (filePath.startsWith(`${PROJECT_DIR_NAME}/`)) continue;\n if (isDynamicGeneratedPath(filePath)) continue;\n snapshot.files[filePath] = hashContent(content);\n }\n\n const snapshotPath = path.join(rootDir, PROJECT_DIR_NAME, SNAPSHOT_FILE);\n // Atomic write: a crash mid-snapshot (e.g. user kills a long `dreamboard\n // sync`) must not leave `.dreamboard/snapshot.json` truncated, or\n // `getLocalDiff` will silently report everything as \"added\" on the\n // next run and force a full re-sync.\n await atomicWriteFile(\n snapshotPath,\n `${JSON.stringify(snapshot, null, 2)}\\n`,\n { mode: 0o644 },\n );\n}\n\nfunction isIgnorableLocalDiffPath(filePath: string): boolean {\n return (\n filePath.startsWith(\"test/generated/\") ||\n filePath.startsWith(\".playwright-cli/\")\n );\n}\n\nexport async function getLocalDiff(rootDir: string): Promise<{\n modified: string[];\n added: string[];\n deleted: string[];\n}> {\n const snapshotPath = path.join(rootDir, PROJECT_DIR_NAME, SNAPSHOT_FILE);\n const snapshot = await readJsonFile<Snapshot>(snapshotPath).catch(() => null);\n if (!snapshot) {\n return { modified: [], added: [], deleted: [] };\n }\n\n const files = await collectLocalFiles(rootDir);\n const currentHashes: Record<string, string> = {};\n\n for (const [filePath, content] of Object.entries(files)) {\n if (filePath.startsWith(`${PROJECT_DIR_NAME}/`)) continue;\n if (isIgnorableLocalDiffPath(filePath)) continue;\n if (isDynamicGeneratedPath(filePath)) continue;\n currentHashes[filePath] = hashContent(content);\n }\n\n const modified: string[] = [];\n const added: string[] = [];\n const deleted: string[] = [];\n\n for (const [filePath, hash] of Object.entries(currentHashes)) {\n const prevHash = snapshot.files[filePath];\n if (!prevHash) {\n added.push(filePath);\n } else if (prevHash !== hash) {\n modified.push(filePath);\n }\n }\n\n for (const filePath of Object.keys(snapshot.files)) {\n if (isIgnorableLocalDiffPath(filePath)) continue;\n if (isDynamicGeneratedPath(filePath)) continue;\n if (!currentHashes[filePath]) {\n deleted.push(filePath);\n }\n }\n\n return { modified, added, deleted };\n}\n"],"mappings":"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","names":["isAllowedGamePath","isLibraryPath"]}
|