@dreamboard-games/cli 0.1.30-alpha.17 → 0.1.30-alpha.19

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 (74) hide show
  1. package/README.md +3 -1
  2. package/dist/agent-verifier/agent-workspace-verifier.mjs +12 -12
  3. package/dist/agent-verifier/{chunk-LKQ557TJ.mjs → chunk-334H4LE4.mjs} +3 -3
  4. package/dist/agent-verifier/{chunk-DWLTCUUX.mjs → chunk-3Y4FRMTK.mjs} +6 -6
  5. package/dist/agent-verifier/{chunk-NFL3Z4Z7.mjs → chunk-5D3OJBDT.mjs} +12 -8
  6. package/dist/agent-verifier/{chunk-NFL3Z4Z7.mjs.map → chunk-5D3OJBDT.mjs.map} +1 -1
  7. package/dist/agent-verifier/{chunk-AXXUGU7Q.mjs → chunk-6AKXIY37.mjs} +54 -36
  8. package/dist/agent-verifier/chunk-6AKXIY37.mjs.map +1 -0
  9. package/dist/agent-verifier/{chunk-H3XNWKJU.mjs → chunk-7LFDFXLS.mjs} +2 -2
  10. package/dist/agent-verifier/{chunk-CO3BRUD6.mjs → chunk-AWZ4M4NS.mjs} +15 -3
  11. package/dist/agent-verifier/chunk-AWZ4M4NS.mjs.map +1 -0
  12. package/dist/agent-verifier/{chunk-S4ZQSUPB.mjs → chunk-H5L4KK4Y.mjs} +9 -12
  13. package/dist/agent-verifier/chunk-H5L4KK4Y.mjs.map +1 -0
  14. package/dist/agent-verifier/{chunk-V6AQDR7W.mjs → chunk-HUBV22JQ.mjs} +3 -3
  15. package/dist/agent-verifier/chunk-HUBV22JQ.mjs.map +1 -0
  16. package/dist/agent-verifier/{chunk-A67WUYN2.mjs → chunk-LEWM26XR.mjs} +4 -4
  17. package/dist/agent-verifier/chunk-LEWM26XR.mjs.map +1 -0
  18. package/dist/agent-verifier/{chunk-WAFBU5U7.mjs → chunk-OJFZVGEL.mjs} +38 -13
  19. package/dist/agent-verifier/chunk-OJFZVGEL.mjs.map +1 -0
  20. package/dist/agent-verifier/{chunk-G2ECODRB.mjs → chunk-PLXXH5LY.mjs} +2 -2
  21. package/dist/agent-verifier/{chunk-DPYC2NDB.mjs → chunk-UMW24KZI.mjs} +2 -2
  22. package/dist/agent-verifier/{chunk-5GCZZ6NW.mjs → chunk-ZOR5FTIG.mjs} +2 -2
  23. package/dist/agent-verifier/{compile-SDQ25N7K.mjs → compile-MO2URO5Z.mjs} +12 -12
  24. package/dist/agent-verifier/{global-config-6UGFPLDA.mjs → global-config-SXR6X3OZ.mjs} +3 -3
  25. package/dist/agent-verifier/{keychain-backend-BQLW5VEC.mjs → keychain-backend-UF3Z26JM.mjs} +1 -1
  26. package/dist/agent-verifier/keychain-backend-UF3Z26JM.mjs.map +1 -0
  27. package/dist/agent-verifier/{local-files-WPHUV6GU.mjs → local-files-DAFIR7SN.mjs} +4 -4
  28. package/dist/agent-verifier/{materialize-workspace-ENPMB66P.mjs → materialize-workspace-PWNT6HQK.mjs} +6 -6
  29. package/dist/agent-verifier/{reducer-native-test-harness-GY2CCQWN.mjs → reducer-native-test-harness-X2KQYSCD.mjs} +8 -8
  30. package/dist/agent-verifier/{static-scaffold-K52P52SV.mjs → static-scaffold-HXUQLJVN.mjs} +4 -4
  31. package/dist/agent-verifier/{sync-JSATJXVJ.mjs → sync-5YM4CSXL.mjs} +13 -13
  32. package/dist/agent-verifier/{test-LQAGEQLY.mjs → test-CNNVTFIG.mjs} +11 -11
  33. package/dist/agent-verifier/{workspace-codegen-4IWICKLB.mjs → workspace-codegen-SPPVHURX.mjs} +3 -3
  34. package/dist/authoring-compatibility-internal.js +1 -1
  35. package/dist/{chunk-ITT6Y7MM.js → chunk-R6RB4EKH.js} +23 -9
  36. package/dist/chunk-R6RB4EKH.js.map +1 -0
  37. package/dist/{chunk-AVOAT522.js → chunk-VWMKJL4A.js} +49 -38
  38. package/dist/chunk-VWMKJL4A.js.map +1 -0
  39. package/dist/{chunk-LWJUF42S.js → chunk-YRSE5DLH.js} +45 -23
  40. package/dist/{chunk-LWJUF42S.js.map → chunk-YRSE5DLH.js.map} +1 -1
  41. package/dist/{global-config-NLGAFSRU.js → global-config-UHGWFJIK.js} +2 -2
  42. package/dist/index.js +403 -12
  43. package/dist/index.js.map +1 -1
  44. package/dist/internal.js +3 -3
  45. package/dist/{keychain-backend-47LZ5IX5.js → keychain-backend-GO34KGTG.js} +1 -1
  46. package/dist/keychain-backend-GO34KGTG.js.map +1 -0
  47. package/package.json +1 -1
  48. package/release/authoring-release-set.json +4 -4
  49. package/dist/agent-verifier/chunk-A67WUYN2.mjs.map +0 -1
  50. package/dist/agent-verifier/chunk-AXXUGU7Q.mjs.map +0 -1
  51. package/dist/agent-verifier/chunk-CO3BRUD6.mjs.map +0 -1
  52. package/dist/agent-verifier/chunk-S4ZQSUPB.mjs.map +0 -1
  53. package/dist/agent-verifier/chunk-V6AQDR7W.mjs.map +0 -1
  54. package/dist/agent-verifier/chunk-WAFBU5U7.mjs.map +0 -1
  55. package/dist/agent-verifier/keychain-backend-BQLW5VEC.mjs.map +0 -1
  56. package/dist/chunk-AVOAT522.js.map +0 -1
  57. package/dist/chunk-ITT6Y7MM.js.map +0 -1
  58. package/dist/keychain-backend-47LZ5IX5.js.map +0 -1
  59. /package/dist/agent-verifier/{chunk-LKQ557TJ.mjs.map → chunk-334H4LE4.mjs.map} +0 -0
  60. /package/dist/agent-verifier/{chunk-DWLTCUUX.mjs.map → chunk-3Y4FRMTK.mjs.map} +0 -0
  61. /package/dist/agent-verifier/{chunk-H3XNWKJU.mjs.map → chunk-7LFDFXLS.mjs.map} +0 -0
  62. /package/dist/agent-verifier/{chunk-G2ECODRB.mjs.map → chunk-PLXXH5LY.mjs.map} +0 -0
  63. /package/dist/agent-verifier/{chunk-DPYC2NDB.mjs.map → chunk-UMW24KZI.mjs.map} +0 -0
  64. /package/dist/agent-verifier/{chunk-5GCZZ6NW.mjs.map → chunk-ZOR5FTIG.mjs.map} +0 -0
  65. /package/dist/agent-verifier/{compile-SDQ25N7K.mjs.map → compile-MO2URO5Z.mjs.map} +0 -0
  66. /package/dist/agent-verifier/{global-config-6UGFPLDA.mjs.map → global-config-SXR6X3OZ.mjs.map} +0 -0
  67. /package/dist/agent-verifier/{local-files-WPHUV6GU.mjs.map → local-files-DAFIR7SN.mjs.map} +0 -0
  68. /package/dist/agent-verifier/{materialize-workspace-ENPMB66P.mjs.map → materialize-workspace-PWNT6HQK.mjs.map} +0 -0
  69. /package/dist/agent-verifier/{reducer-native-test-harness-GY2CCQWN.mjs.map → reducer-native-test-harness-X2KQYSCD.mjs.map} +0 -0
  70. /package/dist/agent-verifier/{static-scaffold-K52P52SV.mjs.map → static-scaffold-HXUQLJVN.mjs.map} +0 -0
  71. /package/dist/agent-verifier/{sync-JSATJXVJ.mjs.map → sync-5YM4CSXL.mjs.map} +0 -0
  72. /package/dist/agent-verifier/{test-LQAGEQLY.mjs.map → test-CNNVTFIG.mjs.map} +0 -0
  73. /package/dist/agent-verifier/{workspace-codegen-4IWICKLB.mjs.map → workspace-codegen-SPPVHURX.mjs.map} +0 -0
  74. /package/dist/{global-config-NLGAFSRU.js.map → global-config-UHGWFJIK.js.map} +0 -0
