@dreamboard-games/cli 0.1.30-alpha.0 → 0.1.30-alpha.2

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 (114) hide show
  1. package/README.md +179 -22
  2. package/dist/{chunk-TSJVWTJO.js → chunk-N7XPNNUI.js} +14 -12
  3. package/dist/chunk-N7XPNNUI.js.map +1 -0
  4. package/dist/chunk-SEGVTWSK.js +44 -0
  5. package/dist/{chunk-3XNJT3RK.js → chunk-TAQKH67O.js} +21279 -35845
  6. package/dist/chunk-TAQKH67O.js.map +1 -0
  7. package/dist/{global-config-UKSWNDTX.js → global-config-S4ZIPECE.js} +3 -3
  8. package/dist/index.js +955 -230
  9. package/dist/index.js.map +1 -1
  10. package/dist/internal.js +3 -4
  11. package/dist/{agent-verifier/keychain-backend-TNOPQV3Z.mjs → keychain-backend-HDF4TZDL.js} +2 -1
  12. package/dist/{agent-verifier/prompt-3BAINGAQ.mjs → prompt-NDV3AE5L.js} +2 -1
  13. package/package.json +6 -6
  14. package/skills/dreamboard/references/building-your-first-game.md +510 -0
  15. package/skills/dreamboard/references/cli.md +104 -0
  16. package/skills/dreamboard/references/game-interface.md +548 -0
  17. package/skills/dreamboard/references/manifest-authoring.md +597 -0
  18. package/skills/dreamboard/references/quickstart.md +66 -0
  19. package/skills/dreamboard/references/reducer.md +864 -0
  20. package/skills/dreamboard/references/rule-authoring.md +147 -0
  21. package/skills/dreamboard/references/testing.md +249 -0
  22. package/skills/dreamboard/scripts/events-extract.mjs +218 -0
  23. package/dist/agent-verifier/agent-workspace-verifier.mjs +0 -227
  24. package/dist/agent-verifier/chunk-2E5P5NWG.mjs +0 -835
  25. package/dist/agent-verifier/chunk-2GBBP27W.mjs +0 -301
  26. package/dist/agent-verifier/chunk-2NZNKIND.mjs +0 -166
  27. package/dist/agent-verifier/chunk-2QMNAVV4.mjs +0 -14522
  28. package/dist/agent-verifier/chunk-2SZHMP6F.mjs +0 -264
  29. package/dist/agent-verifier/chunk-54TAYXUD.mjs +0 -12
  30. package/dist/agent-verifier/chunk-6A5HRJMQ.mjs +0 -3174
  31. package/dist/agent-verifier/chunk-6UUJEYDV.mjs +0 -213
  32. package/dist/agent-verifier/chunk-7653FPGJ.mjs +0 -381
  33. package/dist/agent-verifier/chunk-BVVNBJM4.mjs +0 -221
  34. package/dist/agent-verifier/chunk-CEDUHGNH.mjs +0 -74
  35. package/dist/agent-verifier/chunk-CEQ2VJWN.mjs +0 -149
  36. package/dist/agent-verifier/chunk-CFU5EWIC.mjs +0 -69
  37. package/dist/agent-verifier/chunk-DTMJCPS4.mjs +0 -730
  38. package/dist/agent-verifier/chunk-EIQWDQWJ.mjs +0 -186
  39. package/dist/agent-verifier/chunk-EOQIV6PS.mjs +0 -649
  40. package/dist/agent-verifier/chunk-HBNDKQT5.mjs +0 -8381
  41. package/dist/agent-verifier/chunk-HJFQDSTU.mjs +0 -225
  42. package/dist/agent-verifier/chunk-LI3ZR3BI.mjs +0 -41
  43. package/dist/agent-verifier/chunk-LM3OZLZG.mjs +0 -48
  44. package/dist/agent-verifier/chunk-MINCYHXN.mjs +0 -106
  45. package/dist/agent-verifier/chunk-MRCUP5SW.mjs +0 -128
  46. package/dist/agent-verifier/chunk-PM3SVG6R.mjs +0 -38
  47. package/dist/agent-verifier/chunk-RBDDIIPM.mjs +0 -19
  48. package/dist/agent-verifier/chunk-RJBLBYHX.mjs +0 -1681
  49. package/dist/agent-verifier/chunk-SHUMAVAP.mjs +0 -59
  50. package/dist/agent-verifier/chunk-SYPLYRGB.mjs +0 -2812
  51. package/dist/agent-verifier/chunk-U6OJN7XS.mjs +0 -8092
  52. package/dist/agent-verifier/chunk-VYJTHSYR.mjs +0 -44
  53. package/dist/agent-verifier/chunk-XYDL7GY6.mjs +0 -10
  54. package/dist/agent-verifier/compile-WNCQQVOF.mjs +0 -313
  55. package/dist/agent-verifier/global-config-WX3ZZIVU.mjs +0 -17
  56. package/dist/agent-verifier/local-files-MTPLP62S.mjs +0 -46
  57. package/dist/agent-verifier/local-typecheck-QFYYAZOK.mjs +0 -9
  58. package/dist/agent-verifier/materialize-workspace-EWGZIVOY.mjs +0 -90
  59. package/dist/agent-verifier/project-state-7GR6BQTQ.mjs +0 -32
  60. package/dist/agent-verifier/reducer-bundle-preflight-C73LEXI2.mjs +0 -23
  61. package/dist/agent-verifier/reducer-contract-preflight-22X7DSZW.mjs +0 -10
  62. package/dist/agent-verifier/reducer-native-test-harness-GMWBUISX.mjs +0 -53
  63. package/dist/agent-verifier/static-scaffold-4YEQME5N.mjs +0 -28
  64. package/dist/agent-verifier/sync-LOQAH4RC.mjs +0 -594
  65. package/dist/agent-verifier/test-YOJERVHN.mjs +0 -356
  66. package/dist/agent-verifier/testing-5K2BJYF2.mjs +0 -674
  67. package/dist/agent-verifier/workspace-codegen-JDZJRSDV.mjs +0 -11
  68. package/dist/agent-verifier/workspace-dependencies-HZ6VVS4G.mjs +0 -14
  69. package/dist/chunk-2H7UOFLK.js +0 -11
  70. package/dist/chunk-3XNJT3RK.js.map +0 -1
  71. package/dist/chunk-7FOO4AJI.js +0 -50
  72. package/dist/chunk-7FOO4AJI.js.map +0 -1
  73. package/dist/chunk-TSJVWTJO.js.map +0 -1
  74. package/dist/internal.d.ts +0 -311
  75. package/dist/keychain-backend-JHTXAKWC.js +0 -135
  76. package/dist/prompt-GMZABCJC.js +0 -756
  77. package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +0 -71
  78. package/dist/runtime-packages/ui-host-runtime/src/browser-interaction.ts +0 -139
  79. package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +0 -374
  80. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +0 -266
  81. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +0 -212
  82. package/dist/runtime-packages/ui-host-runtime/src/components/host-primitives.tsx +0 -271
  83. package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +0 -135
  84. package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +0 -5
  85. package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +0 -194
  86. package/dist/runtime-packages/ui-host-runtime/src/gameplay-authority-transport.ts +0 -626
  87. package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +0 -1
  88. package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +0 -1
  89. package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +0 -294
  90. package/dist/runtime-packages/ui-host-runtime/src/index.ts +0 -3
  91. package/dist/runtime-packages/ui-host-runtime/src/logger.ts +0 -11
  92. package/dist/runtime-packages/ui-host-runtime/src/perf.ts +0 -324
  93. package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +0 -195
  94. package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +0 -138
  95. package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +0 -159
  96. package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +0 -551
  97. package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +0 -13
  98. package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +0 -122
  99. package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +0 -26
  100. package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +0 -583
  101. package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +0 -219
  102. package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +0 -117
  103. package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +0 -431
  104. package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +0 -211
  105. package/dist/runtime-packages/ui-host-runtime/src/session-recovery.ts +0 -80
  106. package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +0 -1034
  107. package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +0 -416
  108. package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +0 -184
  109. package/dist/testing-KLSV6CPJ.js +0 -674
  110. package/dist/testing-KLSV6CPJ.js.map +0 -1
  111. /package/dist/{chunk-2H7UOFLK.js.map → chunk-SEGVTWSK.js.map} +0 -0
  112. /package/dist/{global-config-UKSWNDTX.js.map → global-config-S4ZIPECE.js.map} +0 -0
  113. /package/dist/{keychain-backend-JHTXAKWC.js.map → keychain-backend-HDF4TZDL.js.map} +0 -0
  114. /package/dist/{prompt-GMZABCJC.js.map → prompt-NDV3AE5L.js.map} +0 -0
