@dreamboard-games/cli 0.1.30-alpha.30 → 0.1.30-alpha.32

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 (39) hide show
  1. package/README.md +8 -8
  2. package/dist/agent-verifier/agent-workspace-verifier.mjs +243 -102
  3. package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -1
  4. package/dist/agent-verifier/{chunk-QMOBTQ5G.mjs → chunk-3SPDNMLA.mjs} +3 -3
  5. package/dist/agent-verifier/{chunk-QMOBTQ5G.mjs.map → chunk-3SPDNMLA.mjs.map} +1 -1
  6. package/dist/agent-verifier/{chunk-FNSHNMDY.mjs → chunk-MIRGCMUC.mjs} +3 -10
  7. package/dist/agent-verifier/chunk-MIRGCMUC.mjs.map +1 -0
  8. package/dist/agent-verifier/{global-config-SWWR2LP4.mjs → global-config-2NUESNEQ.mjs} +2 -2
  9. package/dist/agent-verifier/{keychain-backend-UF3Z26JM.mjs → keychain-backend-FF4I6ODB.mjs} +1 -1
  10. package/dist/agent-verifier/{keychain-backend-UF3Z26JM.mjs.map → keychain-backend-FF4I6ODB.mjs.map} +1 -1
  11. package/dist/agent-verifier/{materialize-workspace-K4WYFG5E.mjs → materialize-workspace-J2S4XIIC.mjs} +2 -2
  12. package/dist/agent-verifier/{static-scaffold-MHVM63HU.mjs → static-scaffold-56QBCO6P.mjs} +2 -2
  13. package/dist/authoring-compatibility-internal.js +1 -1
  14. package/dist/{chunk-UI7NWSYA.js → chunk-6NYVJYN4.js} +7 -28
  15. package/dist/chunk-6NYVJYN4.js.map +1 -0
  16. package/dist/{chunk-RTNKVNQA.js → chunk-DWWMZBFB.js} +247 -158
  17. package/dist/chunk-DWWMZBFB.js.map +1 -0
  18. package/dist/{chunk-I4SZ7FA4.js → chunk-TRF7IPXK.js} +3 -9
  19. package/dist/{chunk-I4SZ7FA4.js.map → chunk-TRF7IPXK.js.map} +1 -1
  20. package/dist/{global-config-GK2UC2X6.js → global-config-RBMW7IVA.js} +2 -2
  21. package/dist/index.js +123 -305
  22. package/dist/index.js.map +1 -1
  23. package/dist/internal.js +3 -3
  24. package/dist/internal.js.map +1 -1
  25. package/dist/{keychain-backend-GO34KGTG.js → keychain-backend-FSNTNTZE.js} +1 -1
  26. package/dist/{keychain-backend-GO34KGTG.js.map → keychain-backend-FSNTNTZE.js.map} +1 -1
  27. package/package.json +1 -1
  28. package/release/authoring-release-set.json +2 -2
  29. package/skills/dreamboard/SKILL.md +3 -3
  30. package/skills/dreamboard/references/building-your-first-game.md +1 -1
  31. package/skills/dreamboard/references/cli.md +20 -19
  32. package/skills/dreamboard/references/quickstart.md +3 -3
  33. package/dist/agent-verifier/chunk-FNSHNMDY.mjs.map +0 -1
  34. package/dist/chunk-RTNKVNQA.js.map +0 -1
  35. package/dist/chunk-UI7NWSYA.js.map +0 -1
  36. /package/dist/agent-verifier/{global-config-SWWR2LP4.mjs.map → global-config-2NUESNEQ.mjs.map} +0 -0
  37. /package/dist/agent-verifier/{materialize-workspace-K4WYFG5E.mjs.map → materialize-workspace-J2S4XIIC.mjs.map} +0 -0
  38. /package/dist/agent-verifier/{static-scaffold-MHVM63HU.mjs.map → static-scaffold-56QBCO6P.mjs.map} +0 -0
  39. /package/dist/{global-config-GK2UC2X6.js.map → global-config-RBMW7IVA.js.map} +0 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # dreamboard
2
2
 
3
- Dreamboard CLI for working with Dreamboard games from your own editor/tooling.
3
+ Dreamboard for working with Dreamboard games from your own editor/tooling.
4
4
 
5
5
  Dreamboard is built to take you from napkin sketch to playable prototype without the paper cuts:
6
6
 