@@ -11,20 +11,36 @@ import {
11
11
  import os from "os";
12
12
  import path from "path";
13
13
  import { promises as fs } from "fs";
14
-
15
- // src/build-target.ts
16
- var injectedBuildChannel = true ? "development" : void 0;
17
- var BUILD_CHANNEL = injectedBuildChannel === "published" ? "published" : "development";
18
- var IS_PUBLISHED_BUILD = BUILD_CHANNEL === "published";
19
- var PUBLISHED_ENVIRONMENT = "prod";
20
-
21
- // src/config/credential-store.ts
14
+ var credentialDirectoryOverrideForTests = null;
15
+ function getCredentialDirectory() {
16
+ return credentialDirectoryOverrideForTests ?? path.join(os.homedir(), PROJECT_DIR_NAME);
17
+ }
22
18
  function getCredentialFilePath() {
23
- return path.join(os.homedir(), PROJECT_DIR_NAME, "auth.json");
19
+ return path.join(getCredentialDirectory(), "auth.json");
20
+ }
21
+ function getCredentialAuditLogPath() {
22
+ return path.join(getCredentialDirectory(), "auth-events.log");
24
23
  }
25
24
  function getCredentialLockPath() {
26
25
  return `${getCredentialFilePath()}.lock`;
27
26
  }
27
+ async function appendCredentialAuditEvent(event) {
28
+ try {
29
+ const logPath = getCredentialAuditLogPath();
30
+ await fs.mkdir(path.dirname(logPath), { recursive: true, mode: 448 });
31
+ await fs.appendFile(
32
+ logPath,
33
+ `${JSON.stringify({
34
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35
+ pid: process.pid,
36
+ ...event
37
+ })}
38
+ `,
39
+ { mode: 384 }
40
+ );
41
+ } catch {
42
+ }
43
+ }
28
44
  async function fileRead() {
29
45
  const filePath = getCredentialFilePath();
30
46
  let data;
@@ -90,12 +106,27 @@ async function fileWriteAccessOnly(accessToken) {
90
106
  }
91
107
  await writeFilePayload({ authToken: accessToken });
92
108
  }
93
- async function fileClear() {
109
+ async function fileClear(reason = "credential_store_clear") {
94
110
  const filePath = getCredentialFilePath();
95
111
  try {
96
112
  await fs.unlink(filePath);
113
+ await appendCredentialAuditEvent({
114
+ event: "auth_file_deleted",
115
+ reason,
116
+ authPath: filePath,
117
+ backend: "file"
118
+ });
97
119
  } catch (err) {
98
- if (err.code !== "ENOENT") throw err;
120
+ if (err.code === "ENOENT") {
121
+ await appendCredentialAuditEvent({
122
+ event: "auth_file_delete_missing",
123
+ reason,
124
+ authPath: filePath,
125
+ backend: "file"
126
+ });
127
+ return;
128
+ }
129
+ throw err;
99
130
  }
100
131
  }
101
132
  var fileCredentialBackend = {
@@ -117,19 +148,6 @@ var migrationCompleted = false;
117
148
  var backendResolver = defaultBackendResolver;
118
149
  async function defaultBackendResolver() {
119
150
  const override = (process.env.DREAMBOARD_CREDENTIAL_BACKEND ?? "").trim().toLowerCase();
120
- if (IS_PUBLISHED_BUILD) {
121
- if (override && override !== "keychain" && override !== "auto") {
122
- throw new CredentialStoreUnavailableError(
123
- "published builds require the OS credential store"
124
- );
125
- }
126
- const { tryKeychainBackend: tryKeychainBackend2 } = await import("./keychain-backend-BQLW5VEC.mjs");
127
- const keychain2 = await tryKeychainBackend2();
128
- if (keychain2.available) {
129
- return keychain2.backend;
130
- }
131
- throw new CredentialStoreUnavailableError(keychain2.reason);
132
- }
133
151
  if (override === "file") {
134
152
  return fileCredentialBackend;
135
153
  }
@@ -142,7 +160,7 @@ async function defaultBackendResolver() {
142
160
  if (!useKeychain) {
143
161
  return fileCredentialBackend;
144
162
  }
145
- const { tryKeychainBackend } = await import("./keychain-backend-BQLW5VEC.mjs");
163
+ const { tryKeychainBackend } = await import("./keychain-backend-UF3Z26JM.mjs");
146
164
  const keychain = await tryKeychainBackend();
147
165
  if (keychain.available) {
148
166
  return keychain.backend;
@@ -151,7 +169,7 @@ async function defaultBackendResolver() {
151
169
  }
152
170
  async function readCredentialBackendPreference() {
153
171
  try {
154
- const { loadGlobalConfig } = await import("./global-config-6UGFPLDA.mjs");
172
+ const { loadGlobalConfig } = await import("./global-config-SXR6X3OZ.mjs");
155
173
  const config = await loadGlobalConfig();
156
174
  return config.credentialBackend === "keychain";
157
175
  } catch {
@@ -162,9 +180,7 @@ async function getCredentialBackend() {
162
180
  if (cachedBackend === null) {
163
181
  cachedBackend = await backendResolver();
164
182
  if (!migrationCompleted && cachedBackend.name !== "file") {
165
- await migrateFromFileBackendIfNeeded(cachedBackend, {
166
- failClosed: IS_PUBLISHED_BUILD
167
- });
183
+ await migrateFromFileBackendIfNeeded(cachedBackend);
168
184
  }
169
185
  migrationCompleted = true;
170
186
  }
@@ -178,7 +194,6 @@ async function migrateFromFileBackendIfNeeded(target, options = {}) {
178
194
  ]);
179
195
  if (!onDisk) return;
180
196
  if (onTarget) {
181
- await fileCredentialBackend.clear();
182
197
  return;
183
198
  }
184
199
  if (onDisk.accessToken && onDisk.refreshToken) {
@@ -204,7 +219,6 @@ async function migrateFromFileBackendIfNeeded(target, options = {}) {
204
219
  } else {
205
220
  return;
206
221
  }
207
- await fileCredentialBackend.clear();
208
222
  } catch (error) {
209
223
  if (options.failClosed) {
210
224
  throw new CredentialStoreUnavailableError(
@@ -226,6 +240,12 @@ async function getStoredSession() {
226
240
  const backend = await getCredentialBackend();
227
241
  return backend.read();
228
242
  }
243
+ async function clearCredentials(reason = "credential_store_clear") {
244
+ await withFileLock(getCredentialLockPath(), async () => {
245
+ const backend = await getCredentialBackend();
246
+ await backend.clear(reason);
247
+ });
248
+ }
229
249
  async function withCredentialLock(fn, options) {
230
250
  return withFileLock(
231
251
  getCredentialLockPath(),
@@ -236,7 +256,7 @@ async function withCredentialLock(fn, options) {
236
256
  read: () => backend.read(),
237
257
  writeFull: (creds) => backend.writeFull(creds),
238
258
  writeAccessOnly: (accessToken) => backend.writeAccessOnly(accessToken),
239
- clear: () => backend.clear()
259
+ clear: (reason) => backend.clear(reason)
240
260
  };
241
261
  return fn(ops);
242
262
  },
@@ -245,11 +265,9 @@ async function withCredentialLock(fn, options) {
245
265
  }
246
266
 
247
267
  export {
248
- BUILD_CHANNEL,
249
- IS_PUBLISHED_BUILD,
250
- PUBLISHED_ENVIRONMENT,
251
268
  getCredentialFilePath,
252
269
  getStoredSession,
270
+ clearCredentials,
253
271
  withCredentialLock
254
272
  };
255
- //# sourceMappingURL=chunk-AXXUGU7Q.mjs.map
273
+ //# sourceMappingURL=chunk-6AKXIY37.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/config/credential-store.ts"],"sourcesContent":["/**\n * Single writer for the long-lived Dreamboard session credentials.\n *\n * Design invariants (enforced at the type level and tested in\n * `credential-store.test.ts`):\n *\n * 1. This module is the ONLY place in the CLI that writes credentials to\n * disk or the OS keychain. `global-config.ts` used to own both the\n * config and the credentials via `saveGlobalConfig`, which made it\n * trivial to wipe a refresh token by accident. The `GlobalConfig` type\n * no longer carries credentials, so attempting to persist one through\n * the config path is a type error.\n *\n * 2. The mutating surface is intentionally narrow:\n * - `setCredentials(c)` for refreshable sessions (both tokens present)\n * - `setAccessOnlySession(accessToken)` for the `auth set` / `config set\n * --token` power-user path, which has no refresh token by\n * construction\n * - `clearCredentials()` wipes the file entirely\n * There is no \"partial update\" API. `Credentials` requires both\n * `accessToken` and `refreshToken`, so it is impossible to persist a\n * half-populated refreshable session.\n *\n * 3. Writes go through `atomicWriteFile` + `withFileLock`, so a crash or\n * interrupt during `dreamboard sync`/`compile` cannot leave `auth.json`\n * truncated, and parallel CLI invocations cannot clobber each other's\n * rotated refresh tokens.\n *\n * 4. The on-disk JSON shape for the file backend is kept backward\n * compatible: we continue to read/write `authToken` + `refreshToken`\n * so existing users are not forced to log in again after this change.\n * A newer `accessToken` key is also accepted for read to ease any\n * future format bump.\n *\n * 5. All builds default to the file backend. The OS keychain is an explicit\n * opt-in through config or `DREAMBOARD_CREDENTIAL_BACKEND=keychain`.\n */\n\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promises as fs } from \"node:fs\";\nimport { PROJECT_DIR_NAME } from \"../constants.js\";\nimport {\n atomicWriteFile,\n withFileLock,\n type FileLockOptions,\n} from \"../utils/atomic-file.js\";\n\n/**\n * Fully refreshable session. `accessToken` is the Clerk OAuth bootstrap token\n * retained for refresh/exchange compatibility; ordinary API calls use\n * `dreamboardApiToken`.\n */\nexport type Credentials = {\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly tokenExpiresAt?: string;\n readonly dreamboardApiToken?: string;\n readonly dreamboardApiExpiresAt?: string;\n readonly clerkOAuthIssuer?: string;\n readonly clerkOAuthClientId?: string;\n readonly clerkOAuthTokenUrl?: string;\n readonly environment?: string;\n};\n\n/**\n * Raw on-disk snapshot. Either or both fields may be present. The refresh\n * coordinator only acts on snapshots that have both tokens populated.\n */\nexport type StoredSessionSnapshot = {\n readonly accessToken?: string;\n readonly refreshToken?: string;\n readonly tokenExpiresAt?: string;\n readonly dreamboardApiToken?: string;\n readonly dreamboardApiExpiresAt?: string;\n readonly clerkOAuthIssuer?: string;\n readonly clerkOAuthClientId?: string;\n readonly clerkOAuthTokenUrl?: string;\n readonly environment?: string;\n};\n\nexport type CredentialBackendName = \"file\" | \"keychain\";\n\nexport type CredentialBackend = {\n readonly name: CredentialBackendName;\n read(): Promise<StoredSessionSnapshot | null>;\n writeFull(creds: Credentials): Promise<void>;\n writeAccessOnly(accessToken: string): Promise<void>;\n clear(reason?: CredentialClearReason): Promise<void>;\n};\n\nexport type CredentialLockOps = {\n readonly backendName: CredentialBackendName;\n read(): Promise<StoredSessionSnapshot | null>;\n writeFull(creds: Credentials): Promise<void>;\n writeAccessOnly(accessToken: string): Promise<void>;\n clear(reason?: CredentialClearReason): Promise<void>;\n};\n\nexport type CredentialClearReason =\n | \"auth_clear_command\"\n | \"logout_command\"\n | \"user_token_manager_logout\"\n | \"credential_store_clear\";\n\ntype DiskShape = Partial<{\n clerkAccessToken: string;\n clerkAccessExpiresAt: string;\n accessToken: string;\n authToken: string;\n refreshToken: string;\n tokenExpiresAt: string;\n dreamboardApiToken: string;\n dreamboardApiExpiresAt: string;\n clerkOAuthIssuer: string;\n clerkOAuthClientId: string;\n clerkOAuthTokenUrl: string;\n environment: string;\n}>;\n\nlet credentialDirectoryOverrideForTests: string | null = null;\n\nfunction getCredentialDirectory(): string {\n return (\n credentialDirectoryOverrideForTests ??\n path.join(os.homedir(), PROJECT_DIR_NAME)\n );\n}\n\nexport function getCredentialFilePath(): string {\n return path.join(getCredentialDirectory(), \"auth.json\");\n}\n\nexport function getCredentialAuditLogPath(): string {\n return path.join(getCredentialDirectory(), \"auth-events.log\");\n}\n\nfunction getCredentialLockPath(): string {\n return `${getCredentialFilePath()}.lock`;\n}\n\nasync function appendCredentialAuditEvent(event: {\n readonly event: \"auth_file_deleted\" | \"auth_file_delete_missing\";\n readonly reason: CredentialClearReason;\n readonly authPath: string;\n readonly backend: CredentialBackendName;\n}): Promise<void> {\n try {\n const logPath = getCredentialAuditLogPath();\n await fs.mkdir(path.dirname(logPath), { recursive: true, mode: 0o700 });\n await fs.appendFile(\n logPath,\n `${JSON.stringify({\n timestamp: new Date().toISOString(),\n pid: process.pid,\n ...event,\n })}\\n`,\n { mode: 0o600 },\n );\n } catch {\n // Credential clearing must not fail because local diagnostic logging failed.\n }\n}\n\nasync function fileRead(): Promise<StoredSessionSnapshot | null> {\n const filePath = getCredentialFilePath();\n let data: string;\n try {\n data = await fs.readFile(filePath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n if (data.trim().length === 0) {\n return null;\n }\n let parsed: DiskShape;\n try {\n parsed = JSON.parse(data) as DiskShape;\n } catch {\n return null;\n }\n const accessToken =\n parsed.clerkAccessToken ?? parsed.accessToken ?? parsed.authToken;\n const refreshToken = parsed.refreshToken;\n if (!accessToken && !refreshToken) return null;\n return {\n accessToken: accessToken || undefined,\n refreshToken: refreshToken || undefined,\n tokenExpiresAt:\n parsed.clerkAccessExpiresAt || parsed.tokenExpiresAt || undefined,\n dreamboardApiToken: parsed.dreamboardApiToken || undefined,\n dreamboardApiExpiresAt: parsed.dreamboardApiExpiresAt || undefined,\n clerkOAuthIssuer: parsed.clerkOAuthIssuer || undefined,\n clerkOAuthClientId: parsed.clerkOAuthClientId || undefined,\n clerkOAuthTokenUrl: parsed.clerkOAuthTokenUrl || undefined,\n environment: parsed.environment || undefined,\n };\n}\n\nasync function writeFilePayload(payload: DiskShape): Promise<void> {\n await atomicWriteFile(\n getCredentialFilePath(),\n `${JSON.stringify(payload, null, 2)}\\n`,\n { mode: 0o600 },\n );\n}\n\nasync function fileWriteFull(creds: Credentials): Promise<void> {\n if (!creds.accessToken || !creds.refreshToken) {\n throw new Error(\n \"Refusing to persist credentials with an empty accessToken or refreshToken.\",\n );\n }\n await writeFilePayload({\n clerkAccessToken: creds.accessToken,\n refreshToken: creds.refreshToken,\n clerkAccessExpiresAt: creds.tokenExpiresAt,\n dreamboardApiToken: creds.dreamboardApiToken,\n dreamboardApiExpiresAt: creds.dreamboardApiExpiresAt,\n clerkOAuthIssuer: creds.clerkOAuthIssuer,\n clerkOAuthClientId: creds.clerkOAuthClientId,\n clerkOAuthTokenUrl: creds.clerkOAuthTokenUrl,\n environment: creds.environment,\n });\n}\n\nasync function fileWriteAccessOnly(accessToken: string): Promise<void> {\n if (!accessToken) {\n throw new Error(\"Refusing to persist an empty access token.\");\n }\n await writeFilePayload({ authToken: accessToken });\n}\n\nasync function fileClear(\n reason: CredentialClearReason = \"credential_store_clear\",\n): Promise<void> {\n const filePath = getCredentialFilePath();\n try {\n await fs.unlink(filePath);\n await appendCredentialAuditEvent({\n event: \"auth_file_deleted\",\n reason,\n authPath: filePath,\n backend: \"file\",\n });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n await appendCredentialAuditEvent({\n event: \"auth_file_delete_missing\",\n reason,\n authPath: filePath,\n backend: \"file\",\n });\n return;\n }\n throw err;\n }\n}\n\nexport const fileCredentialBackend: CredentialBackend = {\n name: \"file\",\n read: fileRead,\n writeFull: fileWriteFull,\n writeAccessOnly: fileWriteAccessOnly,\n clear: fileClear,\n};\n\nexport type BackendResolver = () =>\n | CredentialBackend\n | Promise<CredentialBackend>;\n\nexport class CredentialStoreUnavailableError extends Error {\n readonly code = \"CREDENTIAL_STORE_UNAVAILABLE\";\n\n constructor(reason: string) {\n super(`Credential store unavailable: ${reason}`);\n this.name = \"CredentialStoreUnavailableError\";\n }\n}\n\nlet cachedBackend: CredentialBackend | null = null;\nlet migrationCompleted = false;\nlet backendResolver: BackendResolver = defaultBackendResolver;\n\n/**\n * Resolver precedence for all builds:\n *\n * 1. `DREAMBOARD_CREDENTIAL_BACKEND` env var (debugging / CI override).\n * - \"file\" -> force file\n * - \"keychain\" -> force keychain (falls back to file if the native\n * module or the OS keyring is unavailable)\n * - \"auto\" -> same as unset (use config)\n * - unknown -> throw so typos fail loud\n * 2. `credentialBackend` in `~/.dreamboard/config.json`.\n * - \"keychain\" -> opt in to the OS keychain (with file fallback)\n * - \"file\" / unset / malformed -> file\n * 3. Default: file backend.\n *\n * Keychain is opt-in because on macOS the OS login-keychain prompts for\n * the user's password the first time a new binary tries to write to an\n * item, and re-prompts whenever the Node binary signature changes. We\n * would rather ship a zero-prompt default and let users who care about\n * encrypted-at-rest storage enable it.\n *\n * The resolver is async because the keychain probe requires a dynamic\n * `@napi-rs/keyring` import.\n */\nasync function defaultBackendResolver(): Promise<CredentialBackend> {\n const override = (process.env.DREAMBOARD_CREDENTIAL_BACKEND ?? \"\")\n .trim()\n .toLowerCase();\n if (override === \"file\") {\n return fileCredentialBackend;\n }\n if (override && override !== \"keychain\" && override !== \"auto\") {\n // Fail loud on typos rather than silently falling back: this env\n // var exists specifically for users who are debugging auth issues\n // and need to know their override took effect.\n throw new Error(\n `Unknown DREAMBOARD_CREDENTIAL_BACKEND value \"${override}\" (expected \"file\", \"keychain\", or \"auto\").`,\n );\n }\n\n const useKeychain =\n override === \"keychain\" || (await readCredentialBackendPreference());\n if (!useKeychain) {\n return fileCredentialBackend;\n }\n\n const { tryKeychainBackend } = await import(\"./keychain-backend.js\");\n const keychain = await tryKeychainBackend();\n if (keychain.available) {\n return keychain.backend;\n }\n // The user explicitly asked for keychain but the platform can't\n // provide one (no libsecret on Linux, missing native module, etc).\n // Silently degrade to the file backend so the CLI stays usable; the\n // active backend is still visible through `dreamboard auth status`.\n return fileCredentialBackend;\n}\n\nasync function readCredentialBackendPreference(): Promise<boolean> {\n try {\n // Dynamic import to avoid a top-level cycle with `global-config.ts`\n // (which imports `getCredentialFilePath` from this module). Using\n // the async path keeps the cycle purely lazy.\n const { loadGlobalConfig } = await import(\"./global-config.js\");\n const config = await loadGlobalConfig();\n return config.credentialBackend === \"keychain\";\n } catch {\n // If the config file is unreadable or the dynamic import fails\n // (e.g. during early bootstrap), fall back to the file-backed\n // default rather than crashing credential lookups.\n return false;\n }\n}\n\n/**\n * Override which backend is used. Tests use this to inject in-memory\n * backends; production code uses the file-default resolver.\n */\nexport function setCredentialBackendResolver(resolver: BackendResolver): void {\n backendResolver = resolver;\n cachedBackend = null;\n migrationCompleted = false;\n}\n\nexport async function getCredentialBackend(): Promise<CredentialBackend> {\n if (cachedBackend === null) {\n cachedBackend = await backendResolver();\n // One-time migration: if we resolved to a non-file backend and\n // `auth.json` still has credentials from the old layout, copy them\n // over. The file is intentionally left in place; implicit backend\n // migration must not make a working CLI session appear to vanish from\n // the default file-backed view.\n if (!migrationCompleted && cachedBackend.name !== \"file\") {\n await migrateFromFileBackendIfNeeded(cachedBackend);\n }\n migrationCompleted = true;\n }\n return cachedBackend;\n}\n\nasync function migrateFromFileBackendIfNeeded(\n target: CredentialBackend,\n options: { failClosed?: boolean } = {},\n): Promise<void> {\n try {\n const [onDisk, onTarget] = await Promise.all([\n fileCredentialBackend.read(),\n target.read(),\n ]);\n if (!onDisk) return;\n if (onTarget) {\n // Target already has a session - the user has already migrated. Leave the\n // file copy alone so a transient keychain override/probe cannot remove\n // the visible file-backed session.\n return;\n }\n if (onDisk.accessToken && onDisk.refreshToken) {\n const migrated: Credentials = {\n accessToken: onDisk.accessToken,\n refreshToken: onDisk.refreshToken,\n tokenExpiresAt: onDisk.tokenExpiresAt,\n dreamboardApiToken: onDisk.dreamboardApiToken,\n dreamboardApiExpiresAt: onDisk.dreamboardApiExpiresAt,\n clerkOAuthIssuer: onDisk.clerkOAuthIssuer,\n clerkOAuthClientId: onDisk.clerkOAuthClientId,\n clerkOAuthTokenUrl: onDisk.clerkOAuthTokenUrl,\n environment: onDisk.environment,\n };\n await target.writeFull(migrated);\n await verifyMigratedSession(target, migrated);\n } else if (onDisk.accessToken) {\n await target.writeAccessOnly(onDisk.accessToken);\n const migrated = await target.read();\n if (migrated?.accessToken !== onDisk.accessToken) {\n throw new Error(\"Credential migration verification failed.\");\n }\n } else {\n return;\n }\n } catch (error) {\n if (options.failClosed) {\n throw new CredentialStoreUnavailableError(\n error instanceof Error ? error.message : String(error),\n );\n }\n // Migration is best-effort. A failure here should not block CLI\n // operation; on next run the file backend is still consulted\n // directly because the keychain backend's `read` returns null and\n // callers fall through to \"missing session\" → login prompt.\n }\n}\n\nasync function verifyMigratedSession(\n target: CredentialBackend,\n expected: Credentials,\n): Promise<void> {\n const migrated = await target.read();\n if (\n migrated?.accessToken !== expected.accessToken ||\n migrated.refreshToken !== expected.refreshToken\n ) {\n throw new Error(\"Credential migration verification failed.\");\n }\n}\n\nexport async function getActiveCredentialBackendName(): Promise<CredentialBackendName> {\n const backend = await getCredentialBackend();\n return backend.name;\n}\n\n/** Loose read: returns whatever is on disk, including access-only sessions. */\nexport async function getStoredSession(): Promise<StoredSessionSnapshot | null> {\n if (process.env.DREAMBOARD_AGENT_TOKEN?.trim()) {\n return null;\n }\n const backend = await getCredentialBackend();\n return backend.read();\n}\n\n/** Strict read: returns a refreshable pair, or null if either token is missing. */\nexport async function getCredentials(): Promise<Credentials | null> {\n const snapshot = await getStoredSession();\n if (!snapshot) return null;\n const { accessToken, refreshToken } = snapshot;\n if (!accessToken || !refreshToken) return null;\n return {\n accessToken,\n refreshToken,\n tokenExpiresAt: snapshot.tokenExpiresAt,\n dreamboardApiToken: snapshot.dreamboardApiToken,\n dreamboardApiExpiresAt: snapshot.dreamboardApiExpiresAt,\n clerkOAuthIssuer: snapshot.clerkOAuthIssuer,\n clerkOAuthClientId: snapshot.clerkOAuthClientId,\n clerkOAuthTokenUrl: snapshot.clerkOAuthTokenUrl,\n environment: snapshot.environment,\n };\n}\n\nexport async function setCredentials(creds: Credentials): Promise<void> {\n await withFileLock(getCredentialLockPath(), async () => {\n const backend = await getCredentialBackend();\n await backend.writeFull(creds);\n });\n}\n\nexport async function setAccessOnlySession(accessToken: string): Promise<void> {\n await withFileLock(getCredentialLockPath(), async () => {\n const backend = await getCredentialBackend();\n await backend.writeAccessOnly(accessToken);\n });\n}\n\nexport async function clearCredentials(\n reason: CredentialClearReason = \"credential_store_clear\",\n): Promise<void> {\n await withFileLock(getCredentialLockPath(), async () => {\n const backend = await getCredentialBackend();\n await backend.clear(reason);\n });\n}\n\n/**\n * Run `fn` while holding the cross-process credential lock. `fn` receives\n * an ops handle that reads/writes the active backend without re-acquiring\n * the lock (avoiding deadlock).\n *\n * This is the only correct way to perform a read-modify-write on stored\n * credentials (e.g. CLI refresh rotation) in the presence of\n * concurrent CLI invocations.\n */\nexport async function withCredentialLock<T>(\n fn: (ops: CredentialLockOps) => Promise<T>,\n options?: FileLockOptions,\n): Promise<T> {\n return withFileLock(\n getCredentialLockPath(),\n async () => {\n const backend = await getCredentialBackend();\n const ops: CredentialLockOps = {\n backendName: backend.name,\n read: () => backend.read(),\n writeFull: (creds) => backend.writeFull(creds),\n writeAccessOnly: (accessToken) => backend.writeAccessOnly(accessToken),\n clear: (reason) => backend.clear(reason),\n };\n return fn(ops);\n },\n options,\n );\n}\n\n/** Test-only reset of module state. Not exported through the barrel. */\nexport function _resetCredentialStoreForTests(): void {\n cachedBackend = null;\n migrationCompleted = false;\n backendResolver = defaultBackendResolver;\n credentialDirectoryOverrideForTests = null;\n}\n\n/** Test-only override of the credential directory. Not exported through the barrel. */\nexport function _setCredentialDirectoryForTests(directory: string | null): void {\n credentialDirectoryOverrideForTests = directory;\n cachedBackend = null;\n migrationCompleted = false;\n}\n"],"mappings":"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","names":[]}
@@ -14,7 +14,7 @@ import {
14
14
  workspacePathExists,
15
15
  writeWorkspaceJsonFile,
16
16
  writeWorkspaceTextFile
17
- } from "./chunk-WAFBU5U7.mjs";
17
+ } from "./chunk-OJFZVGEL.mjs";
18
18
  import {
19
19
  MANIFEST_FILE,
20
20
  MATERIALIZED_MANIFEST_FILE
@@ -2603,4 +2603,4 @@ export {
2603
2603
  isDynamicSeedPath,
2604
2604
  isLibraryPath
2605
2605
  };
2606
- //# sourceMappingURL=chunk-H3XNWKJU.mjs.map
2606
+ //# sourceMappingURL=chunk-7LFDFXLS.mjs.map
@@ -1,8 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- IS_PUBLISHED_BUILD,
3
+ clearCredentials,
4
4
  withCredentialLock
5
- } from "./chunk-AXXUGU7Q.mjs";
5
+ } from "./chunk-6AKXIY37.mjs";
6
+
7
+ // src/build-target.ts
8
+ var injectedBuildChannel = true ? "development" : void 0;
9
+ var BUILD_CHANNEL = injectedBuildChannel === "published" ? "published" : "development";
10
+ var IS_PUBLISHED_BUILD = BUILD_CHANNEL === "published";
11
+ var PUBLISHED_ENVIRONMENT = "prod";
6
12
 
7
13
  // src/auth/clerk-oauth.ts
8
14
  import crypto from "crypto";
@@ -164,6 +170,9 @@ function createUserTokenManager(config) {
164
170
  audience: "dreamboard-git"
165
171
  };
166
172
  });
173
+ },
174
+ async logout() {
175
+ await clearCredentials("user_token_manager_logout");
167
176
  }
168
177
  };
169
178
  }
@@ -336,7 +345,10 @@ function isLocalAwsUrl(rawUrl) {
336
345
  }
337
346
 
338
347
  export {
348
+ BUILD_CHANNEL,
349
+ IS_PUBLISHED_BUILD,
350
+ PUBLISHED_ENVIRONMENT,
339
351
  createUserTokenManager,
340
352
  resolveLocalHarnessAccessToken
341
353
  };
342
- //# sourceMappingURL=chunk-CO3BRUD6.mjs.map
354
+ //# sourceMappingURL=chunk-AWZ4M4NS.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/build-target.ts","../../src/auth/clerk-oauth.ts","../../src/auth/token-exchange.ts","../../src/auth/user-token-manager.ts","../../src/config/local-harness-auth.ts"],"sourcesContent":["declare const __DREAMBOARD_BUILD_CHANNEL__: string | undefined;\n\nconst injectedBuildChannel =\n typeof __DREAMBOARD_BUILD_CHANNEL__ === \"string\"\n ? __DREAMBOARD_BUILD_CHANNEL__\n : undefined;\n\nexport const BUILD_CHANNEL =\n injectedBuildChannel === \"published\" ? \"published\" : \"development\";\n\nexport const IS_PUBLISHED_BUILD = BUILD_CHANNEL === \"published\";\nexport const PUBLISHED_ENVIRONMENT = \"prod\" as const;\n","import crypto from \"node:crypto\";\n\nexport type ClerkOAuthConfig = {\n issuer?: string;\n clientId?: string;\n tokenUrl?: string;\n scope?: string;\n};\n\nexport type ClerkOAuthTokenResponse = {\n accessToken: string;\n refreshToken: string;\n expiresAt?: string;\n tokenUrl: string;\n};\n\nexport function createPkcePair(): { verifier: string; challenge: string } {\n const verifier = base64Url(crypto.randomBytes(32));\n const challenge = base64Url(\n crypto.createHash(\"sha256\").update(verifier).digest(),\n );\n return { verifier, challenge };\n}\n\nexport function buildClerkAuthorizationUrl(input: {\n config: ClerkOAuthConfig;\n redirectUri: string;\n state: string;\n codeChallenge: string;\n}): URL {\n const { issuer, clientId, scope } = assertConfigured(input.config);\n const url = new URL(\"/oauth/authorize\", issuer);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", clientId);\n url.searchParams.set(\"redirect_uri\", input.redirectUri);\n url.searchParams.set(\"state\", input.state);\n url.searchParams.set(\"code_challenge\", input.codeChallenge);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"scope\", scope ?? \"openid profile email offline_access\");\n return url;\n}\n\nexport async function exchangeClerkOAuthCode(input: {\n config: ClerkOAuthConfig;\n code: string;\n redirectUri: string;\n codeVerifier: string;\n}): Promise<ClerkOAuthTokenResponse> {\n const { clientId, tokenUrl } = assertConfigured(input.config);\n const body = new URLSearchParams({\n grant_type: \"authorization_code\",\n client_id: clientId,\n code: input.code,\n redirect_uri: input.redirectUri,\n code_verifier: input.codeVerifier,\n });\n return requestClerkToken(tokenUrl, body);\n}\n\nexport async function refreshClerkOAuthToken(input: {\n config: ClerkOAuthConfig;\n refreshToken: string;\n}): Promise<ClerkOAuthTokenResponse> {\n const { clientId, tokenUrl } = assertConfigured(input.config);\n const body = new URLSearchParams({\n grant_type: \"refresh_token\",\n client_id: clientId,\n refresh_token: input.refreshToken,\n });\n return requestClerkToken(tokenUrl, body);\n}\n\nfunction assertConfigured(config: ClerkOAuthConfig): {\n issuer: string;\n clientId: string;\n tokenUrl: string;\n scope?: string;\n} {\n const issuer = config.issuer?.trim().replace(/\\/$/, \"\");\n const clientId = config.clientId?.trim();\n if (!issuer || !clientId) {\n throw new Error(\n [\n \"Clerk OAuth CLI is not configured for this environment.\",\n \"The CLI expects first-party environments to be configured in its built-in registry.\",\n \"If this environment has no registered public Clerk OAuth client, create one and release a CLI with its client id.\",\n \"For emergency overrides, set the environment-specific DREAMBOARD_<ENV>_CLERK_OAUTH_* variables or DREAMBOARD_CLERK_OAUTH_*.\",\n \"For local harness auth, use `pnpm auth:local` or the auto-bootstrapped local harness flows instead.\",\n ].join(\" \"),\n );\n }\n return {\n issuer,\n clientId,\n tokenUrl:\n config.tokenUrl?.trim() || new URL(\"/oauth/token\", issuer).toString(),\n scope: config.scope?.trim() || undefined,\n };\n}\n\nasync function requestClerkToken(\n tokenUrl: string,\n body: URLSearchParams,\n): Promise<ClerkOAuthTokenResponse> {\n const response = await fetch(tokenUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body,\n });\n if (!response.ok) {\n const detail = await response.text();\n throw new Error(\n `Clerk OAuth token request failed (${response.status}): ${detail}`,\n );\n }\n const payload = (await response.json()) as {\n access_token?: unknown;\n refresh_token?: unknown;\n expires_in?: unknown;\n };\n if (typeof payload.access_token !== \"string\") {\n throw new Error(\"Clerk OAuth token response did not include access_token.\");\n }\n if (typeof payload.refresh_token !== \"string\") {\n throw new Error(\n \"Clerk OAuth token response did not include refresh_token.\",\n );\n }\n const expiresAt =\n typeof payload.expires_in === \"number\"\n ? new Date(Date.now() + payload.expires_in * 1000).toISOString()\n : undefined;\n return {\n accessToken: payload.access_token,\n refreshToken: payload.refresh_token,\n expiresAt,\n tokenUrl,\n };\n}\n\nfunction base64Url(bytes: Buffer): string {\n return bytes\n .toString(\"base64\")\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/g, \"\");\n}\n","export type DreamboardTokenAudience = \"dreamboard-api\" | \"dreamboard-git\";\n\nexport type DreamboardTokenResponse = {\n accessToken: string;\n tokenType: \"Bearer\";\n audience: DreamboardTokenAudience;\n expiresIn?: number;\n expiresAt?: string;\n};\n\nexport async function exchangeDreamboardUserToken(input: {\n apiBaseUrl: string;\n clerkAccessToken: string;\n audience: DreamboardTokenAudience;\n fetchImpl?: typeof fetch;\n}): Promise<DreamboardTokenResponse> {\n const fetchImpl = input.fetchImpl ?? globalThis.fetch.bind(globalThis);\n const response = await fetchImpl(\n new URL(\"/api/auth/token-exchange\", input.apiBaseUrl),\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${input.clerkAccessToken}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify({ audience: input.audience }),\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `Dreamboard token exchange failed (${response.status}). Run \\`dreamboard login\\` to authenticate again.`,\n );\n }\n\n const payload = (await response.json()) as {\n accessToken?: unknown;\n tokenType?: unknown;\n audience?: unknown;\n expiresIn?: unknown;\n };\n\n if (typeof payload.accessToken !== \"string\" || payload.accessToken === \"\") {\n throw new Error(\"Dreamboard token exchange response omitted accessToken.\");\n }\n if (payload.tokenType !== \"Bearer\") {\n throw new Error(\n \"Dreamboard token exchange response had invalid tokenType.\",\n );\n }\n if (payload.audience !== input.audience) {\n throw new Error(\"Dreamboard token exchange response had wrong audience.\");\n }\n\n const expiresIn =\n typeof payload.expiresIn === \"number\" && Number.isFinite(payload.expiresIn)\n ? payload.expiresIn\n : undefined;\n\n return {\n accessToken: payload.accessToken,\n tokenType: \"Bearer\",\n audience: input.audience,\n expiresIn,\n expiresAt:\n expiresIn === undefined\n ? undefined\n : new Date(Date.now() + expiresIn * 1000).toISOString(),\n };\n}\n","import type {\n Credentials,\n StoredSessionSnapshot,\n} from \"../config/credential-store.js\";\nimport {\n clearCredentials,\n withCredentialLock,\n} from \"../config/credential-store.js\";\nimport type {\n AccessToken,\n UserTokenManager,\n} from \"@dreamboard-games/cli-core\";\nimport { refreshClerkOAuthToken } from \"./clerk-oauth.js\";\nimport {\n exchangeDreamboardUserToken,\n type DreamboardTokenAudience,\n} from \"./token-exchange.js\";\nimport type { ResolvedConfig } from \"../types.js\";\n\nconst TOKEN_REFRESH_WINDOW_MS = 60 * 1000;\n\nexport function createUserTokenManager(\n config: ResolvedConfig,\n): UserTokenManager {\n return {\n async resolveApiToken() {\n const localOrInjected = resolveNonStoredToken(config, \"dreamboard-api\");\n if (localOrInjected) return localOrInjected;\n\n if (!usesStoredSession(config)) return null;\n\n return withCredentialLock(async (ops) => {\n const stored = await ops.read();\n const apiToken = freshStoredApiToken(stored);\n if (apiToken) return apiToken;\n\n const clerk = await resolveFreshClerkAccessToken(config, stored);\n const exchanged = await exchangeDreamboardUserToken({\n apiBaseUrl: config.apiBaseUrl,\n clerkAccessToken: clerk.accessToken,\n audience: \"dreamboard-api\",\n });\n\n await ops.writeFull({\n ...clerk,\n dreamboardApiToken: exchanged.accessToken,\n dreamboardApiExpiresAt: exchanged.expiresAt,\n });\n\n return {\n token: exchanged.accessToken,\n expiresAt: exchanged.expiresAt,\n audience: \"dreamboard-api\",\n };\n });\n },\n\n async resolveGitToken() {\n const localOrInjected = resolveNonStoredToken(config, \"dreamboard-git\");\n if (localOrInjected) return localOrInjected;\n\n if (!usesStoredSession(config)) {\n throw new Error(\n \"Missing Dreamboard session. Run `dreamboard login` to authenticate.\",\n );\n }\n\n return withCredentialLock(async (ops) => {\n const stored = await ops.read();\n const clerk = await resolveFreshClerkAccessToken(config, stored);\n const exchanged = await exchangeDreamboardUserToken({\n apiBaseUrl: config.apiBaseUrl,\n clerkAccessToken: clerk.accessToken,\n audience: \"dreamboard-git\",\n });\n\n await ops.writeFull(clerk);\n\n return {\n token: exchanged.accessToken,\n expiresAt: exchanged.expiresAt,\n audience: \"dreamboard-git\",\n };\n });\n },\n\n async logout() {\n await clearCredentials(\"user_token_manager_logout\");\n },\n };\n}\n\nfunction resolveNonStoredToken(\n config: ResolvedConfig,\n audience: DreamboardTokenAudience,\n): AccessToken | null {\n if (usesStoredSession(config)) return null;\n if (!config.authToken) return null;\n return {\n token: config.authToken,\n expiresAt: config.tokenExpiresAt,\n audience,\n };\n}\n\nfunction freshStoredApiToken(\n stored: StoredSessionSnapshot | null,\n): AccessToken | null {\n if (!stored?.dreamboardApiToken) return null;\n if (isFresh(stored.dreamboardApiExpiresAt, stored.dreamboardApiToken)) {\n return {\n token: stored.dreamboardApiToken,\n expiresAt: stored.dreamboardApiExpiresAt,\n audience: \"dreamboard-api\",\n };\n }\n return null;\n}\n\nasync function resolveFreshClerkAccessToken(\n config: ResolvedConfig,\n stored: StoredSessionSnapshot | null,\n): Promise<Credentials> {\n const accessToken = stored?.accessToken ?? config.clerkAccessToken;\n const refreshToken = stored?.refreshToken ?? config.refreshToken;\n const tokenExpiresAt = stored?.tokenExpiresAt ?? config.clerkAccessExpiresAt;\n\n if (!refreshToken) {\n throw new Error(\n \"Stored Dreamboard session is missing its refresh token. Run `dreamboard login` to authenticate again.\",\n );\n }\n\n if (accessToken && isFresh(tokenExpiresAt, accessToken)) {\n return {\n accessToken,\n refreshToken,\n tokenExpiresAt,\n dreamboardApiToken: stored?.dreamboardApiToken,\n dreamboardApiExpiresAt: stored?.dreamboardApiExpiresAt,\n clerkOAuthIssuer: stored?.clerkOAuthIssuer ?? config.clerkOAuthIssuer,\n clerkOAuthClientId:\n stored?.clerkOAuthClientId ?? config.clerkOAuthClientId,\n clerkOAuthTokenUrl:\n stored?.clerkOAuthTokenUrl ?? config.clerkOAuthTokenUrl,\n environment: stored?.environment ?? config.environment,\n };\n }\n\n const payload = await refreshClerkOAuthToken({\n config: {\n issuer: stored?.clerkOAuthIssuer ?? config.clerkOAuthIssuer,\n clientId: stored?.clerkOAuthClientId ?? config.clerkOAuthClientId,\n tokenUrl: stored?.clerkOAuthTokenUrl ?? config.clerkOAuthTokenUrl,\n },\n refreshToken,\n });\n\n return {\n accessToken: payload.accessToken,\n refreshToken: payload.refreshToken,\n tokenExpiresAt: payload.expiresAt,\n clerkOAuthIssuer: stored?.clerkOAuthIssuer ?? config.clerkOAuthIssuer,\n clerkOAuthClientId: stored?.clerkOAuthClientId ?? config.clerkOAuthClientId,\n clerkOAuthTokenUrl: payload.tokenUrl,\n environment: stored?.environment ?? config.environment,\n };\n}\n\nfunction isFresh(expiresAt: string | undefined, token: string): boolean {\n const expiry = expiresAt ? new Date(expiresAt) : getJwtExpiry(token);\n return (\n expiry !== null &&\n Number.isFinite(expiry.getTime()) &&\n expiry.getTime() > Date.now() + TOKEN_REFRESH_WINDOW_MS\n );\n}\n\nfunction getJwtExpiry(accessToken: string | undefined): Date | null {\n if (!accessToken) return null;\n const parts = accessToken.split(\".\");\n if (parts.length !== 3) return null;\n try {\n const payload = JSON.parse(\n Buffer.from(parts[1]!, \"base64url\").toString(\"utf8\"),\n ) as { exp?: unknown };\n if (typeof payload.exp !== \"number\" || !Number.isFinite(payload.exp)) {\n return null;\n }\n return new Date(payload.exp * 1000);\n } catch {\n return null;\n }\n}\n\nfunction usesStoredSession(config: ResolvedConfig): boolean {\n return config.refreshTokenSource === \"global\";\n}\n","import { createHmac, randomUUID } from \"node:crypto\";\nimport type { ResolvedConfig } from \"../types.js\";\nimport { IS_PUBLISHED_BUILD } from \"../build-target.js\";\n\ntype LocalHarnessProfile = \"local\" | \"local-aws\";\n\nconst DEFAULT_SUBJECT = \"harness-smoke-local@dreamboard.local\";\nconst DEFAULT_ISSUER = \"dreamboard-local-harness\";\nconst DEFAULT_SECRET = \"dreamboard-local-harness-token-secret\";\nconst LOCAL_AWS_ISSUER = \"dreamboard-local-aws-harness\";\nconst LOCAL_AWS_SECRET = \"dreamboard-local-aws-harness-token-secret\";\nconst DEFAULT_TTL_SECONDS = 8 * 60 * 60;\n\nconst mintedTokens = new Map<string, string>();\n\nexport function resolveLocalHarnessAccessToken(\n config: ResolvedConfig,\n): string | undefined {\n if (IS_PUBLISHED_BUILD || config.environment !== \"local\") {\n return undefined;\n }\n\n const profile = inferLocalHarnessProfile(config);\n if (\n config.authToken &&\n (profile !== \"local-aws\" || isExplicitTokenSource(config.authTokenSource))\n ) {\n return undefined;\n }\n\n const cacheKey = [\n profile,\n process.env.LOCAL_HARNESS_TOKEN_ISSUER ?? \"\",\n process.env.LOCAL_HARNESS_TOKEN_SECRET ?? \"\",\n process.env.LOCAL_HARNESS_SUBJECT ?? \"\",\n process.env.HARNESS_USER_EMAIL ?? \"\",\n process.env.LOCAL_HARNESS_EMAIL ?? \"\",\n process.env.LOCAL_HARNESS_TOKEN_TTL_SECONDS ?? \"\",\n ].join(\"\\0\");\n const cached = mintedTokens.get(cacheKey);\n if (cached) return cached;\n\n const token = mintLocalHarnessToken(profile);\n mintedTokens.set(cacheKey, token);\n return token;\n}\n\nfunction isExplicitTokenSource(\n source: ResolvedConfig[\"authTokenSource\"],\n): boolean {\n return source === \"flag\" || source === \"env\" || source === \"agent-env\";\n}\n\nexport function inferLocalHarnessProfile(\n config: Pick<ResolvedConfig, \"apiBaseUrl\" | \"webBaseUrl\">,\n): LocalHarnessProfile {\n return isLocalAwsUrl(config.apiBaseUrl) || isLocalAwsUrl(config.webBaseUrl)\n ? \"local-aws\"\n : \"local\";\n}\n\nfunction mintLocalHarnessToken(profile: LocalHarnessProfile): string {\n const subject =\n envValue(process.env.LOCAL_HARNESS_SUBJECT) ??\n envValue(process.env.HARNESS_USER_EMAIL) ??\n DEFAULT_SUBJECT;\n const email =\n envValue(process.env.LOCAL_HARNESS_EMAIL) ??\n (subject.includes(\"@\") ? subject : undefined);\n const issuer =\n envValue(process.env.LOCAL_HARNESS_TOKEN_ISSUER) ??\n (profile === \"local-aws\" ? LOCAL_AWS_ISSUER : DEFAULT_ISSUER);\n const secret =\n envValue(process.env.LOCAL_HARNESS_TOKEN_SECRET) ??\n (profile === \"local-aws\" ? LOCAL_AWS_SECRET : DEFAULT_SECRET);\n const ttlSeconds = Number(\n envValue(process.env.LOCAL_HARNESS_TOKEN_TTL_SECONDS) ??\n String(DEFAULT_TTL_SECONDS),\n );\n if (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0) {\n throw new Error(\n \"LOCAL_HARNESS_TOKEN_TTL_SECONDS must be a positive number.\",\n );\n }\n\n const now = Math.floor(Date.now() / 1000);\n const payload = {\n typ: \"local_harness_access\",\n dreamboard_provider: \"local-harness\",\n dreamboard_provider_subject: subject,\n ...(email ? { email } : {}),\n iss: issuer,\n sub: subject,\n iat: now,\n exp: now + Math.floor(ttlSeconds),\n jti: randomUUID(),\n };\n\n const headerPart = base64UrlJson({ alg: \"HS256\", typ: \"JWT\" });\n const payloadPart = base64UrlJson(payload);\n const signature = createHmac(\"sha256\", secret)\n .update(`${headerPart}.${payloadPart}`)\n .digest(\"base64url\");\n return `${headerPart}.${payloadPart}.${signature}`;\n}\n\nfunction base64UrlJson(value: unknown): string {\n return Buffer.from(JSON.stringify(value), \"utf8\").toString(\"base64url\");\n}\n\nfunction envValue(raw: string | undefined): string | undefined {\n return typeof raw === \"string\" && raw.trim().length > 0\n ? raw.trim()\n : undefined;\n}\n\nfunction isLocalAwsUrl(rawUrl: string | undefined): boolean {\n if (!rawUrl) return false;\n try {\n const url = new URL(rawUrl);\n return (\n (url.hostname === \"localhost\" || url.hostname === \"127.0.0.1\") &&\n (url.port === \"18080\" || url.port === \"8088\")\n );\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":[]}
@@ -8,7 +8,7 @@ import {
8
8
  import {
9
9
  isDynamicSeedPath,
10
10
  materializeManifest
11
- } from "./chunk-H3XNWKJU.mjs";
11
+ } from "./chunk-7LFDFXLS.mjs";
12
12
  import {
13
13
  normalizeOwnedProjectPath,
14
14
  readWorkspaceTextFile,
@@ -18,7 +18,7 @@ import {
18
18
  unlinkWorkspaceFile,
19
19
  workspacePathExists,
20
20
  writeWorkspaceTextFile
21
- } from "./chunk-WAFBU5U7.mjs";
21
+ } from "./chunk-OJFZVGEL.mjs";
22
22
  import {
23
23
  FRAMEWORK_PNPM_OVERRIDES,
24
24
  FRAMEWORK_REACT_DEPENDENCIES,
@@ -41,11 +41,11 @@ var AUTHORING_RELEASE_SET = {
41
41
  "packages": {
42
42
  "cli": {
43
43
  "name": "@dreamboard-games/cli",
44
- "version": "0.1.30-alpha.17"
44
+ "version": "0.1.30-alpha.19"
45
45
  },
46
46
  "sdk": {
47
47
  "name": "@dreamboard-games/sdk",
48
- "version": "0.4.0-alpha.5"
48
+ "version": "0.4.0-alpha.6"
49
49
  },
50
50
  "apiClient": {
51
51
  "name": "@dreamboard-games/api-client",
@@ -53,7 +53,7 @@ var AUTHORING_RELEASE_SET = {
53
53
  },
54
54
  "devHost": {
55
55
  "name": "@dreamboard-games/dev-host",
56
- "version": "0.1.30-alpha.17"
56
+ "version": "0.1.30-alpha.19"
57
57
  }
58
58
  },
59
59
  "protocols": {
@@ -71,7 +71,7 @@ var AUTHORING_RELEASE_SET = {
71
71
  "portable": true
72
72
  },
73
73
  "packageManager": "pnpm@10.4.1",
74
- "releaseSetId": "sha256:379c5fbf570f4d63e100777b13f90707adfcd900276434bb6b863240dc43f1b6"
74
+ "releaseSetId": "sha256:94f9e944361da712ec7f0b420643d3690e73b93a2c23d3a893b0f4183a93a847"
75
75
  };
76
76
 
77
77
  // src/services/project/static-scaffold.ts
@@ -120,14 +120,11 @@ export default defineScenario({
120
120
  "Sanity check that the scaffolded workspace boots into its initial phase.",
121
121
  from: "initial-turn",
122
122
  when: async () => undefined,
123
- then: ({ expect, interactions, players, state }) => {
123
+ then: ({ expect, players, state }) => {
124
124
  const playerIds = players();
125
125
  expect(playerIds).toHaveLength(playerIds.length);
126
126
  expect(playerIds.length).toBeGreaterThanOrEqual(1);
127
127
  expect(state()).toBe("setup");
128
- for (const playerId of playerIds) {
129
- expect(interactions(playerId)).toEqual([]);
130
- }
131
128
  },
132
129
  });
133
130
  `;
@@ -334,7 +331,7 @@ async function writeInitialScenario(projectRoot, mode) {
334
331
  projectRoot,
335
332
  "test/scenarios/smoke-initial-turn.scenario.ts"
336
333
  );
337
- if (existing === null || existing.trim().length === 0 || existing === INITIAL_SCENARIO_CONTENT) {
334
+ if (existing === null || existing.trim().length === 0 || existing.startsWith(GENERATED_SCENARIO_PREFIX)) {
338
335
  await writeWorkspaceTextFile(
339
336
  projectRoot,
340
337
  "test/scenarios/smoke-initial-turn.scenario.ts",
@@ -720,4 +717,4 @@ export {
720
717
  migrateLegacyScenarioImports,
721
718
  resolveStaticAssetRoot
722
719
  };
723
- //# sourceMappingURL=chunk-S4ZQSUPB.mjs.map
720
+ //# sourceMappingURL=chunk-H5L4KK4Y.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/project/static-scaffold.ts","../../src/release/authoring-release-set.generated.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readdir, readFile, rmdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { GameTopologyManifest } from \"@dreamboard-games/sdk/types\";\nimport { AUTHORING_RELEASE_SET } from \"../../release/authoring-release-set.js\";\nimport { REDUCER_TESTING_TYPES_WRAPPER_CONTENT } from \"../../templates/testing-types-content.js\";\nimport {\n MANIFEST_TYPECHECK_CONFIG_FILE,\n PROJECT_CONFIG_FILE,\n PROJECT_DIR_NAME,\n} from \"../../constants.js\";\nimport type { LocalMaintainerRegistryConfig } from \"../../types.js\";\nimport { ensureDir } from \"../../utils/fs.js\";\nimport { materializeManifest } from \"./manifest-authoring.js\";\nimport { isDynamicSeedPath } from \"./scaffold-ownership.js\";\nimport {\n normalizeOwnedProjectPath,\n readWorkspaceTextFile,\n readWorkspaceTextFileIfExists,\n removeWorkspacePath,\n resolveWorkspacePath,\n unlinkWorkspaceFile,\n workspacePathExists,\n writeWorkspaceTextFile,\n} from \"./workspace-path.js\";\nimport {\n FRAMEWORK_PNPM_OVERRIDES,\n FRAMEWORK_REACT_DEPENDENCIES,\n FRAMEWORK_ZOD_VERSION,\n} from \"./framework-dependencies.js\";\n\ntype StaticScaffoldMode = \"new\" | \"update\";\ntype StaticAssetEntry = {\n targetPath: string;\n content: string;\n};\ntype StaticScaffoldOptions = {\n localMaintainerRegistry?: LocalMaintainerRegistryConfig | null;\n};\ntype RootPackageJsonShape = {\n private?: boolean;\n packageManager?: string;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n optionalDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n overrides?: Record<string, unknown>;\n pnpm?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\nconst DREAMBOARD_SYNC_COMMAND = \"dreamboard sync\";\nconst DREAMBOARD_GITIGNORE_BLOCK = [\n \"# Dreamboard local state\",\n \".dreamboard/state.json\",\n \".dreamboard/snapshot.json\",\n \".dreamboard/dev/\",\n \".dreamboard/generated/\",\n \"\",\n].join(\"\\n\");\nconst TESTING_TYPES_STUB =\n \"export function defineScenario(scenario) { return scenario; }\\n\";\nconst GENERATED_TESTING_TYPES_PREFIX = \"// Generated by dreamboard\";\nconst GENERATED_SCENARIO_PREFIX = \"// Generated by dreamboard scaffold.\";\nconst LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT = `export {\n ErrorBoundary,\n type ErrorBoundaryProps,\n} from \"@dreamboard-games/sdk/ui\";\nexport { PluginRuntime, type PluginRuntimeProps } from \"@dreamboard-games/sdk/runtime\";\n`;\nconst OLD_LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT = [\n \"export {\",\n \" ErrorBoundary,\",\n \" PluginRuntime,\",\n \" type ErrorBoundaryProps,\",\n \" type PluginRuntimeProps,\",\n `} from \"@dreamboard/ui-${\"sdk\"}\";`,\n \"\",\n].join(\"\\n\");\nconst OLD_PUBLIC_DREAMBOARD_COMPONENT_INDEX_CONTENT = [\n \"export {\",\n \" ErrorBoundary,\",\n \" PluginRuntime,\",\n \" type ErrorBoundaryProps,\",\n \" type PluginRuntimeProps,\",\n `} from \"@dreamboard-games/ui-${\"sdk\"}\";`,\n \"\",\n].join(\"\\n\");\nconst INITIAL_SCENARIO_CONTENT = `${GENERATED_SCENARIO_PREFIX}\nimport { defineScenario } from \"../testing-types\";\n\nexport default defineScenario({\n id: \"smoke-initial-turn\",\n description:\n \"Sanity check that the scaffolded workspace boots into its initial phase.\",\n from: \"initial-turn\",\n when: async () => undefined,\n then: ({ expect, players, state }) => {\n const playerIds = players();\n expect(playerIds).toHaveLength(playerIds.length);\n expect(playerIds.length).toBeGreaterThanOrEqual(1);\n expect(state()).toBe(\"setup\");\n },\n});\n`;\nconst STATIC_ASSET_ROOT = resolveStaticAssetRoot();\nconst SDK_DEPENDENCY_RANGES = {\n \"@dreamboard-games/sdk\": AUTHORING_RELEASE_SET.packages.sdk.version,\n} as const;\nconst DEV_HOST_DEPENDENCY_RANGES = {\n \"@dreamboard-games/dev-host\": AUTHORING_RELEASE_SET.packages.devHost.version,\n} as const;\nconst DREAMBOARD_PACKAGE_OVERRIDES = {\n \"@dreamboard-games/api-client\":\n AUTHORING_RELEASE_SET.packages.apiClient.version,\n \"@dreamboard-games/dev-host\": AUTHORING_RELEASE_SET.packages.devHost.version,\n \"@dreamboard-games/sdk\": AUTHORING_RELEASE_SET.packages.sdk.version,\n} as const;\n\nconst FRAMEWORK_SCRIPTS = {\n dev: \"dreamboard dev\",\n \"test:ui\":\n \"tsx --tsconfig test/tsconfig.tsx-runtime.json --test test/ui/**/*.test.tsx\",\n typecheck: `tsc --noEmit -p ${MANIFEST_TYPECHECK_CONFIG_FILE} && tsc --noEmit -p app/tsconfig.json && tsc --noEmit -p ui/tsconfig.json`,\n \"typecheck:manifest\": `tsc --noEmit -p ${MANIFEST_TYPECHECK_CONFIG_FILE}`,\n \"typecheck:app\": \"tsc --noEmit -p app/tsconfig.json\",\n \"typecheck:ui\": \"tsc --noEmit -p ui/tsconfig.json\",\n} as const;\nconst SHARED_DEPENDENCIES = {\n ...FRAMEWORK_REACT_DEPENDENCIES,\n} as const;\nconst ROOT_APP_DEPENDENCIES = {\n zod: FRAMEWORK_ZOD_VERSION,\n} as const;\nconst SHARED_DEV_DEPENDENCIES = {\n typescript: \"^5.9.2\",\n \"@types/node\": \"^24.5.2\",\n \"@types/react\": \"^19.0.0\",\n \"@types/react-dom\": \"^19.0.0\",\n csstype: \"^3.1.3\",\n tsx: \"^4.20.5\",\n} as const;\n\nexport async function scaffoldStaticWorkspace(\n projectRoot: string,\n mode: StaticScaffoldMode,\n options: StaticScaffoldOptions = {},\n): Promise<void> {\n await writeFrameworkStaticFiles(projectRoot, mode, options);\n await ensureDreamboardGitignore(projectRoot);\n await writeManifestTypecheckTsconfig(projectRoot);\n await removeLegacyVendoredSdkPaths(projectRoot);\n await removeLegacyDreamboardComponentPath(projectRoot);\n\n await ensureDir(resolveWorkspacePath(projectRoot, \"test/bases\"));\n await ensureDir(resolveWorkspacePath(projectRoot, \"test/scenarios\"));\n await ensureDir(resolveWorkspacePath(projectRoot, \"test/generated\"));\n\n await writeTestReadme(projectRoot);\n await writeGeneratedTestingStubs(projectRoot, mode);\n const initialTestPlayerCount = await inferInitialTestPlayerCount(projectRoot);\n await writeInitialBase(projectRoot, mode, initialTestPlayerCount);\n await writeInitialScenario(projectRoot, mode);\n await writeTestingTypes(projectRoot, mode);\n await writeTestTsconfig(projectRoot);\n\n if (await workspacePathExists(projectRoot, \"test/testing-types.d.ts\")) {\n await unlinkWorkspaceFile(projectRoot, \"test/testing-types.d.ts\");\n }\n if (await workspacePathExists(projectRoot, \"test/base-scenarios.json\")) {\n await unlinkWorkspaceFile(projectRoot, \"test/base-scenarios.json\");\n }\n\n await migrateLegacyScenarioImports(projectRoot);\n}\n\nasync function ensureDreamboardGitignore(projectRoot: string): Promise<void> {\n const existing = await readWorkspaceTextFileIfExists(\n projectRoot,\n \".gitignore\",\n );\n if (existing?.includes(\".dreamboard/state.json\")) {\n return;\n }\n await writeWorkspaceTextFile(\n projectRoot,\n \".gitignore\",\n `${existing ? `${existing.trimEnd()}\\n\\n` : \"\"}${DREAMBOARD_GITIGNORE_BLOCK}`,\n );\n}\n\nexport async function assertCliStaticScaffoldComplete(\n projectRoot: string,\n deletedPaths: readonly string[] = [],\n): Promise<void> {\n const expectedEntries = await getExpectedStaticEntries(projectRoot);\n const missingOrBlankPaths: string[] = [];\n\n for (const entry of expectedEntries) {\n const content = await readWorkspaceTextFileIfExists(\n projectRoot,\n entry.targetPath,\n );\n\n if (content === null || content.trim().length === 0) {\n missingOrBlankPaths.push(entry.targetPath);\n }\n }\n\n const staticPaths = new Set(expectedEntries.map((entry) => entry.targetPath));\n const deletedStaticPaths = deletedPaths\n .map(normalizeOwnedProjectPath)\n .filter(\n (filePath): filePath is string =>\n filePath !== null && staticPaths.has(filePath),\n )\n .sort();\n\n if (missingOrBlankPaths.length === 0 && deletedStaticPaths.length === 0) {\n return;\n }\n\n const problems: string[] = [];\n if (missingOrBlankPaths.length > 0) {\n problems.push(\n `missing or blank: ${summarizePaths(missingOrBlankPaths.sort())}`,\n );\n }\n if (deletedStaticPaths.length > 0) {\n problems.push(`deleted: ${summarizePaths(deletedStaticPaths)}`);\n }\n\n throw new Error(\n `CLI static scaffold is incomplete (${problems.join(\"; \")}). Run \\`${DREAMBOARD_SYNC_COMMAND}\\` to refresh the scaffold before compiling.`,\n );\n}\n\nasync function writeFrameworkStaticFiles(\n projectRoot: string,\n mode: StaticScaffoldMode,\n options: StaticScaffoldOptions,\n): Promise<void> {\n const assetEntries = await getStaticAssetEntries();\n\n for (const entry of assetEntries) {\n // Dynamic seed files are user-customizable: only write them on first\n // scaffold; preserve existing content on subsequent updates.\n if (mode === \"update\" && isDynamicSeedPath(entry.targetPath)) {\n const existing = await readWorkspaceTextFileIfExists(\n projectRoot,\n entry.targetPath,\n );\n if (existing !== null && existing.trim().length > 0) {\n continue;\n }\n }\n await writeWorkspaceTextFile(projectRoot, entry.targetPath, entry.content);\n }\n\n for (const entry of await getDynamicStaticEntries(\n projectRoot,\n mode,\n options,\n )) {\n await writeWorkspaceTextFile(projectRoot, entry.targetPath, entry.content);\n }\n\n if (!options.localMaintainerRegistry) {\n await removeWorkspacePath(projectRoot, \".npmrc\", { force: true });\n }\n}\n\nasync function removeLegacyVendoredSdkPaths(\n projectRoot: string,\n): Promise<void> {\n await removeWorkspacePath(projectRoot, \"app/sdk\", {\n recursive: true,\n force: true,\n });\n await removeWorkspacePath(projectRoot, \"ui/sdk\", {\n recursive: true,\n force: true,\n });\n}\n\nasync function removeLegacyDreamboardComponentPath(\n projectRoot: string,\n): Promise<void> {\n const legacyIndexProjectPath = \"ui/components/dreamboard/index.ts\";\n const legacyDirPath = resolveWorkspacePath(\n projectRoot,\n \"ui/components/dreamboard\",\n );\n const existing = await readWorkspaceTextFileIfExists(\n projectRoot,\n legacyIndexProjectPath,\n );\n\n const removableLegacyContents = new Set([\n LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT.trim(),\n OLD_LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT.trim(),\n OLD_PUBLIC_DREAMBOARD_COMPONENT_INDEX_CONTENT.trim(),\n ]);\n\n if (existing === null || !removableLegacyContents.has(existing.trim())) {\n return;\n }\n\n await unlinkWorkspaceFile(projectRoot, legacyIndexProjectPath);\n const remainingEntries = await readdir(legacyDirPath).catch(() => []);\n if (remainingEntries.length === 0) {\n await rmdir(legacyDirPath);\n }\n}\n\nasync function writeTestReadme(projectRoot: string): Promise<void> {\n await writeWorkspaceTextFile(\n projectRoot,\n \"test/README.md\",\n \"# Dreamboard Test Workspace\\n\\nTypeScript bases live in `test/bases/*.base.ts` and scenarios live in `test/scenarios/*.scenario.ts`.\\n\\n1. Define reusable seeded bases with `defineBase({ id, seed, players, setupProfileId?, setup })`.\\n2. Define scenarios with `defineScenario({ id, from, when, then })`.\\n3. Scenario assertions can read `players()`, `state()`, `view(playerId)`, and `interactions(playerId)`.\\n4. Generate deterministic base snapshots: `dreamboard test generate`.\\n5. Run tests: `dreamboard test run`.\\n\\nImport test helpers from `../testing-types`.\\n\\nGenerated artifacts are written to `test/generated/*` and should not be edited manually.\\n\",\n );\n}\n\nasync function writeInitialBase(\n projectRoot: string,\n mode: StaticScaffoldMode,\n players: number,\n): Promise<void> {\n if (mode === \"update\") {\n return;\n }\n\n await writeWorkspaceTextFile(\n projectRoot,\n \"test/bases/initial-turn.base.ts\",\n `import { defineBase } from \"../testing-types\";\n\nexport default defineBase({\n id: \"initial-turn\",\n seed: 1337,\n players: ${players},\n setup: async () => undefined,\n});\n`,\n );\n}\n\nasync function writeInitialScenario(\n projectRoot: string,\n mode: StaticScaffoldMode,\n): Promise<void> {\n if (mode === \"new\") {\n await writeWorkspaceTextFile(\n projectRoot,\n \"test/scenarios/smoke-initial-turn.scenario.ts\",\n INITIAL_SCENARIO_CONTENT,\n );\n return;\n }\n\n const existing = await readWorkspaceTextFileIfExists(\n projectRoot,\n \"test/scenarios/smoke-initial-turn.scenario.ts\",\n );\n if (\n existing === null ||\n existing.trim().length === 0 ||\n existing.startsWith(GENERATED_SCENARIO_PREFIX)\n ) {\n await writeWorkspaceTextFile(\n projectRoot,\n \"test/scenarios/smoke-initial-turn.scenario.ts\",\n INITIAL_SCENARIO_CONTENT,\n );\n }\n}\n\nasync function writeTestingTypes(\n projectRoot: string,\n mode: StaticScaffoldMode,\n): Promise<void> {\n if (mode === \"new\") {\n await writeWorkspaceTextFile(\n projectRoot,\n \"test/testing-types.ts\",\n REDUCER_TESTING_TYPES_WRAPPER_CONTENT,\n );\n return;\n }\n\n const existing = await readWorkspaceTextFileIfExists(\n projectRoot,\n \"test/testing-types.ts\",\n );\n if (shouldRefreshGeneratedTestingTypes(existing)) {\n await writeWorkspaceTextFile(\n projectRoot,\n \"test/testing-types.ts\",\n REDUCER_TESTING_TYPES_WRAPPER_CONTENT,\n );\n }\n}\n\nasync function writeGeneratedTestingStubs(\n projectRoot: string,\n mode: StaticScaffoldMode,\n): Promise<void> {\n const header = \"// Generated by dreamboard scaffold. Do not edit by hand.\\n\";\n await writeGeneratedTestingStubFile(\n projectRoot,\n \"test/generated/base-states.generated.ts\",\n `${header}export const BASE_STATES = {} as const;\\nexport const BASE_STATES_CONTRACT_FINGERPRINT = undefined;\\n`,\n mode,\n );\n await writeGeneratedTestingStubFile(\n projectRoot,\n \"test/generated/base-states.generated.d.ts\",\n `${header}export declare const BASE_STATES: Record<string, unknown>;\\nexport declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;\\n`,\n mode,\n );\n await writeGeneratedTestingStubFile(\n projectRoot,\n \"test/generated/testing-contract.ts\",\n `${header}export type BaseId = string;\\nexport type GameView = unknown;\\nexport type InteractionId = string;\\nexport type InteractionParamsOf<_Id extends string> = Record<string, unknown>;\\nexport type PhaseName = string;\\nexport type PlayerId = string;\\nexport type RejectionCode = string;\\nexport type StateName = string;\\nexport type TestRunner = \"reducer\" | \"remote\" | \"browser\";\\nexport type ViewByPhase = Record<string, GameView>;\\nexport type WorkspaceStageName<_Phase extends string = string> = string;\\nexport type Expectation = { [matcher: string]: (...args: unknown[]) => unknown; not: Expectation };\\nexport type ExpectFn = (actual: unknown) => Expectation;\\nexport type InteractionExplanation = { interactionId: string; phase: string; step: string | null; availability: \"available\" | \"notYourTurn\" | \"wrongPhase\" | \"wrongStep\" | \"blocked\"; rules: readonly { ruleId: string; outcome: \"passed\" | \"failed\" | \"notEvaluated\"; errorCode?: string; message?: string; }[]; actor: { required: readonly string[]; playerIsActor: boolean }; inputs: readonly { key: string; kind: string; eligibleCount: number | \"lazy\"; }[]; };\\nexport interface InteractionDescriptorFor<Id extends string = string> { interactionId: Id; [key: string]: unknown; }\\nexport interface ScenarioGameApi { start(): Promise<void>; submit<Id extends InteractionId>(playerId: PlayerId, interactionId: Id, params?: InteractionParamsOf<Id>): Promise<void>; }\\nexport interface BaseContext { game: ScenarioGameApi; players(): readonly PlayerId[]; seat(index: number): PlayerId; }\\nexport interface SharedScenarioContext { game: ScenarioGameApi; players(): readonly PlayerId[]; seat(index: number): PlayerId; state(): StateName; view(playerId: PlayerId): GameView; interactions(playerId: PlayerId): readonly InteractionDescriptorFor[]; explain(playerId: PlayerId, interactionId: InteractionId): InteractionExplanation; expect: ExpectFn; }\\nexport type ScenarioContext<Phase extends PhaseName | undefined = undefined> = Omit<SharedScenarioContext, \"state\" | \"view\"> & { state(): Phase extends PhaseName ? Phase : StateName; view(playerId: PlayerId): Phase extends PhaseName ? ViewByPhase[Phase] : GameView; };\\nexport type ScenarioThenContext<_Runners extends readonly TestRunner[] = readonly [\"reducer\"], Phase extends PhaseName | undefined = undefined> = ScenarioContext<Phase>;\\nexport interface BaseDefinition { id: string; seed?: number; players?: number; setupProfileId?: string; extends?: BaseId | string; setup: (ctx: BaseContext) => void | Promise<void>; }\\nexport interface ScenarioDefinition<Runners extends readonly TestRunner[] = readonly [\"reducer\"], Phase extends PhaseName | undefined = undefined> { id: string; description?: string; from: BaseId | string; runners?: Runners; phase?: Phase; stage?: Phase extends PhaseName ? WorkspaceStageName<Phase> : never; when: (ctx: ScenarioContext<Phase>) => void | Promise<void>; then: (ctx: ScenarioThenContext<Runners, Phase>) => void | Promise<void>; }\\n`,\n mode,\n );\n await writeGeneratedTestingStubFile(\n projectRoot,\n \"test/generated/scenario-manifest.generated.ts\",\n `${header}export const SCENARIO_MANIFEST = [] as const;\\n`,\n mode,\n );\n}\n\nasync function writeGeneratedTestingStubFile(\n projectRoot: string,\n projectPath: string,\n content: string,\n mode: StaticScaffoldMode,\n): Promise<void> {\n if (mode === \"new\") {\n await writeWorkspaceTextFile(projectRoot, projectPath, content);\n return;\n }\n\n const existing = await readWorkspaceTextFileIfExists(\n projectRoot,\n projectPath,\n );\n if (\n existing === null ||\n existing.trim().length === 0 ||\n existing.startsWith(GENERATED_SCENARIO_PREFIX)\n ) {\n await writeWorkspaceTextFile(projectRoot, projectPath, content);\n }\n}\n\nasync function writeTestTsconfig(projectRoot: string): Promise<void> {\n await writeWorkspaceTextFile(\n projectRoot,\n \"test/tsconfig.json\",\n `${JSON.stringify(\n {\n compilerOptions: {\n target: \"ES2022\",\n module: \"ESNext\",\n moduleResolution: \"bundler\",\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n noEmit: true,\n },\n include: [\n \"./**/*.ts\",\n \"./**/*.d.ts\",\n \"../shared/**/*.ts\",\n \"../shared/**/*.d.ts\",\n ],\n },\n null,\n 2,\n )}\\n`,\n );\n}\n\nasync function writeManifestTypecheckTsconfig(\n projectRoot: string,\n): Promise<void> {\n await writeWorkspaceTextFile(\n projectRoot,\n MANIFEST_TYPECHECK_CONFIG_FILE,\n `${JSON.stringify(\n {\n compilerOptions: {\n target: \"ES2022\",\n module: \"ESNext\",\n moduleResolution: \"bundler\",\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n noEmit: true,\n allowImportingTsExtensions: true,\n },\n include: [\"./manifest.ts\"],\n },\n null,\n 2,\n )}\\n`,\n );\n}\n\nexport async function migrateLegacyScenarioImports(\n projectRoot: string,\n): Promise<void> {\n if (!(await workspacePathExists(projectRoot, \"test/scenarios\"))) return;\n\n const scenariosRoot = resolveWorkspacePath(projectRoot, \"test/scenarios\");\n const scenarioFiles = await collectScenarioFiles(scenariosRoot);\n for (const filePath of scenarioFiles) {\n const projectPath = toWorkspaceProjectPath(projectRoot, filePath);\n const content = await readWorkspaceTextFile(projectRoot, projectPath);\n if (!content.includes(\"@dreamboard/cli/testing\")) continue;\n\n const relativeToTestingTypes = normalizeImportPath(\n path.relative(\n path.dirname(filePath),\n path.join(projectRoot, \"test\", \"testing-types\"),\n ),\n );\n\n const migrated = content\n .replaceAll('\"@dreamboard/cli/testing\"', `\"${relativeToTestingTypes}\"`)\n .replaceAll(\"'@dreamboard/cli/testing'\", `'${relativeToTestingTypes}'`);\n\n if (migrated !== content) {\n await writeWorkspaceTextFile(projectRoot, projectPath, migrated);\n }\n }\n}\n\nfunction shouldRefreshGeneratedTestingTypes(\n existingContent: string | null,\n): boolean {\n if (existingContent === null || existingContent.trim().length === 0) {\n return true;\n }\n if (existingContent === TESTING_TYPES_STUB) {\n return true;\n }\n return existingContent.startsWith(GENERATED_TESTING_TYPES_PREFIX);\n}\n\nasync function inferInitialTestPlayerCount(\n projectRoot: string,\n): Promise<number> {\n if (!(await workspacePathExists(projectRoot, \"manifest.ts\"))) {\n return 4;\n }\n\n try {\n const manifest = await materializeManifest(projectRoot);\n return manifest.players.optimalPlayers ?? manifest.players.minPlayers ?? 4;\n } catch {\n return 4;\n }\n}\n\nasync function collectScenarioFiles(rootDir: string): Promise<string[]> {\n const files: string[] = [];\n const stack = [rootDir];\n\n while (stack.length > 0) {\n const dir = stack.pop();\n if (!dir) continue;\n\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n stack.push(fullPath);\n } else if (entry.isFile() && entry.name.endsWith(\".ts\")) {\n files.push(fullPath);\n }\n }\n }\n\n return files;\n}\n\nexport function resolveStaticAssetRoot(\n importUrl: string = import.meta.url,\n): string {\n const candidates = [\n fileURLToPath(new URL(\"../../scaffold/assets/static/\", importUrl)),\n fileURLToPath(new URL(\"./scaffold/assets/static/\", importUrl)),\n fileURLToPath(new URL(\"../scaffold/assets/static/\", importUrl)),\n ];\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n throw new Error(\n `Unable to locate CLI static scaffold assets. Checked: ${candidates.join(\", \")}`,\n );\n}\n\nasync function getStaticAssetEntries(): Promise<StaticAssetEntry[]> {\n const files = await walkFiles(STATIC_ASSET_ROOT);\n const entries: StaticAssetEntry[] = [];\n\n for (const filePath of files) {\n const targetPath = normalizeOwnedProjectPath(\n path.relative(STATIC_ASSET_ROOT, filePath).replaceAll(path.sep, \"/\"),\n );\n if (targetPath === null) {\n throw new Error(`Unsafe static scaffold asset path: ${filePath}`);\n }\n entries.push({\n targetPath,\n content: await readFile(filePath, \"utf8\"),\n });\n }\n\n entries.sort((left, right) =>\n left.targetPath.localeCompare(right.targetPath),\n );\n return entries;\n}\n\nasync function getDynamicStaticEntries(\n projectRoot: string,\n mode: StaticScaffoldMode,\n options: StaticScaffoldOptions = {},\n): Promise<StaticAssetEntry[]> {\n const entries: StaticAssetEntry[] = [\n {\n targetPath: \"package.json\",\n content: await buildRootPackageJson(projectRoot, mode, options),\n },\n {\n targetPath: \"ui/package.json\",\n content: buildUiPackageJson(),\n },\n ];\n\n if (options.localMaintainerRegistry) {\n entries.push({\n targetPath: \".npmrc\",\n content: buildWorkspaceNpmrc(options.localMaintainerRegistry.registryUrl),\n });\n }\n\n return entries;\n}\n\nasync function getExpectedStaticEntries(\n projectRoot: string,\n): Promise<StaticAssetEntry[]> {\n const entries = [\n ...(await getStaticAssetEntries()).filter(\n (entry) => entry.targetPath !== \".npmrc\",\n ),\n ...(await getDynamicStaticEntries(projectRoot, \"update\")),\n ];\n entries.sort((left, right) =>\n left.targetPath.localeCompare(right.targetPath),\n );\n return entries;\n}\n\nasync function walkFiles(rootDir: string): Promise<string[]> {\n const files: string[] = [];\n const stack = [rootDir];\n\n while (stack.length > 0) {\n const currentDir = stack.pop();\n if (!currentDir) continue;\n\n const entries = await readdir(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n stack.push(fullPath);\n } else if (entry.isFile()) {\n files.push(fullPath);\n }\n }\n }\n\n files.sort((left, right) => left.localeCompare(right));\n return files;\n}\n\nasync function buildRootPackageJson(\n projectRoot: string,\n mode: StaticScaffoldMode,\n options: StaticScaffoldOptions,\n): Promise<string> {\n const sdkPackageRanges = {\n ...SDK_DEPENDENCY_RANGES,\n ...(options.localMaintainerRegistry?.packages ?? {}),\n };\n const existingPackageJson =\n mode === \"update\" &&\n (await workspacePathExists(projectRoot, \"package.json\"))\n ? (JSON.parse(\n await readWorkspaceTextFile(projectRoot, \"package.json\"),\n ) as RootPackageJsonShape)\n : null;\n const {\n dreamboardFrameworkVersion: _legacyFrameworkVersion,\n ...existingPackageJsonWithoutLegacyVersion\n } = existingPackageJson ?? {};\n const frameworkDependencies = {\n \"@dreamboard-games/sdk\":\n sdkPackageRanges[\"@dreamboard-games/sdk\"] ??\n SDK_DEPENDENCY_RANGES[\"@dreamboard-games/sdk\"],\n ...SHARED_DEPENDENCIES,\n ...ROOT_APP_DEPENDENCIES,\n };\n const frameworkDevDependencies = {\n ...SHARED_DEV_DEPENDENCIES,\n ...DEV_HOST_DEPENDENCY_RANGES,\n };\n const nextPackageJson: RootPackageJsonShape = {\n ...existingPackageJsonWithoutLegacyVersion,\n private: true,\n packageManager: AUTHORING_RELEASE_SET.packageManager,\n scripts: {\n ...(existingPackageJson?.scripts ?? {}),\n ...FRAMEWORK_SCRIPTS,\n },\n dependencies: {\n ...(existingPackageJson?.dependencies ?? {}),\n ...frameworkDependencies,\n },\n devDependencies: {\n ...(existingPackageJson?.devDependencies ?? {}),\n ...frameworkDevDependencies,\n },\n pnpm: mergePnpmConfig(existingPackageJson?.pnpm),\n };\n return `${JSON.stringify(nextPackageJson, null, 2)}\\n`;\n}\n\nfunction mergePnpmConfig(\n existingPnpm: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const existingOverrides =\n existingPnpm?.overrides &&\n typeof existingPnpm.overrides === \"object\" &&\n !Array.isArray(existingPnpm.overrides)\n ? (existingPnpm.overrides as Record<string, unknown>)\n : {};\n return {\n ...(existingPnpm ?? {}),\n overrides: {\n ...existingOverrides,\n ...FRAMEWORK_PNPM_OVERRIDES,\n ...DREAMBOARD_PACKAGE_OVERRIDES,\n },\n };\n}\n\nfunction buildWorkspaceNpmrc(registryUrl: string): string {\n return `@dreamboard-games:registry=${registryUrl}\\n`;\n}\n\nfunction buildUiPackageJson(): string {\n return `${JSON.stringify(\n {\n private: true,\n dependencies: SHARED_DEPENDENCIES,\n devDependencies: SHARED_DEV_DEPENDENCIES,\n },\n null,\n 2,\n )}\\n`;\n}\n\nfunction normalizeImportPath(relativePath: string): string {\n const normalized = relativePath.replaceAll(\"\\\\\", \"/\");\n if (normalized.startsWith(\".\")) return normalized;\n return `./${normalized}`;\n}\n\nfunction toWorkspaceProjectPath(projectRoot: string, filePath: string): string {\n const relativePath = path\n .relative(path.resolve(projectRoot), path.resolve(filePath))\n .replaceAll(path.sep, \"/\");\n const projectPath = normalizeOwnedProjectPath(relativePath);\n if (projectPath === null) {\n throw new Error(`Unsafe project path: ${relativePath}`);\n }\n return projectPath;\n}\n\nfunction summarizePaths(paths: readonly string[]): string {\n const maxShown = 5;\n const shown = paths.slice(0, maxShown).join(\", \");\n if (paths.length <= maxShown) return shown;\n return `${shown}, and ${paths.length - maxShown} more`;\n}\n","// Generated by scripts/generate-authoring-release-set.ts. Do not edit by hand.\nimport type { AuthoringReleaseSetV1 } from \"./authoring-release-set.js\";\n\nexport const AUTHORING_RELEASE_SET = {\n \"schemaVersion\": 1,\n \"channel\": \"public\",\n \"packages\": {\n \"cli\": {\n \"name\": \"@dreamboard-games/cli\",\n \"version\": \"0.1.30-alpha.19\"\n },\n \"sdk\": {\n \"name\": \"@dreamboard-games/sdk\",\n \"version\": \"0.4.0-alpha.6\"\n },\n \"apiClient\": {\n \"name\": \"@dreamboard-games/api-client\",\n \"version\": \"0.3.0-alpha.4\"\n },\n \"devHost\": {\n \"name\": \"@dreamboard-games/dev-host\",\n \"version\": \"0.1.30-alpha.19\"\n }\n },\n \"protocols\": {\n \"authoringAdapter\": 1,\n \"devHost\": 1,\n \"verifier\": 1\n },\n \"schemas\": {\n \"scaffold\": 2,\n \"manifest\": 2,\n \"generatedArtifacts\": 1\n },\n \"registry\": {\n \"kind\": \"public-npm\",\n \"portable\": true\n },\n \"packageManager\": \"pnpm@10.4.1\",\n \"releaseSetId\": \"sha256:94f9e944361da712ec7f0b420643d3690e73b93a2c23d3a893b0f4183a93a847\"\n} as const satisfies AuthoringReleaseSetV1;\n"],"mappings":"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","names":[]}
@@ -4,7 +4,7 @@ import {
4
4
  readWorkspaceTextFileIfExists,
5
5
  validateGeneratedArtifacts,
6
6
  writeWorkspaceTextFile
7
- } from "./chunk-WAFBU5U7.mjs";
7
+ } from "./chunk-OJFZVGEL.mjs";
8
8
 
9
9
  // src/services/project/workspace-codegen.ts
10
10
  var STARTER_UI_SEED_FILES = /* @__PURE__ */ new Set([
@@ -24,7 +24,7 @@ function isFrameworkOwnedSetupProfilesSeed(content) {
24
24
  async function applyWorkspaceCodegen(options) {
25
25
  const { projectRoot, manifest } = options;
26
26
  const { adapter } = await loadProjectAuthoringAdapter(projectRoot);
27
- const artifacts = validateGeneratedArtifacts([
27
+ const artifacts = validateGeneratedArtifacts(adapter, [
28
28
  ...adapter.generateWorkspaceArtifacts(manifest),
29
29
  ...adapter.generateTestArtifacts({ manifest })
30
30
  ]);
@@ -86,4 +86,4 @@ async function applyWorkspaceCodegen(options) {
86
86
  export {
87
87
  applyWorkspaceCodegen
88
88
  };
89
- //# sourceMappingURL=chunk-V6AQDR7W.mjs.map
89
+ //# sourceMappingURL=chunk-HUBV22JQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/project/workspace-codegen.ts"],"sourcesContent":["import type { GameTopologyManifest } from \"@dreamboard-games/sdk/types\";\nimport { loadProjectAuthoringAdapter } from \"../project-authoring/loader.js\";\nimport { validateGeneratedArtifacts } from \"../project-authoring/validation.js\";\nimport {\n readWorkspaceTextFileIfExists,\n writeWorkspaceTextFile,\n} from \"./workspace-path.js\";\n\nexport interface WorkspaceCodegenWriteResult {\n written: string[];\n skipped: string[];\n merged: string[];\n}\n\nconst STARTER_UI_SEED_FILES = new Set([\n \"ui/interaction-routes.tsx\",\n \"ui/setup-screen.tsx\",\n \"ui/styles.ts\",\n \"ui/ui-contract-typing-smoke.tsx\",\n]);\nconst SETUP_PROFILES_SEED_MARKER = \"Dreamboard generated setup profile seeds.\";\n\nfunction isFrameworkOwnedSetupProfilesSeed(\n content: string | null | undefined,\n): boolean {\n if (content === null || content === undefined) {\n return false;\n }\n const trimmed = content.trim();\n return trimmed.length === 0 || trimmed.includes(SETUP_PROFILES_SEED_MARKER);\n}\n\nexport async function applyWorkspaceCodegen(options: {\n projectRoot: string;\n manifest: GameTopologyManifest;\n}): Promise<WorkspaceCodegenWriteResult> {\n const { projectRoot, manifest } = options;\n const { adapter } = await loadProjectAuthoringAdapter(projectRoot);\n const artifacts = validateGeneratedArtifacts(adapter, [\n ...adapter.generateWorkspaceArtifacts(manifest),\n ...adapter.generateTestArtifacts({ manifest }),\n ]);\n const authoritativeFiles = new Map(\n artifacts\n .filter((artifact) => artifact.ownership !== \"seed\")\n .map((artifact) => [artifact.path, artifact.content]),\n );\n const seedFiles = new Map(\n artifacts\n .filter((artifact) => artifact.ownership === \"seed\")\n .map((artifact) => [artifact.path, artifact.content]),\n );\n\n const written: string[] = [];\n const skipped: string[] = [];\n const merged: string[] = [];\n const existingUiAppBeforeSeeds = await readWorkspaceTextFileIfExists(\n projectRoot,\n \"ui/App.tsx\",\n );\n const shouldWriteStarterUiSeedFiles =\n existingUiAppBeforeSeeds === null ||\n existingUiAppBeforeSeeds.trim().length === 0;\n\n for (const [relativePath, content] of authoritativeFiles) {\n const existingContent = await readWorkspaceTextFileIfExists(\n projectRoot,\n relativePath,\n );\n await writeWorkspaceTextFile(projectRoot, relativePath, content);\n if (existingContent !== content) {\n written.push(relativePath);\n }\n }\n\n for (const [relativePath, content] of seedFiles) {\n const existingContent = await readWorkspaceTextFileIfExists(\n projectRoot,\n relativePath,\n );\n if (\n STARTER_UI_SEED_FILES.has(relativePath) &&\n !shouldWriteStarterUiSeedFiles &&\n existingContent === null\n ) {\n skipped.push(relativePath);\n continue;\n }\n\n const shouldRefreshFrameworkSeed =\n relativePath === \"app/setup-profiles.ts\" &&\n isFrameworkOwnedSetupProfilesSeed(existingContent);\n\n if (shouldRefreshFrameworkSeed) {\n await writeWorkspaceTextFile(projectRoot, relativePath, content);\n if (existingContent !== content) {\n written.push(relativePath);\n }\n continue;\n }\n\n const hasExistingContent =\n existingContent !== null && existingContent.trim().length > 0;\n if (hasExistingContent) {\n skipped.push(relativePath);\n continue;\n }\n\n await writeWorkspaceTextFile(projectRoot, relativePath, content);\n written.push(relativePath);\n }\n\n written.sort();\n skipped.sort();\n merged.sort();\n return { written, skipped, merged };\n}\n"],"mappings":";;;;;;;;;AAcA,IAAM,wBAAwB,oBAAI,IAAI;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,IAAM,6BAA6B;AAEnC,SAAS,kCACP,SACS;AACT,MAAI,YAAY,QAAQ,YAAY,QAAW;AAC7C,WAAO;AAAA,EACT;AACA,QAAM,UAAU,QAAQ,KAAK;AAC7B,SAAO,QAAQ,WAAW,KAAK,QAAQ,SAAS,0BAA0B;AAC5E;AAEA,eAAsB,sBAAsB,SAGH;AACvC,QAAM,EAAE,aAAa,SAAS,IAAI;AAClC,QAAM,EAAE,QAAQ,IAAI,MAAM,4BAA4B,WAAW;AACjE,QAAM,YAAY,2BAA2B,SAAS;AAAA,IACpD,GAAG,QAAQ,2BAA2B,QAAQ;AAAA,IAC9C,GAAG,QAAQ,sBAAsB,EAAE,SAAS,CAAC;AAAA,EAC/C,CAAC;AACD,QAAM,qBAAqB,IAAI;AAAA,IAC7B,UACG,OAAO,CAAC,aAAa,SAAS,cAAc,MAAM,EAClD,IAAI,CAAC,aAAa,CAAC,SAAS,MAAM,SAAS,OAAO,CAAC;AAAA,EACxD;AACA,QAAM,YAAY,IAAI;AAAA,IACpB,UACG,OAAO,CAAC,aAAa,SAAS,cAAc,MAAM,EAClD,IAAI,CAAC,aAAa,CAAC,SAAS,MAAM,SAAS,OAAO,CAAC;AAAA,EACxD;AAEA,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAoB,CAAC;AAC3B,QAAM,SAAmB,CAAC;AAC1B,QAAM,2BAA2B,MAAM;AAAA,IACrC;AAAA,IACA;AAAA,EACF;AACA,QAAM,gCACJ,6BAA6B,QAC7B,yBAAyB,KAAK,EAAE,WAAW;AAE7C,aAAW,CAAC,cAAc,OAAO,KAAK,oBAAoB;AACxD,UAAM,kBAAkB,MAAM;AAAA,MAC5B;AAAA,MACA;AAAA,IACF;AACA,UAAM,uBAAuB,aAAa,cAAc,OAAO;AAC/D,QAAI,oBAAoB,SAAS;AAC/B,cAAQ,KAAK,YAAY;AAAA,IAC3B;AAAA,EACF;AAEA,aAAW,CAAC,cAAc,OAAO,KAAK,WAAW;AAC/C,UAAM,kBAAkB,MAAM;AAAA,MAC5B;AAAA,MACA;AAAA,IACF;AACA,QACE,sBAAsB,IAAI,YAAY,KACtC,CAAC,iCACD,oBAAoB,MACpB;AACA,cAAQ,KAAK,YAAY;AACzB;AAAA,IACF;AAEA,UAAM,6BACJ,iBAAiB,2BACjB,kCAAkC,eAAe;AAEnD,QAAI,4BAA4B;AAC9B,YAAM,uBAAuB,aAAa,cAAc,OAAO;AAC/D,UAAI,oBAAoB,SAAS;AAC/B,gBAAQ,KAAK,YAAY;AAAA,MAC3B;AACA;AAAA,IACF;AAEA,UAAM,qBACJ,oBAAoB,QAAQ,gBAAgB,KAAK,EAAE,SAAS;AAC9D,QAAI,oBAAoB;AACtB,cAAQ,KAAK,YAAY;AACzB;AAAA,IACF;AAEA,UAAM,uBAAuB,aAAa,cAAc,OAAO;AAC/D,YAAQ,KAAK,YAAY;AAAA,EAC3B;AAEA,UAAQ,KAAK;AACb,UAAQ,KAAK;AACb,SAAO,KAAK;AACZ,SAAO,EAAE,SAAS,SAAS,OAAO;AACpC;","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  toDreamboardApiError
4
- } from "./chunk-G2ECODRB.mjs";
4
+ } from "./chunk-PLXXH5LY.mjs";
5
5
  import {
6
6
  createGameRevision,
7
7
  createProjectSourceBlobUploadSession,
@@ -13,7 +13,7 @@ import {
13
13
  listProjectCompiledResults,
14
14
  queueProjectRevisionCompile,
15
15
  uploadProjectInitialProjection
16
- } from "./chunk-H3XNWKJU.mjs";
16
+ } from "./chunk-7LFDFXLS.mjs";
17
17
  import {
18
18
  external_exports
19
19
  } from "./chunk-JZTH3EMV.mjs";
@@ -278,7 +278,7 @@ var configCommandArgsSchema = configFlagsSchema.extend({
278
278
  scope: external_exports.enum(["global", "workspace"]).optional().default("global")
279
279
  });
280
280
  var authCommandArgsSchema = external_exports.object({
281
- action: external_exports.enum(["set", "clear", "login", "env", "status"]),
281
+ action: external_exports.enum(["set", "clear", "login", "env", "status", "git-credential"]),
282
282
  tokenValue: external_exports.string().optional(),
283
283
  token: external_exports.string().optional(),
284
284
  jwt: external_exports.boolean().optional(),
@@ -615,4 +615,4 @@ export {
615
615
  uploadInitialProjectionSdk,
616
616
  uploadProjectSourceBlobsSdk
617
617
  };
618
- //# sourceMappingURL=chunk-A67WUYN2.mjs.map
618
+ //# sourceMappingURL=chunk-LEWM26XR.mjs.map