@@ -1,50 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // ../../node_modules/.pnpm/@dreamboard-games+sdk@0.4.0-alpha.0_@types+react-dom@19.2.3_@types+react@19.2.17__@type_b74cbe125b074769500a56e94fa7f664/node_modules/@dreamboard-games/sdk/dist/chunk-PZ5AY32C.js
4
- var __defProp = Object.defineProperty;
5
- var __export = (target, all) => {
6
- for (var name in all)
7
- __defProp(target, name, { get: all[name], enumerable: true });
8
- };
9
-
10
- // ../../node_modules/.pnpm/@dreamboard-games+sdk@0.4.0-alpha.0_@types+react-dom@19.2.3_@types+react@19.2.17__@type_b74cbe125b074769500a56e94fa7f664/node_modules/@dreamboard-games/sdk/dist/chunk-3OZMHZK3.js
11
- var DEFAULT_REMEDY_BY_ARTIFACT = {
12
- "base-states": "run `dreamboard test generate`, then re-run the tests.",
13
- "session-state": "reset the dev session or run `dreamboard test generate`."
14
- };
15
- function artifactLabel(artifact) {
16
- return artifact === "base-states" ? "base states" : "session state";
17
- }
18
- function artifactVerb(artifact) {
19
- return artifact === "base-states" ? "were" : "was";
20
- }
21
- var StaleContractArtifactError = class extends Error {
22
- code = "STALE_CONTRACT_ARTIFACT";
23
- artifact;
24
- expected;
25
- found;
26
- remedy;
27
- constructor(options) {
28
- const remedy = options.remedy ?? DEFAULT_REMEDY_BY_ARTIFACT[options.artifact];
29
- super(
30
- `${artifactLabel(options.artifact)} ${artifactVerb(
31
- options.artifact
32
- )} generated for contract ${options.found} but the current contract is ${options.expected}. Your state or phase schemas changed since the artifact was created. Remedy: ${remedy}`
33
- );
34
- this.name = "StaleContractArtifactError";
35
- this.artifact = options.artifact;
36
- this.expected = options.expected;
37
- this.found = options.found;
38
- this.remedy = remedy;
39
- }
40
- };
41
- function isStaleContractArtifactError(error) {
42
- return error instanceof StaleContractArtifactError || typeof error === "object" && error !== null && error.code === "STALE_CONTRACT_ARTIFACT";
43
- }
44
-
45
- export {
46
- __export,
47
- StaleContractArtifactError,
48
- isStaleContractArtifactError
49
- };
50
- //# sourceMappingURL=chunk-7FOO4AJI.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../node_modules/.pnpm/@dreamboard-games+sdk@0.4.0-alpha.0_@types+react-dom@19.2.3_@types+react@19.2.17__@type_b74cbe125b074769500a56e94fa7f664/node_modules/@dreamboard-games/sdk/dist/chunk-PZ5AY32C.js","../../../node_modules/.pnpm/@dreamboard-games+sdk@0.4.0-alpha.0_@types+react-dom@19.2.3_@types+react@19.2.17__@type_b74cbe125b074769500a56e94fa7f664/node_modules/@dreamboard-games/sdk/src/reducer/stale-contract-artifact-error.ts"],"sourcesContent":["var __defProp = Object.defineProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\n\nexport {\n __export\n};\n//# sourceMappingURL=chunk-PZ5AY32C.js.map","export type StaleContractArtifactKind = \"base-states\" | \"session-state\";\n\nexport type StaleContractArtifactErrorOptions = {\n artifact: StaleContractArtifactKind;\n expected: string;\n found: string;\n remedy?: string;\n};\n\nconst DEFAULT_REMEDY_BY_ARTIFACT: Record<StaleContractArtifactKind, string> = {\n \"base-states\": \"run `dreamboard test generate`, then re-run the tests.\",\n \"session-state\": \"reset the dev session or run `dreamboard test generate`.\",\n};\n\nfunction artifactLabel(artifact: StaleContractArtifactKind): string {\n return artifact === \"base-states\" ? \"base states\" : \"session state\";\n}\n\nfunction artifactVerb(artifact: StaleContractArtifactKind): string {\n return artifact === \"base-states\" ? \"were\" : \"was\";\n}\n\nexport class StaleContractArtifactError extends Error {\n readonly code = \"STALE_CONTRACT_ARTIFACT\";\n readonly artifact: StaleContractArtifactKind;\n readonly expected: string;\n readonly found: string;\n readonly remedy: string;\n\n constructor(options: StaleContractArtifactErrorOptions) {\n const remedy =\n options.remedy ?? DEFAULT_REMEDY_BY_ARTIFACT[options.artifact];\n super(\n `${artifactLabel(options.artifact)} ${artifactVerb(\n options.artifact,\n )} generated for contract ${options.found} but the current contract is ${\n options.expected\n }. ` +\n `Your state or phase schemas changed since the artifact was created. ` +\n `Remedy: ${remedy}`,\n );\n this.name = \"StaleContractArtifactError\";\n this.artifact = options.artifact;\n this.expected = options.expected;\n this.found = options.found;\n this.remedy = remedy;\n }\n}\n\nexport function isStaleContractArtifactError(\n error: unknown,\n): error is StaleContractArtifactError {\n return (\n error instanceof StaleContractArtifactError ||\n (typeof error === \"object\" &&\n error !== null &&\n (error as { code?: unknown }).code === \"STALE_CONTRACT_ARTIFACT\")\n );\n}\n"],"mappings":";;;AAAA,IAAI,YAAY,OAAO;AACvB,IAAI,WAAW,CAAC,QAAQ,QAAQ;AAC9B,WAAS,QAAQ;AACf,cAAU,QAAQ,MAAM,EAAE,KAAK,IAAI,IAAI,GAAG,YAAY,KAAK,CAAC;AAChE;;;ACKA,IAAM,6BAAwE;EAC5E,eAAe;EACf,iBAAiB;AACnB;AAEA,SAAS,cAAc,UAA6C;AAClE,SAAO,aAAa,gBAAgB,gBAAgB;AACtD;AAEA,SAAS,aAAa,UAA6C;AACjE,SAAO,aAAa,gBAAgB,SAAS;AAC/C;AAEO,IAAM,6BAAN,cAAyC,MAAM;EAC3C,OAAO;EACP;EACA;EACA;EACA;EAET,YAAY,SAA4C;AACtD,UAAM,SACJ,QAAQ,UAAU,2BAA2B,QAAQ,QAAQ;AAC/D;MACE,GAAG,cAAc,QAAQ,QAAQ,CAAC,IAAI;QACpC,QAAQ;MACV,CAAC,2BAA2B,QAAQ,KAAK,gCACvC,QAAQ,QACV,iFAEa,MAAM;IACrB;AACA,SAAK,OAAO;AACZ,SAAK,WAAW,QAAQ;AACxB,SAAK,WAAW,QAAQ;AACxB,SAAK,QAAQ,QAAQ;AACrB,SAAK,SAAS;EAChB;AACF;AAEO,SAAS,6BACd,OACqC;AACrC,SACE,iBAAiB,8BAChB,OAAO,UAAU,YAChB,UAAU,QACT,MAA6B,SAAS;AAE7C;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/global-config.ts","../src/constants.ts","../src/utils/fs.ts","../src/utils/atomic-file.ts","../src/config/credential-store.ts"],"sourcesContent":["import os from \"node:os\";\nimport path from \"node:path\";\nimport type { CredentialBackendPreference, GlobalConfig } from \"../types.js\";\nimport { PROJECT_DIR_NAME } from \"../constants.js\";\nimport { ensureDir, readJsonFile } from \"../utils/fs.js\";\nimport { atomicWriteFile } from \"../utils/atomic-file.js\";\nimport { getCredentialFilePath } from \"./credential-store.js\";\n\nfunction normalizeCredentialBackend(\n value: unknown,\n): CredentialBackendPreference | undefined {\n if (value === \"file\" || value === \"keychain\") return value;\n // Tolerate unknown / malformed values rather than refusing to load the\n // whole config - an unrecognised backend name should degrade to \"use\n // the default\" instead of locking the user out of their CLI.\n return undefined;\n}\n\nexport function getGlobalConfigPath(): string {\n return path.join(os.homedir(), PROJECT_DIR_NAME, \"config.json\");\n}\n\n/**\n * Path to the on-disk credential file used by the file backend of\n * `CredentialStore`. Re-exported here to avoid circular / ad-hoc imports\n * in UI surface (`auth status`, `config show`, etc).\n */\nexport function getGlobalAuthPath(): string {\n return getCredentialFilePath();\n}\n\n/**\n * Load non-credential CLI configuration.\n *\n * Note: this function used to also load `authToken` / `refreshToken`\n * from `auth.json` and flatten them onto `GlobalConfig`. That shape\n * enabled the refresh-token-wipe bug: `saveGlobalConfig({ ...config })`\n * without explicit auth fields erased the stored refresh token.\n *\n * Credentials are now owned exclusively by `CredentialStore`. Callers\n * that need them must import `getCredentials()` directly.\n */\nexport async function loadGlobalConfig(): Promise<GlobalConfig> {\n const config = await readJsonFile<GlobalConfig>(getGlobalConfigPath()).catch(\n () => ({}) as GlobalConfig,\n );\n return {\n environment: config.environment,\n credentialBackend: normalizeCredentialBackend(config.credentialBackend),\n };\n}\n\n/**\n * Persist non-credential CLI configuration.\n *\n * This function cannot write credentials, by construction: the\n * `GlobalConfig` type has no credential fields. Credentials must be\n * persisted through `setCredentials` / `clearCredentials` from\n * `credential-store.ts`.\n */\nexport async function saveGlobalConfig(config: GlobalConfig): Promise<void> {\n const configDir = path.join(os.homedir(), PROJECT_DIR_NAME);\n await ensureDir(configDir);\n const normalized: GlobalConfig = {\n environment: config.environment,\n credentialBackend: normalizeCredentialBackend(config.credentialBackend),\n };\n await atomicWriteFile(\n getGlobalConfigPath(),\n `${JSON.stringify(normalized, null, 2)}\\n`,\n { mode: 0o600 },\n );\n}\n","import type { EnvironmentConfig } from \"./types.js\";\n\nexport const DEFAULT_API_BASE_URL = \"https://api.dreamboard.games\";\nexport const DEFAULT_WEB_BASE_URL = \"https://dreamboard.games\";\n\nexport const PROJECT_DIR_NAME = \".dreamboard\";\n\n// Predefined environment configurations\nexport const ENVIRONMENT_CONFIGS: Record<string, EnvironmentConfig> = {\n local: {\n apiBaseUrl: \"http://localhost:8080\",\n webBaseUrl: \"http://localhost:5173\",\n clerkOAuthIssuer: process.env.DREAMBOARD_LOCAL_CLERK_OAUTH_ISSUER,\n clerkOAuthClientId: process.env.DREAMBOARD_LOCAL_CLERK_OAUTH_CLIENT_ID,\n clerkOAuthScope: process.env.DREAMBOARD_LOCAL_CLERK_OAUTH_SCOPE,\n },\n staging: {\n apiBaseUrl: \"https://api-staging.dreamboard.games\",\n webBaseUrl: \"https://staging.dreamboard.games\",\n clerkOAuthIssuer:\n process.env.DREAMBOARD_STAGING_CLERK_OAUTH_ISSUER ??\n process.env.DREAMBOARD_CLERK_OAUTH_ISSUER,\n clerkOAuthClientId:\n process.env.DREAMBOARD_STAGING_CLERK_OAUTH_CLIENT_ID ??\n process.env.DREAMBOARD_CLERK_OAUTH_CLIENT_ID,\n clerkOAuthScope:\n process.env.DREAMBOARD_STAGING_CLERK_OAUTH_SCOPE ??\n process.env.DREAMBOARD_CLERK_OAUTH_SCOPE,\n },\n prod: {\n apiBaseUrl: \"https://api.dreamboard.games\",\n webBaseUrl: \"https://dreamboard.games\",\n clerkOAuthIssuer:\n process.env.DREAMBOARD_PROD_CLERK_OAUTH_ISSUER ??\n process.env.DREAMBOARD_CLERK_OAUTH_ISSUER,\n clerkOAuthClientId:\n process.env.DREAMBOARD_PROD_CLERK_OAUTH_CLIENT_ID ??\n process.env.DREAMBOARD_CLERK_OAUTH_CLIENT_ID,\n clerkOAuthScope:\n process.env.DREAMBOARD_PROD_CLERK_OAUTH_SCOPE ??\n process.env.DREAMBOARD_CLERK_OAUTH_SCOPE,\n },\n};\nexport const PROJECT_CONFIG_FILE = \"project.json\";\nexport const PROJECT_STATE_FILE = \"state.json\";\nexport const SNAPSHOT_FILE = \"snapshot.json\";\nexport const MANIFEST_FILE = \"manifest.ts\";\nexport const GENERATED_DIR_NAME = \"generated\";\nexport const MATERIALIZED_MANIFEST_FILE = `${PROJECT_DIR_NAME}/${GENERATED_DIR_NAME}/manifest.json`;\nexport const MANIFEST_TYPECHECK_CONFIG_FILE = \"manifest.tsconfig.json\";\nexport const RULE_FILE = \"rule.md\";\nexport const DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;\nexport const DEFAULT_TURN_DELAY_MS = 250;\n\nexport const LOCAL_IGNORE_DIRS = new Set([\n \".dreamboard\",\n \".git\",\n \"node_modules\",\n \"dist\",\n]);\n","import { mkdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n await mkdir(dirPath, { recursive: true });\n}\n\nexport async function exists(filePath: string): Promise<boolean> {\n try {\n await stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readTextFile(filePath: string): Promise<string> {\n return readFile(filePath, \"utf8\");\n}\n\nexport async function readTextFileIfExists(\n filePath: string,\n): Promise<string | null> {\n try {\n return await readFile(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n\nexport async function writeTextFile(\n filePath: string,\n content: string,\n): Promise<void> {\n await ensureDir(path.dirname(filePath));\n await writeFile(filePath, content, \"utf8\");\n}\n\nexport async function readJsonFile<T>(filePath: string): Promise<T> {\n const data = await readTextFile(filePath);\n return JSON.parse(data) as T;\n}\n\nexport async function writeJsonFile(\n filePath: string,\n data: unknown,\n): Promise<void> {\n await writeTextFile(filePath, `${JSON.stringify(data, null, 2)}\\n`);\n}\n","/**\n * Primitives for safely mutating local state files owned by the CLI.\n *\n * Two guarantees:\n * - Writes are atomic-ish: we stage the payload in a sibling temp file with\n * the target permissions, fsync the contents, then `rename` over the target.\n * On POSIX `rename` within the same directory is atomic; on Windows it is\n * atomic within the same volume which is always the case for files we write\n * inside `~/.dreamboard`.\n * - We refuse to clobber a file with an empty payload. The original bug that\n * wiped refresh tokens on a failing `sync`/`compile` hinged on `undefined`\n * JSON values being persisted and reloaded as `{}`. Forbidding empty\n * writes here removes that entire failure mode at the primitive level.\n *\n * Additionally, `withFileLock` provides a cross-process advisory lock built on\n * `O_CREAT | O_EXCL` so that parallel CLI invocations (e.g. `dreamboard sync`\n * running while `dreamboard compile` is in flight) serialize around mutations\n * of the same credential state.\n */\n\nimport { constants as fsConstants, promises as fs, type Stats } from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\n\nexport type AtomicWriteOptions = {\n /** File mode applied to the written file (default: 0o600). */\n mode?: number;\n /** Call `fsync` on the temp file before renaming. Default: true. */\n fsync?: boolean;\n};\n\nexport async function atomicWriteFile(\n targetPath: string,\n contents: string,\n options: AtomicWriteOptions = {},\n): Promise<void> {\n if (contents.length === 0) {\n throw new Error(\n `Refusing to atomicWriteFile an empty payload to ${targetPath}`,\n );\n }\n const mode = options.mode ?? 0o600;\n const shouldFsync = options.fsync ?? true;\n const dir = path.dirname(targetPath);\n await fs.mkdir(dir, { recursive: true });\n\n const suffix = crypto.randomBytes(6).toString(\"hex\");\n const tmpPath = `${targetPath}.tmp-${process.pid}-${suffix}`;\n\n const fh = await fs.open(\n tmpPath,\n fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL,\n mode,\n );\n try {\n await fh.writeFile(contents, \"utf8\");\n try {\n await fh.chmod(mode);\n } catch {\n // Some filesystems (e.g. network volumes, Windows) refuse chmod.\n // Ignoring here is safe: the `open` call above already created the\n // file with the requested mode on systems that honor it.\n }\n if (shouldFsync) {\n try {\n await fh.sync();\n } catch {\n // Best-effort. Not all backends (tmpfs on some platforms) support fsync.\n }\n }\n } finally {\n await fh.close();\n }\n\n try {\n await fs.rename(tmpPath, targetPath);\n } catch (err) {\n await fs.unlink(tmpPath).catch(() => undefined);\n throw err;\n }\n}\n\nexport type FileLockOptions = {\n /** Max number of acquisition attempts before giving up. Default: 100. */\n retries?: number;\n /** Minimum backoff between retries in ms. Default: 20. */\n minDelayMs?: number;\n /** Maximum backoff between retries in ms. Default: 200. */\n maxDelayMs?: number;\n /**\n * A lockfile older than this is considered stale and forcibly removed.\n * Guards against crashed processes leaving a permanent lock. Default: 30s.\n */\n staleMs?: number;\n};\n\nexport async function withFileLock<T>(\n lockPath: string,\n fn: () => Promise<T>,\n options: FileLockOptions = {},\n): Promise<T> {\n const retries = options.retries ?? 100;\n const minDelayMs = options.minDelayMs ?? 20;\n const maxDelayMs = options.maxDelayMs ?? 200;\n const staleMs = options.staleMs ?? 30_000;\n\n await fs.mkdir(path.dirname(lockPath), { recursive: true });\n\n let attempt = 0;\n let acquired = false;\n while (!acquired) {\n try {\n const fh = await fs.open(\n lockPath,\n fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL,\n 0o600,\n );\n await fh.writeFile(`${process.pid}\\n`, \"utf8\");\n await fh.close();\n acquired = true;\n break;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"EEXIST\") {\n throw err;\n }\n }\n\n let stat: Stats | null = null;\n try {\n stat = await fs.stat(lockPath);\n } catch {\n continue;\n }\n if (stat !== null) {\n const ageMs = Date.now() - stat.mtimeMs;\n if (ageMs > staleMs) {\n await fs.unlink(lockPath).catch(() => undefined);\n continue;\n }\n }\n\n attempt += 1;\n if (attempt >= retries) {\n throw new Error(\n `Timed out acquiring file lock at ${lockPath} after ${retries} attempts.`,\n );\n }\n const jitter = Math.floor(\n Math.random() * Math.max(1, maxDelayMs - minDelayMs),\n );\n await new Promise((resolve) => setTimeout(resolve, minDelayMs + jitter));\n }\n\n try {\n return await fn();\n } finally {\n await fs.unlink(lockPath).catch(() => undefined);\n }\n}\n","/**\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. The file backend is the default. The OS keychain is opt-in via\n * `credentialBackend: \"keychain\"` in `~/.dreamboard/config.json`\n * because on macOS the first keychain write triggers a login-password\n * prompt, and re-prompts whenever the executing Node binary's code\n * signature changes (e.g. after an `nvm`/`volta` upgrade). Users who\n * want encrypted-at-rest storage can opt in explicitly; everyone else\n * gets a zero-prompt experience.\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/** Fully refreshable session: both tokens required. */\nexport type Credentials = {\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly tokenExpiresAt?: 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 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(): 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(): Promise<void>;\n};\n\ntype DiskShape = Partial<{\n accessToken: string;\n authToken: string;\n refreshToken: string;\n tokenExpiresAt: string;\n clerkOAuthIssuer: string;\n clerkOAuthClientId: string;\n clerkOAuthTokenUrl: string;\n environment: string;\n}>;\n\nexport function getCredentialFilePath(): string {\n return path.join(os.homedir(), PROJECT_DIR_NAME, \"auth.json\");\n}\n\nfunction getCredentialLockPath(): string {\n return `${getCredentialFilePath()}.lock`;\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 = 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: parsed.tokenExpiresAt || 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 authToken: creds.accessToken,\n refreshToken: creds.refreshToken,\n tokenExpiresAt: creds.tokenExpiresAt,\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(): Promise<void> {\n const filePath = getCredentialFilePath();\n try {\n await fs.unlink(filePath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") 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\nlet cachedBackend: CredentialBackend | null = null;\nlet migrationCompleted = false;\nlet backendResolver: BackendResolver = defaultBackendResolver;\n\n/**\n * Default resolver precedence:\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 default keychain-first 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 and remove the file. We only do this when the new backend is\n // empty, so repeated migrations cannot stomp a newer keychain\n // session with a stale file session.\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): 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.\n // Remove the file so it cannot get re-used accidentally.\n await fileCredentialBackend.clear();\n return;\n }\n if (onDisk.accessToken && onDisk.refreshToken) {\n await target.writeFull({\n accessToken: onDisk.accessToken,\n refreshToken: onDisk.refreshToken,\n });\n } else if (onDisk.accessToken) {\n await target.writeAccessOnly(onDisk.accessToken);\n } else {\n return;\n }\n await fileCredentialBackend.clear();\n } catch {\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\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 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 { accessToken, refreshToken };\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(): Promise<void> {\n await withFileLock(getCredentialLockPath(), async () => {\n const backend = await getCredentialBackend();\n await backend.clear();\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: () => backend.clear(),\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}\n"],"mappings":"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","names":["os","path","path","stat","path","fs","path","fs","loadGlobalConfig","path","os"]}
@@ -1,311 +0,0 @@
1
- import { z } from 'zod';
2
- import { CompiledResult } from '@dreamboard-games/api-client';
3
- import { GameTopologyManifest } from '@dreamboard-games/sdk/types';
4
-
5
- type Environment = "local" | "staging" | "prod";
6
- type LocalMaintainerRegistryPackages = {
7
- "@dreamboard-games/api-client"?: string;
8
- "@dreamboard-games/sdk": string;
9
- };
10
- type LocalMaintainerRegistryConfig = {
11
- registryUrl: string;
12
- snapshotId: string;
13
- fingerprint: string;
14
- publishedAt: string;
15
- packages: LocalMaintainerRegistryPackages;
16
- };
17
- type EnvironmentConfig = {
18
- apiBaseUrl: string;
19
- webBaseUrl: string;
20
- clerkOAuthIssuer?: string;
21
- clerkOAuthClientId?: string;
22
- clerkOAuthScope?: string;
23
- };
24
- /**
25
- * Non-credential configuration persisted at `~/.dreamboard/config.json`.
26
- *
27
- * IMPORTANT: `authToken` and `refreshToken` intentionally do NOT live here.
28
- * Credentials are owned exclusively by the `CredentialStore` module
29
- * (`config/credential-store.ts`). Mixing them into `GlobalConfig` was the
30
- * root cause of the refresh-token-wipe bug: `saveGlobalConfig({ ...config })`
31
- * could silently erase stored credentials if the caller forgot to include
32
- * them. With credentials removed from this type, that failure mode is a
33
- * type error.
34
- *
35
- * `credentialBackend` is a *selector*, not a credential: it only says where
36
- * tokens are stored. The default (unset) is `"file"` because the OS keychain
37
- * prompts the user for their login password on first use on macOS (and is
38
- * re-prompted whenever the Node binary signature changes, e.g. after an
39
- * `nvm`/`volta` upgrade). Users who want encrypted-at-rest storage can opt
40
- * in by writing `"credentialBackend": "keychain"` into this file.
41
- */
42
- type CredentialBackendPreference = "file" | "keychain";
43
- type GlobalConfig = {
44
- environment?: Environment;
45
- credentialBackend?: CredentialBackendPreference;
46
- };
47
- type ProjectPendingSyncPhase = "source_revision_created" | "authoring_state_created";
48
- type ProjectPendingAuthoringSync = {
49
- phase: ProjectPendingSyncPhase;
50
- revisionDigest?: string;
51
- authoringStateId?: string;
52
- ruleId?: string;
53
- manifestId?: string;
54
- manifestContentHash?: string;
55
- localManifestContentHash?: string;
56
- sourceRevisionId: string;
57
- sourceTreeHash: string;
58
- };
59
- type ProjectAuthoringState = {
60
- revisionDigest?: string;
61
- authoringStateId?: string;
62
- ruleId?: string;
63
- manifestId?: string;
64
- manifestContentHash?: string;
65
- localManifestContentHash?: string;
66
- sourceRevisionId?: string;
67
- sourceTreeHash?: string;
68
- pendingSync?: ProjectPendingAuthoringSync;
69
- };
70
- type ProjectCompileAttempt = {
71
- resultId?: string;
72
- jobId?: string;
73
- revisionDigest?: string;
74
- authoringStateId: string;
75
- status: "successful" | "failed";
76
- diagnosticsSummary?: string;
77
- };
78
- type ProjectCompileState = {
79
- latestAttempt?: ProjectCompileAttempt;
80
- latestSuccessful?: {
81
- resultId: string;
82
- authoringStateId: string;
83
- revisionDigest?: string;
84
- };
85
- };
86
- type ProjectManifestV2 = {
87
- schemaVersion: 2;
88
- projectId: string;
89
- slug: string;
90
- };
91
- type ProjectEnvironmentBindingV1 = {
92
- deploymentId: string;
93
- ownerScopeId: string;
94
- gameId?: string;
95
- remoteHeadDigest?: string;
96
- jobId?: string;
97
- agentManaged?: boolean;
98
- workspacePrepared?: boolean;
99
- allowCreateGame?: boolean;
100
- environment?: Environment;
101
- authoring?: ProjectAuthoringState;
102
- compile?: ProjectCompileState;
103
- localMaintainerRegistry?: LocalMaintainerRegistryConfig;
104
- apiBaseUrl?: string;
105
- webBaseUrl?: string;
106
- packageManifest?: Record<string, unknown>;
107
- environmentManifest?: Record<string, unknown>;
108
- };
109
- type ProjectConfig = ProjectManifestV2 & Omit<ProjectEnvironmentBindingV1, "gameId"> & {
110
- gameId: string;
111
- bindingKey?: string;
112
- };
113
- /**
114
- * A resolved, read-only snapshot of "what should this CLI invocation do".
115
- *
116
- * `authToken` and `refreshToken` are read-only projections of the active
117
- * credentials at the moment `resolveConfig` runs. They are marked `readonly`
118
- * to discourage reassignment from command code; the refresh path never
119
- * mutates this object and instead goes through `CredentialStore` directly.
120
- * Persisting a `ResolvedConfig` back into `GlobalConfig`/`auth.json` is
121
- * not possible because `GlobalConfig` has no credential fields.
122
- */
123
- type ResolvedConfig = {
124
- readonly environment: Environment;
125
- readonly apiBaseUrl: string;
126
- readonly webBaseUrl: string;
127
- readonly authToken?: string;
128
- readonly refreshToken?: string;
129
- readonly tokenExpiresAt?: string;
130
- readonly clerkOAuthIssuer?: string;
131
- readonly clerkOAuthClientId?: string;
132
- readonly clerkOAuthTokenUrl?: string;
133
- readonly clerkOAuthScope?: string;
134
- readonly authTokenSource?: "global" | "env" | "agent-env" | "flag" | "none";
135
- readonly refreshTokenSource?: "global" | "env" | "none";
136
- };
137
-
138
- declare const configFlagsSchema: z.ZodObject<{
139
- env: z.ZodOptional<z.ZodEnum<{
140
- local: "local";
141
- staging: "staging";
142
- prod: "prod";
143
- }>>;
144
- token: z.ZodOptional<z.ZodString>;
145
- }, z.core.$strip>;
146
- type ConfigFlags = z.infer<typeof configFlagsSchema>;
147
- declare function parseConfigFlags(args: unknown): ConfigFlags;
148
-
149
- /**
150
- * Single writer for the long-lived Dreamboard session credentials.
151
- *
152
- * Design invariants (enforced at the type level and tested in
153
- * `credential-store.test.ts`):
154
- *
155
- * 1. This module is the ONLY place in the CLI that writes credentials to
156
- * disk or the OS keychain. `global-config.ts` used to own both the
157
- * config and the credentials via `saveGlobalConfig`, which made it
158
- * trivial to wipe a refresh token by accident. The `GlobalConfig` type
159
- * no longer carries credentials, so attempting to persist one through
160
- * the config path is a type error.
161
- *
162
- * 2. The mutating surface is intentionally narrow:
163
- * - `setCredentials(c)` for refreshable sessions (both tokens present)
164
- * - `setAccessOnlySession(accessToken)` for the `auth set` / `config set
165
- * --token` power-user path, which has no refresh token by
166
- * construction
167
- * - `clearCredentials()` wipes the file entirely
168
- * There is no "partial update" API. `Credentials` requires both
169
- * `accessToken` and `refreshToken`, so it is impossible to persist a
170
- * half-populated refreshable session.
171
- *
172
- * 3. Writes go through `atomicWriteFile` + `withFileLock`, so a crash or
173
- * interrupt during `dreamboard sync`/`compile` cannot leave `auth.json`
174
- * truncated, and parallel CLI invocations cannot clobber each other's
175
- * rotated refresh tokens.
176
- *
177
- * 4. The on-disk JSON shape for the file backend is kept backward
178
- * compatible: we continue to read/write `authToken` + `refreshToken`
179
- * so existing users are not forced to log in again after this change.
180
- * A newer `accessToken` key is also accepted for read to ease any
181
- * future format bump.
182
- *
183
- * 5. The file backend is the default. The OS keychain is opt-in via
184
- * `credentialBackend: "keychain"` in `~/.dreamboard/config.json`
185
- * because on macOS the first keychain write triggers a login-password
186
- * prompt, and re-prompts whenever the executing Node binary's code
187
- * signature changes (e.g. after an `nvm`/`volta` upgrade). Users who
188
- * want encrypted-at-rest storage can opt in explicitly; everyone else
189
- * gets a zero-prompt experience.
190
- */
191
-
192
- /**
193
- * Raw on-disk snapshot. Either or both fields may be present. The refresh
194
- * coordinator only acts on snapshots that have both tokens populated.
195
- */
196
- type StoredSessionSnapshot = {
197
- readonly accessToken?: string;
198
- readonly refreshToken?: string;
199
- readonly tokenExpiresAt?: string;
200
- readonly clerkOAuthIssuer?: string;
201
- readonly clerkOAuthClientId?: string;
202
- readonly clerkOAuthTokenUrl?: string;
203
- readonly environment?: string;
204
- };
205
- /** Loose read: returns whatever is on disk, including access-only sessions. */
206
- declare function getStoredSession(): Promise<StoredSessionSnapshot | null>;
207
-
208
- /**
209
- * Resolve the effective CLI config for this invocation.
210
- *
211
- * `resolveConfig` is pure and synchronous: it takes pre-loaded inputs
212
- * (global config, flags, optional project config, optional credential
213
- * snapshot) and assembles a read-only `ResolvedConfig`. It intentionally
214
- * does not touch disk or the network - refreshing/persisting credentials
215
- * is the job of `configureClient` + `RefreshCoordinator`.
216
- *
217
- * Passing `credentials = undefined` is equivalent to "no stored session
218
- * for this call", used by contexts that should never inherit the local
219
- * session (e.g. `dreamboard login` before the browser flow).
220
- */
221
- declare function resolveConfig(globalConfig: GlobalConfig, flags: ConfigFlags, project?: ProjectConfig, credentials?: StoredSessionSnapshot | null): ResolvedConfig;
222
- /**
223
- * Configure the API client for the resolved environment, refreshing the
224
- * stored CLI session first if it is close to expiry.
225
- *
226
- * The refresh path never mutates `config`. It goes through
227
- * Clerk OAuth directly and the CredentialStore writes. After a successful
228
- * rotation the HTTP client
229
- * is configured with the rotated access token; on transient failures we
230
- * fall back to the `config.authToken` snapshot (which is why commands
231
- * still see a bearer header and can surface the original error).
232
- */
233
- declare function configureClient(config: ResolvedConfig): Promise<void>;
234
- declare function requireAuth(config: ResolvedConfig): void;
235
- /**
236
- * Common init pattern used by pull, push, status, update, run commands:
237
- * find project root, load config, resolve config, require auth,
238
- * configure client.
239
- */
240
- declare function resolveProjectContext(flags: ConfigFlags, opts?: {
241
- requireAuth?: boolean;
242
- }): Promise<{
243
- projectRoot: string;
244
- projectConfig: ProjectConfig;
245
- config: ResolvedConfig;
246
- }>;
247
-
248
- /**
249
- * Load non-credential CLI configuration.
250
- *
251
- * Note: this function used to also load `authToken` / `refreshToken`
252
- * from `auth.json` and flatten them onto `GlobalConfig`. That shape
253
- * enabled the refresh-token-wipe bug: `saveGlobalConfig({ ...config })`
254
- * without explicit auth fields erased the stored refresh token.
255
- *
256
- * Credentials are now owned exclusively by `CredentialStore`. Callers
257
- * that need them must import `getCredentials()` directly.
258
- */
259
- declare function loadGlobalConfig(): Promise<GlobalConfig>;
260
-
261
- declare const CONFIG_FLAG_ARGS: {
262
- env: {
263
- type: "string";
264
- description: string;
265
- };
266
- token: {
267
- type: "string";
268
- description: string;
269
- };
270
- };
271
-
272
- declare const ENVIRONMENT_CONFIGS: Record<string, EnvironmentConfig>;
273
-
274
- declare function loadProjectConfig(rootDir: string): Promise<ProjectConfig>;
275
- declare function updateProjectState(rootDir: string, config: ProjectConfig): Promise<void>;
276
-
277
- declare function readJsonFile<T>(filePath: string): Promise<T>;
278
- declare function writeJsonFile(filePath: string, data: unknown): Promise<void>;
279
-
280
- declare function findCompiledResultsForAuthoringState(options: {
281
- gameId: string;
282
- authoringStateId: string;
283
- }): Promise<CompiledResult[]>;
284
-
285
- declare function setLatestCompileAttempt(projectConfig: ProjectConfig, attempt: ProjectCompileAttempt): ProjectConfig;
286
-
287
- declare function loadManifest(rootDir: string): Promise<GameTopologyManifest>;
288
-
289
- declare function writeSnapshot(rootDir: string): Promise<void>;
290
-
291
- interface WorkspaceCodegenWriteResult {
292
- written: string[];
293
- skipped: string[];
294
- merged: string[];
295
- }
296
- declare function applyWorkspaceCodegen(options: {
297
- projectRoot: string;
298
- manifest: GameTopologyManifest;
299
- }): Promise<WorkspaceCodegenWriteResult>;
300
-
301
- declare function ensureReducerNativeTestingFiles(projectRoot: string): Promise<void>;
302
-
303
- declare function shortHash(value: string): string;
304
-
305
- /**
306
- * Browser test runner helpers shared with reducer-native-test-harness (browser runner).
307
- * Screenshot / JSON scenario navigation helpers lived in the deleted `run` command.
308
- */
309
- declare function configurePlaywrightBrowsersPath(): void;
310
-
311
- export { CONFIG_FLAG_ARGS, type ConfigFlags, ENVIRONMENT_CONFIGS, type ProjectConfig, type ResolvedConfig, applyWorkspaceCodegen, configureClient, configurePlaywrightBrowsersPath, ensureReducerNativeTestingFiles, findCompiledResultsForAuthoringState, getStoredSession, loadGlobalConfig, loadManifest, loadProjectConfig, parseConfigFlags, readJsonFile, requireAuth, resolveConfig, resolveProjectContext, setLatestCompileAttempt, shortHash, updateProjectState, writeJsonFile, writeSnapshot };
@@ -1,135 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./chunk-2H7UOFLK.js";
3
-
4
- // src/config/keychain-backend.ts
5
- var KEYCHAIN_SERVICE = "dreamboard-cli";
6
- var KEYCHAIN_ACCOUNT = "session";
7
- var cachedModule;
8
- async function loadKeyringModule() {
9
- if (cachedModule !== void 0) return cachedModule;
10
- try {
11
- const mod = await import("@napi-rs/keyring");
12
- cachedModule = mod;
13
- } catch {
14
- cachedModule = null;
15
- }
16
- return cachedModule;
17
- }
18
- function keychainProbe(entry) {
19
- try {
20
- entry.getPassword();
21
- return true;
22
- } catch {
23
- return false;
24
- }
25
- }
26
- function parsePayload(raw) {
27
- if (raw === null || raw === void 0) return null;
28
- const trimmed = raw.trim();
29
- if (trimmed.length === 0) return null;
30
- try {
31
- const parsed = JSON.parse(trimmed);
32
- if (!parsed.accessToken && !parsed.refreshToken) return null;
33
- return {
34
- accessToken: parsed.accessToken || void 0,
35
- refreshToken: parsed.refreshToken || void 0,
36
- tokenExpiresAt: parsed.tokenExpiresAt || void 0,
37
- clerkOAuthIssuer: parsed.clerkOAuthIssuer || void 0,
38
- clerkOAuthClientId: parsed.clerkOAuthClientId || void 0,
39
- clerkOAuthTokenUrl: parsed.clerkOAuthTokenUrl || void 0,
40
- environment: parsed.environment || void 0
41
- };
42
- } catch {
43
- return null;
44
- }
45
- }
46
- function writeFull(entry, creds) {
47
- if (!creds.accessToken || !creds.refreshToken) {
48
- throw new Error(
49
- "Refusing to persist credentials with an empty accessToken or refreshToken."
50
- );
51
- }
52
- const payload = {
53
- accessToken: creds.accessToken,
54
- refreshToken: creds.refreshToken,
55
- tokenExpiresAt: creds.tokenExpiresAt,
56
- clerkOAuthIssuer: creds.clerkOAuthIssuer,
57
- clerkOAuthClientId: creds.clerkOAuthClientId,
58
- clerkOAuthTokenUrl: creds.clerkOAuthTokenUrl,
59
- environment: creds.environment
60
- };
61
- entry.setPassword(JSON.stringify(payload));
62
- }
63
- function writeAccessOnly(entry, accessToken) {
64
- if (!accessToken) {
65
- throw new Error("Refusing to persist an empty access token.");
66
- }
67
- const payload = { accessToken };
68
- entry.setPassword(JSON.stringify(payload));
69
- }
70
- function clear(entry) {
71
- try {
72
- entry.deletePassword();
73
- } catch {
74
- }
75
- }
76
- async function tryKeychainBackend() {
77
- const mod = await loadKeyringModule();
78
- if (!mod) {
79
- return {
80
- available: false,
81
- reason: "@napi-rs/keyring is not installed for this platform"
82
- };
83
- }
84
- let entry;
85
- try {
86
- entry = new mod.Entry(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
87
- } catch (err) {
88
- return {
89
- available: false,
90
- reason: `Failed to construct keyring entry: ${String(err.message ?? err)}`
91
- };
92
- }
93
- if (!keychainProbe(entry)) {
94
- return {
95
- available: false,
96
- reason: "OS keyring is not accessible from this process"
97
- };
98
- }
99
- const backend = {
100
- name: "keychain",
101
- async read() {
102
- try {
103
- return parsePayload(entry.getPassword());
104
- } catch (err) {
105
- const message = String(err.message ?? err);
106
- if (/no matching entry|not found/i.test(message)) {
107
- return null;
108
- }
109
- throw err;
110
- }
111
- },
112
- async writeFull(creds) {
113
- writeFull(entry, creds);
114
- },
115
- async writeAccessOnly(accessToken) {
116
- writeAccessOnly(entry, accessToken);
117
- },
118
- async clear() {
119
- clear(entry);
120
- }
121
- };
122
- return { available: true, backend };
123
- }
124
- function _setKeyringModuleForTests(mod) {
125
- cachedModule = mod;
126
- }
127
- function _resetKeyringModuleForTests() {
128
- cachedModule = void 0;
129
- }
130
- export {
131
- _resetKeyringModuleForTests,
132
- _setKeyringModuleForTests,
133
- tryKeychainBackend
134
- };
135
- //# sourceMappingURL=keychain-backend-JHTXAKWC.js.map