@@ -18,7 +18,7 @@ Published npm package:
18
18
  npm install -g dreamboard
19
19
  ```
20
20
 
21
- The published CLI targets Node 20+.
21
+ The published `dreamboard` package targets Node 20+.
22
22
 
23
23
  ## Why Dreamboard
24
24
 
@@ -35,11 +35,11 @@ Use browser login:
35
35
  dreamboard auth login
36
36
  ```
37
37
 
38
- The CLI stores your refreshable session in `~/.dreamboard/auth.json` by default. The file is written atomically with owner-only permissions (`0600`).
38
+ Dreamboard stores your refreshable session in `~/.dreamboard/auth.json` by default. The file is written atomically with owner-only permissions (`0600`).
39
39
 
40
40
  The operating system keychain is optional. Set `"credentialBackend": "keychain"` in `~/.dreamboard/config.json`, or use `DREAMBOARD_CREDENTIAL_BACKEND=keychain`, to opt in.
41
41
 
42
- That stored session includes the Clerk refresh token the CLI needs to renew and exchange for short-lived Dreamboard API tokens automatically. Direct JWT injection is intentionally not part of the published CLI flow.
42
+ That stored session includes the Clerk refresh token Dreamboard needs to renew and exchange for short-lived Dreamboard API tokens automatically. Direct JWT injection is intentionally not part of the published Dreamboard flow.
43
43
 
44
44
  ## Source Checkout Setup
45
45
 
@@ -112,15 +112,15 @@ dreamboard dev
112
112
  ## Notes
113
113
 
114
114
  - Project state lives in `.dreamboard/project.json`.
115
- - Published/public CLI installs target Node 20+ and support commit-scoped
115
+ - Published/public `dreamboard` installs target Node 20+ and support commit-scoped
116
116
  build, preview, release, and status workflows.
117
- - Published/public CLI builds are production-only; they do not support environment overrides or direct JWT injection.
117
+ - Published stable `dreamboard` builds are production-only and do not support environment overrides or direct JWT injection. Published alpha builds allow `--env <local|staging|prod>` for operator verification, but still reject direct JWT injection.
118
118
  - Local embedded-harness testing remains Bun-only and requires a source checkout with local backend support.
119
- - Internal source-checkout builds may expose extra auth and environment helpers, but those are not part of the published CLI contract.
119
+ - Internal source-checkout builds may expose extra auth and environment helpers, but those are not part of the published Dreamboard contract.
120
120
 
121
121
  ## Skill Source
122
122
 
123
123
  - Public skill source lives under `skills/dreamboard/`.
124
124
  - `skills/dreamboard/references/*.md` are generated from `docs/` via `pnpm run sync:skill-docs`.
125
125
  - `dreamboard project create` installs the bundled skill into `.agents/skills/dreamboard/` in the generated game project.
126
- - Public GitHub repo for the CLI is [dreamboard-games/dreamboard-cli](https://github.com/dreamboard-games/dreamboard-cli).
126
+ - Public GitHub repo for Dreamboard is [dreamboard-games/dreamboard](https://github.com/dreamboard-games/dreamboard).
@@ -4,11 +4,10 @@ import {
4
4
  loadProjectConfig
5
5
  } from "./chunk-M6YNQZCC.mjs";
6
6
  import {
7
- clearCredentials,
8
7
  getStoredSession,
9
8
  loadGlobalConfig,
10
9
  withCredentialLock
11
- } from "./chunk-FNSHNMDY.mjs";
10
+ } from "./chunk-MIRGCMUC.mjs";
12
11
  import "./chunk-GWRZRWCF.mjs";
13
12
  import {
14
13
  readJsonFile,
@@ -1130,10 +1129,29 @@ var consola = createConsola2();
1130
1129
 
1131
1130
  // src/build-target.ts
1132
1131
  var injectedBuildChannel = true ? "development" : void 0;
1132
+ var injectedPackageVersion = typeof __DREAMBOARD_PACKAGE_VERSION__ === "string" ? __DREAMBOARD_PACKAGE_VERSION__ : void 0;
1133
1133
  var BUILD_CHANNEL = injectedBuildChannel === "published" ? "published" : "development";
1134
+ var PACKAGE_VERSION = injectedPackageVersion ?? "0.0.0-development";
1135
+ function isAlphaReleaseVersion(version) {
1136
+ return /(?:^|-|\.)alpha(?:$|-|\.)/.test(version);
1137
+ }
1134
1138
  var IS_PUBLISHED_BUILD = BUILD_CHANNEL === "published";
1139
+ var IS_ALPHA_RELEASE = isAlphaReleaseVersion(PACKAGE_VERSION);
1140
+ var CAN_SELECT_ENVIRONMENT = !IS_PUBLISHED_BUILD || IS_ALPHA_RELEASE;
1135
1141
  var PUBLISHED_ENVIRONMENT = "prod";
1136
1142
 
1143
+ // src/auth/refresh-error.ts
1144
+ function classifyRefreshError(error) {
1145
+ const message = error.message?.toLowerCase() ?? "";
1146
+ if (error.status === 400 || error.status === 401 || message.includes("invalid_grant") || message.includes("refresh token") || message.includes("expired") || message.includes("revoked")) {
1147
+ return { kind: "permanent_invalid", reason: error.message };
1148
+ }
1149
+ if (error.status === 408 || error.status === 429 || typeof error.status === "number" && error.status >= 500 || message.includes("timeout") || message.includes("network") || message.includes("fetch failed")) {
1150
+ return { kind: "transient", reason: error.message };
1151
+ }
1152
+ return { kind: "unknown", reason: error.message };
1153
+ }
1154
+
1137
1155
  // src/auth/clerk-oauth.ts
1138
1156
  import crypto from "crypto";
1139
1157
  async function refreshClerkOAuthToken(input) {
@@ -1241,75 +1259,183 @@ async function exchangeDreamboardUserToken(input) {
1241
1259
  };
1242
1260
  }
1243
1261
 
1244
- // src/auth/user-token-manager.ts
1245
- var TOKEN_REFRESH_WINDOW_MS = 60 * 1e3;
1246
- function createUserTokenManager(config) {
1262
+ // src/auth/user-session-manager.ts
1263
+ var DEFAULT_TOKEN_MIN_VALIDITY_MS = 60 * 1e3;
1264
+ function createUserSessionManager(config) {
1247
1265
  return {
1248
- async resolveApiToken() {
1249
- const localOrInjected = resolveNonStoredToken(config, "dreamboard-api");
1250
- if (localOrInjected) return localOrInjected;
1251
- if (!usesStoredSession(config)) return null;
1266
+ async establishRefreshableSession(session) {
1252
1267
  return withCredentialLock(async (ops) => {
1253
- const stored = await ops.read();
1254
- const apiToken = freshStoredApiToken(stored);
1255
- if (apiToken) return apiToken;
1256
- const clerk = await resolveFreshClerkAccessToken(config, stored);
1268
+ const credentials = credentialsFromRefreshableSession(session);
1269
+ await ops.writeFull(credentials);
1257
1270
  const exchanged = await exchangeDreamboardUserToken({
1258
1271
  apiBaseUrl: config.apiBaseUrl,
1259
- clerkAccessToken: clerk.accessToken,
1272
+ clerkAccessToken: credentials.accessToken,
1260
1273
  audience: "dreamboard-api"
1261
1274
  });
1262
- await ops.writeFull({
1263
- ...clerk,
1264
- dreamboardApiToken: exchanged.accessToken,
1265
- dreamboardApiExpiresAt: exchanged.expiresAt
1266
- });
1267
- return {
1268
- token: exchanged.accessToken,
1269
- expiresAt: exchanged.expiresAt,
1270
- audience: "dreamboard-api"
1271
- };
1275
+ const apiToken = toAccessToken(exchanged);
1276
+ await ops.writeFull(withApiToken(credentials, apiToken));
1277
+ return apiToken;
1278
+ });
1279
+ },
1280
+ async establishAccessOnlySession(accessToken) {
1281
+ await withCredentialLock((ops) => ops.writeAccessOnly(accessToken));
1282
+ },
1283
+ async resolveApiToken(options) {
1284
+ const localOrInjected = resolveNonStoredToken(config, "dreamboard-api");
1285
+ if (localOrInjected) return localOrInjected;
1286
+ if (!usesStoredSession(config)) return null;
1287
+ const minValidityMs = options?.minValiditySeconds === void 0 ? DEFAULT_TOKEN_MIN_VALIDITY_MS : Math.max(0, options.minValiditySeconds * 1e3);
1288
+ return withCredentialLock(async (ops) => {
1289
+ const stored = await ops.read();
1290
+ return resolveStoredApiToken(ops, config, stored, minValidityMs);
1272
1291
  });
1273
1292
  },
1274
1293
  async resolveGitToken() {
1275
- if (!usesStoredSession(config) && config.authToken) {
1276
- const exchanged = await exchangeDreamboardUserToken({
1277
- apiBaseUrl: config.apiBaseUrl,
1278
- clerkAccessToken: config.authToken,
1279
- audience: "dreamboard-git"
1280
- });
1281
- return {
1282
- token: exchanged.accessToken,
1283
- expiresAt: exchanged.expiresAt,
1284
- audience: "dreamboard-git"
1285
- };
1286
- }
1294
+ const localOrInjected = resolveNonStoredToken(config, "dreamboard-git");
1295
+ if (localOrInjected) return localOrInjected;
1287
1296
  if (!usesStoredSession(config)) {
1288
- throw new Error(
1289
- "Missing Dreamboard session. Run `dreamboard auth login` to authenticate."
1297
+ throw missingSessionError();
1298
+ }
1299
+ return withCredentialLock(async (ops) => {
1300
+ const stored = await ops.read();
1301
+ const clerk = await resolveFreshClerkSession(ops, config, stored);
1302
+ return toAccessToken(
1303
+ await exchangeDreamboardUserToken({
1304
+ apiBaseUrl: config.apiBaseUrl,
1305
+ clerkAccessToken: clerk.accessToken,
1306
+ audience: "dreamboard-git"
1307
+ })
1290
1308
  );
1309
+ });
1310
+ },
1311
+ async inspectSession() {
1312
+ const localOrInjected = resolveNonStoredToken(config, "dreamboard-api");
1313
+ if (localOrInjected) {
1314
+ return inspectAccessOnlyToken(localOrInjected);
1315
+ }
1316
+ if (!usesStoredSession(config)) {
1317
+ return { kind: "none" };
1291
1318
  }
1292
1319
  return withCredentialLock(async (ops) => {
1293
1320
  const stored = await ops.read();
1294
- const clerk = await resolveFreshClerkAccessToken(config, stored);
1295
- const exchanged = await exchangeDreamboardUserToken({
1296
- apiBaseUrl: config.apiBaseUrl,
1297
- clerkAccessToken: clerk.accessToken,
1298
- audience: "dreamboard-git"
1299
- });
1300
- await ops.writeFull(clerk);
1301
- return {
1302
- token: exchanged.accessToken,
1303
- expiresAt: exchanged.expiresAt,
1304
- audience: "dreamboard-git"
1305
- };
1321
+ if (!stored) return { kind: "none" };
1322
+ const cached = freshStoredApiToken(
1323
+ stored,
1324
+ DEFAULT_TOKEN_MIN_VALIDITY_MS
1325
+ );
1326
+ if (cached) {
1327
+ return activeStatus("refreshable", cached, false);
1328
+ }
1329
+ try {
1330
+ const token = await resolveStoredApiToken(
1331
+ ops,
1332
+ config,
1333
+ stored,
1334
+ DEFAULT_TOKEN_MIN_VALIDITY_MS
1335
+ );
1336
+ return activeStatus("refreshable", token, true);
1337
+ } catch (error) {
1338
+ return failedRefreshableStatus(error);
1339
+ }
1306
1340
  });
1307
1341
  },
1308
1342
  async logout() {
1309
- await clearCredentials("user_token_manager_logout");
1343
+ await withCredentialLock((ops) => ops.clear("logout_command"));
1310
1344
  }
1311
1345
  };
1312
1346
  }
1347
+ async function resolveStoredApiToken(ops, config, stored, minValidityMs) {
1348
+ const cached = freshStoredApiToken(stored, minValidityMs);
1349
+ if (cached) return cached;
1350
+ const clerk = await resolveFreshClerkSession(ops, config, stored);
1351
+ const exchanged = await exchangeDreamboardUserToken({
1352
+ apiBaseUrl: config.apiBaseUrl,
1353
+ clerkAccessToken: clerk.accessToken,
1354
+ audience: "dreamboard-api"
1355
+ });
1356
+ const apiToken = toAccessToken(exchanged);
1357
+ await ops.writeFull(withApiToken(clerk, apiToken));
1358
+ return apiToken;
1359
+ }
1360
+ async function resolveFreshClerkSession(ops, config, stored) {
1361
+ if (!stored) {
1362
+ throw missingSessionError();
1363
+ }
1364
+ if (!stored.refreshToken) {
1365
+ throw new Error(
1366
+ "Stored Dreamboard session is missing its refresh token. Run `dreamboard auth login` to authenticate again."
1367
+ );
1368
+ }
1369
+ if (stored.accessToken && isFresh(
1370
+ stored.tokenExpiresAt,
1371
+ stored.accessToken,
1372
+ DEFAULT_TOKEN_MIN_VALIDITY_MS
1373
+ )) {
1374
+ return credentialsFromStored(config, stored);
1375
+ }
1376
+ const payload = await refreshClerkOAuthToken({
1377
+ config: {
1378
+ issuer: stored.clerkOAuthIssuer ?? config.clerkOAuthIssuer,
1379
+ clientId: stored.clerkOAuthClientId ?? config.clerkOAuthClientId,
1380
+ tokenUrl: stored.clerkOAuthTokenUrl ?? config.clerkOAuthTokenUrl
1381
+ },
1382
+ refreshToken: stored.refreshToken
1383
+ });
1384
+ const refreshed = {
1385
+ accessToken: payload.accessToken,
1386
+ refreshToken: payload.refreshToken,
1387
+ tokenExpiresAt: payload.expiresAt,
1388
+ dreamboardApiToken: stored.dreamboardApiToken,
1389
+ dreamboardApiExpiresAt: stored.dreamboardApiExpiresAt,
1390
+ clerkOAuthIssuer: stored.clerkOAuthIssuer ?? config.clerkOAuthIssuer,
1391
+ clerkOAuthClientId: stored.clerkOAuthClientId ?? config.clerkOAuthClientId,
1392
+ clerkOAuthTokenUrl: payload.tokenUrl,
1393
+ environment: stored.environment ?? config.environment
1394
+ };
1395
+ await ops.writeFull(refreshed);
1396
+ return refreshed;
1397
+ }
1398
+ function credentialsFromStored(config, stored) {
1399
+ if (!stored.accessToken || !stored.refreshToken) {
1400
+ throw missingSessionError();
1401
+ }
1402
+ return {
1403
+ accessToken: stored.accessToken,
1404
+ refreshToken: stored.refreshToken,
1405
+ tokenExpiresAt: stored.tokenExpiresAt,
1406
+ dreamboardApiToken: stored.dreamboardApiToken,
1407
+ dreamboardApiExpiresAt: stored.dreamboardApiExpiresAt,
1408
+ clerkOAuthIssuer: stored.clerkOAuthIssuer ?? config.clerkOAuthIssuer,
1409
+ clerkOAuthClientId: stored.clerkOAuthClientId ?? config.clerkOAuthClientId,
1410
+ clerkOAuthTokenUrl: stored.clerkOAuthTokenUrl ?? config.clerkOAuthTokenUrl,
1411
+ environment: stored.environment ?? config.environment
1412
+ };
1413
+ }
1414
+ function credentialsFromRefreshableSession(session) {
1415
+ return {
1416
+ accessToken: session.clerkAccessToken,
1417
+ refreshToken: session.refreshToken,
1418
+ tokenExpiresAt: session.clerkAccessExpiresAt,
1419
+ clerkOAuthIssuer: session.clerkOAuthIssuer,
1420
+ clerkOAuthClientId: session.clerkOAuthClientId,
1421
+ clerkOAuthTokenUrl: session.clerkOAuthTokenUrl,
1422
+ environment: session.environment
1423
+ };
1424
+ }
1425
+ function withApiToken(credentials, token) {
1426
+ return {
1427
+ ...credentials,
1428
+ dreamboardApiToken: token.token,
1429
+ dreamboardApiExpiresAt: token.expiresAt
1430
+ };
1431
+ }
1432
+ function toAccessToken(token) {
1433
+ return {
1434
+ token: token.accessToken,
1435
+ expiresAt: token.expiresAt,
1436
+ audience: token.audience
1437
+ };
1438
+ }
1313
1439
  function resolveNonStoredToken(config, audience) {
1314
1440
  if (usesStoredSession(config)) return null;
1315
1441
  if (!config.authToken) return null;
@@ -1319,9 +1445,13 @@ function resolveNonStoredToken(config, audience) {
1319
1445
  audience
1320
1446
  };
1321
1447
  }
1322
- function freshStoredApiToken(stored) {
1448
+ function freshStoredApiToken(stored, minValidityMs) {
1323
1449
  if (!stored?.dreamboardApiToken) return null;
1324
- if (isFresh(stored.dreamboardApiExpiresAt, stored.dreamboardApiToken)) {
1450
+ if (isFresh(
1451
+ stored.dreamboardApiExpiresAt,
1452
+ stored.dreamboardApiToken,
1453
+ minValidityMs
1454
+ )) {
1325
1455
  return {
1326
1456
  token: stored.dreamboardApiToken,
1327
1457
  expiresAt: stored.dreamboardApiExpiresAt,
@@ -1330,49 +1460,60 @@ function freshStoredApiToken(stored) {
1330
1460
  }
1331
1461
  return null;
1332
1462
  }
1333
- async function resolveFreshClerkAccessToken(config, stored) {
1334
- const accessToken = stored?.accessToken ?? config.clerkAccessToken;
1335
- const refreshToken = stored?.refreshToken ?? config.refreshToken;
1336
- const tokenExpiresAt = stored?.tokenExpiresAt ?? config.clerkAccessExpiresAt;
1337
- if (!refreshToken) {
1338
- throw new Error(
1339
- "Stored Dreamboard session is missing its refresh token. Run `dreamboard auth login` to authenticate again."
1340
- );
1341
- }
1342
- if (accessToken && isFresh(tokenExpiresAt, accessToken)) {
1463
+ function inspectAccessOnlyToken(token) {
1464
+ const expiry = resolveExpiry(token.expiresAt, token.token);
1465
+ if (expiry && expiry.getTime() <= Date.now()) {
1343
1466
  return {
1344
- accessToken,
1345
- refreshToken,
1346
- tokenExpiresAt,
1347
- dreamboardApiToken: stored?.dreamboardApiToken,
1348
- dreamboardApiExpiresAt: stored?.dreamboardApiExpiresAt,
1349
- clerkOAuthIssuer: stored?.clerkOAuthIssuer ?? config.clerkOAuthIssuer,
1350
- clerkOAuthClientId: stored?.clerkOAuthClientId ?? config.clerkOAuthClientId,
1351
- clerkOAuthTokenUrl: stored?.clerkOAuthTokenUrl ?? config.clerkOAuthTokenUrl,
1352
- environment: stored?.environment ?? config.environment
1467
+ kind: "invalid",
1468
+ sessionKind: "access-only",
1469
+ message: "Stored Dreamboard access token is expired. Run `dreamboard auth login` to authenticate again."
1353
1470
  };
1354
1471
  }
1355
- const payload = await refreshClerkOAuthToken({
1356
- config: {
1357
- issuer: stored?.clerkOAuthIssuer ?? config.clerkOAuthIssuer,
1358
- clientId: stored?.clerkOAuthClientId ?? config.clerkOAuthClientId,
1359
- tokenUrl: stored?.clerkOAuthTokenUrl ?? config.clerkOAuthTokenUrl
1360
- },
1361
- refreshToken
1472
+ return activeStatus("access-only", token, false);
1473
+ }
1474
+ function failedRefreshableStatus(error) {
1475
+ const message = error instanceof Error ? error.message : String(error);
1476
+ const errorStatus = typeof error === "object" && error !== null && "status" in error ? error.status : void 0;
1477
+ const classification = classifyRefreshError({
1478
+ message,
1479
+ status: typeof errorStatus === "number" ? errorStatus : void 0
1362
1480
  });
1481
+ if (classification.kind === "permanent_invalid") {
1482
+ return {
1483
+ kind: "invalid",
1484
+ sessionKind: "refreshable",
1485
+ message
1486
+ };
1487
+ }
1363
1488
  return {
1364
- accessToken: payload.accessToken,
1365
- refreshToken: payload.refreshToken,
1366
- tokenExpiresAt: payload.expiresAt,
1367
- clerkOAuthIssuer: stored?.clerkOAuthIssuer ?? config.clerkOAuthIssuer,
1368
- clerkOAuthClientId: stored?.clerkOAuthClientId ?? config.clerkOAuthClientId,
1369
- clerkOAuthTokenUrl: payload.tokenUrl,
1370
- environment: stored?.environment ?? config.environment
1489
+ kind: "degraded",
1490
+ sessionKind: "refreshable",
1491
+ message
1371
1492
  };
1372
1493
  }
1373
- function isFresh(expiresAt, token) {
1374
- const expiry = expiresAt ? new Date(expiresAt) : getJwtExpiry(token);
1375
- return expiry !== null && Number.isFinite(expiry.getTime()) && expiry.getTime() > Date.now() + TOKEN_REFRESH_WINDOW_MS;
1494
+ function activeStatus(sessionKind, apiToken, repaired) {
1495
+ return {
1496
+ kind: "active",
1497
+ sessionKind,
1498
+ apiToken,
1499
+ repaired
1500
+ };
1501
+ }
1502
+ function missingSessionError() {
1503
+ return new Error(
1504
+ "Missing Dreamboard session. Run `dreamboard auth login` to authenticate."
1505
+ );
1506
+ }
1507
+ function isFresh(expiresAt, token, minValidityMs) {
1508
+ const expiry = resolveExpiry(expiresAt, token);
1509
+ return expiry !== null && Number.isFinite(expiry.getTime()) && expiry.getTime() > Date.now() + minValidityMs;
1510
+ }
1511
+ function resolveExpiry(expiresAt, token) {
1512
+ if (expiresAt) {
1513
+ const parsed = new Date(expiresAt);
1514
+ return Number.isFinite(parsed.getTime()) ? parsed : null;
1515
+ }
1516
+ return getJwtExpiry(token);
1376
1517
  }
1377
1518
  function getJwtExpiry(accessToken) {
1378
1519
  if (!accessToken) return null;
@@ -1479,21 +1620,20 @@ function isLocalAwsUrl(rawUrl) {
1479
1620
  }
1480
1621
 
1481
1622
  // src/config/resolve.ts
1482
- var DEFAULT_REFRESH_WINDOW_MS = 5 * 60 * 1e3;
1483
1623
  var TRANSIENT_READ_RETRY_DELAYS_MS = [100, 300];
1484
1624
  function resolveConfig(globalConfig, flags, project, credentials) {
1485
1625
  if (IS_PUBLISHED_BUILD) {
1486
1626
  assertPublicRuntimeFlags(flags);
1487
1627
  }
1488
- const envEnvironment = IS_PUBLISHED_BUILD ? void 0 : environmentFromProcess();
1489
- const projectEnvironment = IS_PUBLISHED_BUILD ? void 0 : project?.environment;
1490
- const environment = IS_PUBLISHED_BUILD ? PUBLISHED_ENVIRONMENT : flags.env || envEnvironment || projectEnvironment || globalConfig.environment || "staging";
1628
+ const envEnvironment = CAN_SELECT_ENVIRONMENT ? environmentFromProcess() : void 0;
1629
+ const projectEnvironment = CAN_SELECT_ENVIRONMENT ? project?.environment : void 0;
1630
+ const environment = CAN_SELECT_ENVIRONMENT ? flags.env || envEnvironment || projectEnvironment || globalConfig.environment || (IS_PUBLISHED_BUILD ? PUBLISHED_ENVIRONMENT : "staging") : PUBLISHED_ENVIRONMENT;
1491
1631
  const envConfig = ENVIRONMENT_CONFIGS[environment];
1492
1632
  const publishedEnvConfig = ENVIRONMENT_CONFIGS[PUBLISHED_ENVIRONMENT];
1493
- const hasExplicitEnvironmentOverride = !IS_PUBLISHED_BUILD && Boolean(flags.env || envEnvironment || projectEnvironment);
1494
- const resolvedApiBaseUrl = IS_PUBLISHED_BUILD ? publishedEnvConfig?.apiBaseUrl ?? DEFAULT_API_BASE_URL : hasExplicitEnvironmentOverride ? projectLocalBaseUrl(project?.apiBaseUrl, environment) || envConfig?.apiBaseUrl || DEFAULT_API_BASE_URL : project?.apiBaseUrl || envConfig?.apiBaseUrl || DEFAULT_API_BASE_URL;
1633
+ const hasExplicitEnvironmentOverride = CAN_SELECT_ENVIRONMENT && Boolean(flags.env || envEnvironment || projectEnvironment);
1634
+ const resolvedApiBaseUrl = IS_PUBLISHED_BUILD && !CAN_SELECT_ENVIRONMENT ? publishedEnvConfig?.apiBaseUrl ?? DEFAULT_API_BASE_URL : hasExplicitEnvironmentOverride ? projectLocalBaseUrl(project?.apiBaseUrl, environment) || envConfig?.apiBaseUrl || DEFAULT_API_BASE_URL : project?.apiBaseUrl || envConfig?.apiBaseUrl || DEFAULT_API_BASE_URL;
1495
1635
  const apiBaseUrl = valueOrUndefined(process.env.DREAMBOARD_API_BASE_URL) ?? resolvedApiBaseUrl;
1496
- const resolvedWebBaseUrl = IS_PUBLISHED_BUILD ? publishedEnvConfig?.webBaseUrl ?? DEFAULT_WEB_BASE_URL : hasExplicitEnvironmentOverride ? projectLocalBaseUrl(project?.webBaseUrl, environment) || envConfig?.webBaseUrl || DEFAULT_WEB_BASE_URL : project?.webBaseUrl || envConfig?.webBaseUrl || DEFAULT_WEB_BASE_URL;
1636
+ const resolvedWebBaseUrl = IS_PUBLISHED_BUILD && !CAN_SELECT_ENVIRONMENT ? publishedEnvConfig?.webBaseUrl ?? DEFAULT_WEB_BASE_URL : hasExplicitEnvironmentOverride ? projectLocalBaseUrl(project?.webBaseUrl, environment) || envConfig?.webBaseUrl || DEFAULT_WEB_BASE_URL : project?.webBaseUrl || envConfig?.webBaseUrl || DEFAULT_WEB_BASE_URL;
1497
1637
  const webBaseUrl = valueOrUndefined(process.env.DREAMBOARD_WEB_BASE_URL) ?? resolvedWebBaseUrl;
1498
1638
  const snapshot = buildCredentialSnapshot(flags, credentials, environment);
1499
1639
  const oauthConfig = resolveEnvironmentOAuthConfig(environment, envConfig);
@@ -1598,9 +1738,10 @@ function projectLocalBaseUrl(rawUrl, environment) {
1598
1738
  return void 0;
1599
1739
  }
1600
1740
  }
1601
- function assertPublicRuntimeFlags(flags) {
1602
- const argv2 = process.argv.slice(2);
1603
- if (flags.env || argv2.includes("--env")) {
1741
+ function assertPublicRuntimeFlags(flags, options = {}) {
1742
+ const canSelectEnvironment = options.canSelectEnvironment ?? CAN_SELECT_ENVIRONMENT;
1743
+ const argv2 = options.argv ?? process.argv.slice(2);
1744
+ if (!canSelectEnvironment && (flags.env || argv2.includes("--env"))) {
1604
1745
  throw new Error(
1605
1746
  "The published Dreamboard CLI is production-only and does not accept `--env`."
1606
1747
  );
@@ -1618,7 +1759,7 @@ function assertPublicRuntimeFlags(flags) {
1618
1759
  }
1619
1760
  async function configureClient(config) {
1620
1761
  const localHarnessToken = resolveLocalHarnessAccessToken(config);
1621
- const resolvedToken = localHarnessToken ? { token: localHarnessToken } : await createUserTokenManager(config).resolveApiToken();
1762
+ const resolvedToken = localHarnessToken ? { token: localHarnessToken } : await createUserSessionManager(config).resolveApiToken();
1622
1763
  const effectiveAccessToken = resolvedToken?.token;
1623
1764
  client.setConfig({
1624
1765
  baseUrl: config.apiBaseUrl,
@@ -1879,7 +2020,7 @@ Usage:
1879
2020
  }
1880
2021
  async function materializePreparedWorkspace(args) {
1881
2022
  const inputPath = readRequiredOption(args, "--input");
1882
- const { materializeWorkspaceProject } = await import("./materialize-workspace-K4WYFG5E.mjs");
2023
+ const { materializeWorkspaceProject } = await import("./materialize-workspace-J2S4XIIC.mjs");
1883
2024
  const input = JSON.parse(await readFile(inputPath, "utf8"));
1884
2025
  await materializeWorkspaceProject({
1885
2026
  ...input,
@@ -1947,7 +2088,7 @@ async function runCloudLocalVerification(projectRoot, projectConfig, config) {
1947
2088
  { assertReducerContractPreflight },
1948
2089
  { getProjectLocalMaintainerRegistry }
1949
2090
  ] = await Promise.all([
1950
- import("./static-scaffold-MHVM63HU.mjs"),
2091
+ import("./static-scaffold-56QBCO6P.mjs"),
1951
2092
  import("./local-files-OF4QFISU.mjs"),
1952
2093
  import("./workspace-codegen-SPPVHURX.mjs"),
1953
2094
  import("./workspace-dependencies-5HEEKZFP.mjs"),