timeback-studio 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -2950,6 +2950,11 @@ ${t}
2950
2950
  ${import_picocolors2.default.green(C)} ${import_picocolors2.default.reset(n)} ${import_picocolors2.default.gray(_2.repeat(Math.max(s - i - 1, 1)) + me)}
2951
2951
  ${c}
2952
2952
  ${import_picocolors2.default.gray(de + _2.repeat(s + 2) + pe)}
2953
+ `);
2954
+ };
2955
+ var xe = (t = "") => {
2956
+ process.stdout.write(`${import_picocolors2.default.gray(d2)} ${import_picocolors2.default.red(t)}
2957
+
2953
2958
  `);
2954
2959
  };
2955
2960
  var Ie = (t = "") => {
@@ -16695,7 +16700,7 @@ function intro(title) {
16695
16700
  }
16696
16701
  var outro = {
16697
16702
  success: (message = "Done") => Se(green(message)),
16698
- cancelled: () => Se(dim("Cancelled")),
16703
+ cancelled: () => xe(dim("Cancelled")),
16699
16704
  error: (message) => Se(red(message)),
16700
16705
  warn: (message) => Se(yellow(message)),
16701
16706
  info: (message) => Se(dim(message))
@@ -16795,8 +16800,8 @@ async function getConfiguredEnvironments() {
16795
16800
  return configured;
16796
16801
  }
16797
16802
  function getEnvCredentials() {
16798
- const clientId = process.env.TIMEBACK_CLIENT_ID;
16799
- const clientSecret = process.env.TIMEBACK_CLIENT_SECRET;
16803
+ const clientId = process.env.TIMEBACK_API_CLIENT_ID ?? process.env.TIMEBACK_CLIENT_ID;
16804
+ const clientSecret = process.env.TIMEBACK_API_CLIENT_SECRET ?? process.env.TIMEBACK_CLIENT_SECRET;
16800
16805
  if (clientId && clientSecret) {
16801
16806
  const result = CredentialsSchema.safeParse({ clientId, clientSecret });
16802
16807
  if (result.success) {
@@ -16840,7 +16845,10 @@ async function validateEmailWithTimeback(environment, clientId, clientSecret, em
16840
16845
  });
16841
16846
  try {
16842
16847
  const page = await client.oneroster.users.list({
16843
- where: { email: email3 },
16848
+ where: {
16849
+ email: email3,
16850
+ status: "active"
16851
+ },
16844
16852
  limit: 1
16845
16853
  });
16846
16854
  if (page.data.length === 0) {
@@ -16868,7 +16876,7 @@ async function promptForCredentials(environment) {
16868
16876
  }
16869
16877
  });
16870
16878
  if (isCancelled(clientId))
16871
- return null;
16879
+ return { status: "cancelled" };
16872
16880
  const clientSecret = await ge({
16873
16881
  message: `Client Secret ${dim(`(${environment})`)}`,
16874
16882
  validate: (value) => {
@@ -16878,7 +16886,7 @@ async function promptForCredentials(environment) {
16878
16886
  }
16879
16887
  });
16880
16888
  if (isCancelled(clientSecret))
16881
- return null;
16889
+ return { status: "cancelled" };
16882
16890
  const email3 = await he({
16883
16891
  message: `Your email ${dim("(for fetching your OneRoster profile)")}`,
16884
16892
  placeholder: "you@example.com",
@@ -16891,33 +16899,47 @@ async function promptForCredentials(environment) {
16891
16899
  }
16892
16900
  });
16893
16901
  if (isCancelled(email3))
16894
- return null;
16902
+ return { status: "cancelled" };
16895
16903
  if (email3) {
16896
16904
  const s = Y2();
16897
16905
  s.start("Validating email...");
16898
16906
  const result = await validateEmailWithTimeback(environment, clientId, clientSecret, email3);
16899
16907
  if (!result.valid) {
16900
- s.stop(red("Email validation failed"));
16901
- M2.error(result.error ?? "Unknown error");
16908
+ const errorMsg = result.error ?? "Email validation failed";
16909
+ s.stop(red(errorMsg));
16902
16910
  M2.info("Please contact a Timeback admin to set up your account.");
16903
- return null;
16911
+ return {
16912
+ status: "error",
16913
+ error: errorMsg
16914
+ };
16904
16915
  }
16905
16916
  s.stop(green("Email validated"));
16906
16917
  }
16907
- return { clientId, clientSecret, email: email3 || undefined };
16918
+ return {
16919
+ status: "ok",
16920
+ credentials: { clientId, clientSecret, email: email3 || undefined }
16921
+ };
16908
16922
  }
16909
16923
  async function ensureCredentials(options) {
16910
16924
  const { env: env2, credentials, introTitle = "Timeback", skipIntro = false } = options;
16911
16925
  const existing = credentials[env2];
16912
- if (existing)
16913
- return existing;
16926
+ if (existing) {
16927
+ return { status: "ok", credentials: existing, source: "existing" };
16928
+ }
16914
16929
  if (!skipIntro) {
16915
16930
  intro(introTitle);
16916
16931
  }
16917
16932
  Me(`No credentials configured for ${env2}.`, "Credential setup required");
16918
- const newCreds = await promptForCredentials(env2);
16919
- if (!newCreds)
16920
- return null;
16933
+ const promptResult = await promptForCredentials(env2);
16934
+ if (promptResult.status === "cancelled") {
16935
+ outro.cancelled();
16936
+ return { status: "cancelled" };
16937
+ }
16938
+ if (promptResult.status === "error") {
16939
+ outro.error("Credential setup failed");
16940
+ return { status: "error", error: promptResult.error };
16941
+ }
16942
+ const newCreds = promptResult.credentials;
16921
16943
  const saved = await saveCredentials(env2, newCreds);
16922
16944
  if (saved) {
16923
16945
  M2.success(`${env2} credentials saved`);
@@ -16926,7 +16948,7 @@ async function ensureCredentials(options) {
16926
16948
  M2.warn(`Credentials not saved`);
16927
16949
  }
16928
16950
  credentials[env2] = newCreds;
16929
- return newCreds;
16951
+ return { status: "ok", credentials: newCreds, source: "prompted" };
16930
16952
  }
16931
16953
  // ../internal/cli-infra/src/config/playcademy.ts
16932
16954
  var FILE_PATTERNS = ["playcademy.config.ts", "playcademy.config.js", "playcademy.config.json"];
@@ -19408,8 +19430,8 @@ async function addCredentials(options = {}) {
19408
19430
  }
19409
19431
  isOverwriting = true;
19410
19432
  }
19411
- const creds = await promptForCredentials(env2);
19412
- if (creds === null) {
19433
+ const result = await promptForCredentials(env2);
19434
+ if (result.status === "cancelled") {
19413
19435
  if (!inline) {
19414
19436
  if (isOverwriting) {
19415
19437
  outro.info("Existing credentials unchanged");
@@ -19417,11 +19439,21 @@ async function addCredentials(options = {}) {
19417
19439
  outro.cancelled();
19418
19440
  }
19419
19441
  }
19420
- if (exitOnComplete)
19442
+ if (exitOnComplete) {
19421
19443
  process.exit(0);
19444
+ }
19445
+ return;
19446
+ }
19447
+ if (result.status === "error") {
19448
+ if (!inline) {
19449
+ outro.error("Credential setup failed");
19450
+ }
19451
+ if (exitOnComplete) {
19452
+ process.exit(1);
19453
+ }
19422
19454
  return;
19423
19455
  }
19424
- await saveCredentials(env2, creds);
19456
+ await saveCredentials(env2, result.credentials);
19425
19457
  M2.success(`${env2} credentials saved`);
19426
19458
  savedCount++;
19427
19459
  }
@@ -19688,14 +19720,21 @@ async function promptInitialCredentials(options = {}) {
19688
19720
  outro.cancelled();
19689
19721
  process.exit(0);
19690
19722
  }
19723
+ const configuredEnvs = [];
19691
19724
  for (const env2 of environments) {
19692
- const creds = await promptForCredentials(env2);
19693
- if (creds) {
19694
- await saveCredentials(env2, creds);
19695
- M2.success(`${env2} credentials saved`);
19725
+ const result = await promptForCredentials(env2);
19726
+ if (result.status === "cancelled") {
19727
+ outro.cancelled();
19728
+ process.exit(0);
19696
19729
  }
19730
+ if (result.status === "error") {
19731
+ outro.error("Credential setup failed");
19732
+ process.exit(1);
19733
+ }
19734
+ await saveCredentials(env2, result.credentials);
19735
+ M2.success(`${env2} credentials saved`);
19736
+ configuredEnvs.push(env2);
19697
19737
  }
19698
- const configuredEnvs = environments;
19699
19738
  let selectedEnv;
19700
19739
  if (configuredEnvs.length === 1) {
19701
19740
  selectedEnv = configuredEnvs[0];
@@ -20058,10 +20097,11 @@ function buildUserConfigFromCourses(courses) {
20058
20097
  };
20059
20098
  }
20060
20099
  async function resolveFromCourseIds(courseIds, env2, credentials, configuredEnvs) {
20061
- const creds = await ensureCredentials({ env: env2, credentials, skipIntro: true });
20062
- if (!creds) {
20100
+ const ensureResult = await ensureCredentials({ env: env2, credentials, skipIntro: true });
20101
+ if (ensureResult.status !== "ok") {
20063
20102
  return null;
20064
20103
  }
20104
+ const creds = ensureResult.credentials;
20065
20105
  const courses = await fetchCourses(creds, env2, courseIds);
20066
20106
  if (courses.length === 0) {
20067
20107
  M2.warn("No courses found for the provided IDs.");
@@ -23440,7 +23480,6 @@ function createEnvMiddleware(ctx, manager) {
23440
23480
  }, 400);
23441
23481
  }
23442
23482
  if (!manager.has(env2)) {
23443
- log7.warn("Environment not configured", { env: env2 });
23444
23483
  const error48 = createStudioError("ENV_NOT_CONFIGURED", `Environment '${env2}' not configured`);
23445
23484
  return c.json({
23446
23485
  success: false,
@@ -23970,10 +24009,14 @@ function startServer(ctx, serverConfig, configFile) {
23970
24009
 
23971
24010
  // src/cli/commands/serve/index.ts
23972
24011
  async function launchServer(serverConfig, userConfig, credentials, env2, opts, configFile) {
23973
- const creds = await ensureCredentials({ env: env2, credentials, skipIntro: true });
23974
- if (!creds) {
24012
+ const ensureResult = await ensureCredentials({ env: env2, credentials, skipIntro: true });
24013
+ if (ensureResult.status === "cancelled") {
24014
+ process.exit(0);
24015
+ }
24016
+ if (ensureResult.status === "error") {
23975
24017
  process.exit(1);
23976
24018
  }
24019
+ const creds = ensureResult.credentials;
23977
24020
  const derivedSensors = await resolveSensors({
23978
24021
  config: userConfig,
23979
24022
  env: env2,
@@ -1 +1 @@
1
- {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/credentials/add.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAE3C;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuEhF"}
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/credentials/add.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAE3C;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuFhF"}
@@ -7,7 +7,7 @@ interface PromptInitialCredentialsOptions {
7
7
  * Runs the first-time credential setup flow.
8
8
  *
9
9
  * @param options - Options for the prompt
10
- * @returns The selected environment to use for this run, or null if cancelled
10
+ * @returns The selected environment to use for this run, or null if cancelled/error
11
11
  */
12
12
  export declare function promptInitialCredentials(options?: PromptInitialCredentialsOptions): Promise<CredentialEnvironment | null>;
13
13
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"initial.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/credentials/lib/initial.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AAEzE,UAAU,+BAA+B;IACxC,kEAAkE;IAClE,SAAS,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,GAAE,+BAAoC,GAC3C,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAyDvC"}
1
+ {"version":3,"file":"initial.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/credentials/lib/initial.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AAEzE,UAAU,+BAA+B;IACxC,kEAAkE;IAClE,SAAS,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,GAAE,+BAAoC,GAC3C,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAoEvC"}
@@ -42,7 +42,7 @@ export declare function buildUserConfigFromCourses(courses: CourseConfig[]): Loa
42
42
  * @param env - Target environment
43
43
  * @param credentials - Credentials map
44
44
  * @param configuredEnvs - List of configured environments
45
- * @returns Resolved config, or null if cancelled
45
+ * @returns Resolved config, or null if cancelled/error
46
46
  */
47
47
  export declare function resolveFromCourseIds(courseIds: string[], env: Environment, credentials: EnvironmentCredentials, configuredEnvs: Environment[]): Promise<ResolvedConfig | null>;
48
48
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/serve/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AA0BH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,KAAK,EACX,WAAW,EACX,WAAW,EACX,sBAAsB,EACtB,gBAAgB,EAChB,aAAa,EACb,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3D,UAAU,yBAAyB;IAClC,QAAQ,EAAE,cAAc,CAAA;IACxB,WAAW,EAAE,sBAAsB,CAAA;IACnC,cAAc,EAAE,WAAW,EAAE,CAAA;CAC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACxC,SAAS,EAAE,MAAM,EAAE,EACnB,IAAI,EAAE,YAAY,EAClB,kBAAkB,EAAE,WAAW,GAC7B,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAqF3C;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,CAqBhF;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,gBAAgB,CAkBpF;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACzC,SAAS,EAAE,MAAM,EAAE,EACnB,GAAG,EAAE,WAAW,EAChB,WAAW,EAAE,sBAAsB,EACnC,cAAc,EAAE,WAAW,EAAE,GAC3B,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA8BhC;AAED;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC9C,WAAW,EAAE,sBAAsB,EACnC,cAAc,EAAE,WAAW,EAAE,EAC7B,UAAU,EAAE,WAAW,EACvB,UAAU,GAAE,aAAkB,GAC5B,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA0BhC;AAmCD;;;;;;;;;GASG;AACH;;GAEG;AACH,UAAU,qBAAqB;IAC9B,MAAM,EAAE,gBAAgB,CAAA;IACxB,GAAG,EAAE,WAAW,CAAA;IAChB,IAAI,EAAE,YAAY,CAAA;IAClB,KAAK,EAAE,WAAW,CAAA;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CACnC,OAAO,EAAE,qBAAqB,GAC5B,OAAO,CAAC,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI,CAAC,CA2EtC;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CAClC,SAAS,EAAE,MAAM,EAAE,EACnB,IAAI,EAAE,YAAY,EAClB,WAAW,EAAE,sBAAsB,EACnC,cAAc,EAAE,WAAW,EAAE,EAC7B,UAAU,EAAE,WAAW,GACrB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAShC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/serve/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AA0BH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,KAAK,EACX,WAAW,EACX,WAAW,EACX,sBAAsB,EACtB,gBAAgB,EAChB,aAAa,EACb,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3D,UAAU,yBAAyB;IAClC,QAAQ,EAAE,cAAc,CAAA;IACxB,WAAW,EAAE,sBAAsB,CAAA;IACnC,cAAc,EAAE,WAAW,EAAE,CAAA;CAC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACxC,SAAS,EAAE,MAAM,EAAE,EACnB,IAAI,EAAE,YAAY,EAClB,kBAAkB,EAAE,WAAW,GAC7B,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAqF3C;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,CAqBhF;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,gBAAgB,CAkBpF;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACzC,SAAS,EAAE,MAAM,EAAE,EACnB,GAAG,EAAE,WAAW,EAChB,WAAW,EAAE,sBAAsB,EACnC,cAAc,EAAE,WAAW,EAAE,GAC3B,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAgChC;AAED;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC9C,WAAW,EAAE,sBAAsB,EACnC,cAAc,EAAE,WAAW,EAAE,EAC7B,UAAU,EAAE,WAAW,EACvB,UAAU,GAAE,aAAkB,GAC5B,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA0BhC;AAmCD;;;;;;;;;GASG;AACH;;GAEG;AACH,UAAU,qBAAqB;IAC9B,MAAM,EAAE,gBAAgB,CAAA;IACxB,GAAG,EAAE,WAAW,CAAA;IAChB,IAAI,EAAE,YAAY,CAAA;IAClB,KAAK,EAAE,WAAW,CAAA;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CACnC,OAAO,EAAE,qBAAqB,GAC5B,OAAO,CAAC,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI,CAAC,CA2EtC;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CAClC,SAAS,EAAE,MAAM,EAAE,EACnB,IAAI,EAAE,YAAY,EAClB,WAAW,EAAE,sBAAsB,EACnC,cAAc,EAAE,WAAW,EAAE,EAC7B,UAAU,EAAE,WAAW,GACrB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAShC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/serve/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmBH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAmM3C;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCzF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/serve/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmBH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AA0M3C;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCzF"}
package/dist/index.js CHANGED
@@ -840,6 +840,11 @@ ${t}
840
840
  ${import_picocolors2.default.green(C)} ${import_picocolors2.default.reset(n)} ${import_picocolors2.default.gray(_2.repeat(Math.max(s - i - 1, 1)) + me)}
841
841
  ${c}
842
842
  ${import_picocolors2.default.gray(de + _2.repeat(s + 2) + pe)}
843
+ `);
844
+ };
845
+ var xe = (t = "") => {
846
+ process.stdout.write(`${import_picocolors2.default.gray(d2)} ${import_picocolors2.default.red(t)}
847
+
843
848
  `);
844
849
  };
845
850
  var Ie = (t = "") => {
@@ -14585,7 +14590,7 @@ function intro(title) {
14585
14590
  }
14586
14591
  var outro = {
14587
14592
  success: (message = "Done") => Se(green(message)),
14588
- cancelled: () => Se(dim("Cancelled")),
14593
+ cancelled: () => xe(dim("Cancelled")),
14589
14594
  error: (message) => Se(red(message)),
14590
14595
  warn: (message) => Se(yellow(message)),
14591
14596
  info: (message) => Se(dim(message))
@@ -14685,8 +14690,8 @@ async function getConfiguredEnvironments() {
14685
14690
  return configured;
14686
14691
  }
14687
14692
  function getEnvCredentials() {
14688
- const clientId = process.env.TIMEBACK_CLIENT_ID;
14689
- const clientSecret = process.env.TIMEBACK_CLIENT_SECRET;
14693
+ const clientId = process.env.TIMEBACK_API_CLIENT_ID ?? process.env.TIMEBACK_CLIENT_ID;
14694
+ const clientSecret = process.env.TIMEBACK_API_CLIENT_SECRET ?? process.env.TIMEBACK_CLIENT_SECRET;
14690
14695
  if (clientId && clientSecret) {
14691
14696
  const result = CredentialsSchema.safeParse({ clientId, clientSecret });
14692
14697
  if (result.success) {
@@ -14730,7 +14735,10 @@ async function validateEmailWithTimeback(environment, clientId, clientSecret, em
14730
14735
  });
14731
14736
  try {
14732
14737
  const page = await client.oneroster.users.list({
14733
- where: { email: email3 },
14738
+ where: {
14739
+ email: email3,
14740
+ status: "active"
14741
+ },
14734
14742
  limit: 1
14735
14743
  });
14736
14744
  if (page.data.length === 0) {
@@ -14758,7 +14766,7 @@ async function promptForCredentials(environment) {
14758
14766
  }
14759
14767
  });
14760
14768
  if (isCancelled(clientId))
14761
- return null;
14769
+ return { status: "cancelled" };
14762
14770
  const clientSecret = await ge({
14763
14771
  message: `Client Secret ${dim(`(${environment})`)}`,
14764
14772
  validate: (value) => {
@@ -14768,7 +14776,7 @@ async function promptForCredentials(environment) {
14768
14776
  }
14769
14777
  });
14770
14778
  if (isCancelled(clientSecret))
14771
- return null;
14779
+ return { status: "cancelled" };
14772
14780
  const email3 = await he({
14773
14781
  message: `Your email ${dim("(for fetching your OneRoster profile)")}`,
14774
14782
  placeholder: "you@example.com",
@@ -14781,33 +14789,47 @@ async function promptForCredentials(environment) {
14781
14789
  }
14782
14790
  });
14783
14791
  if (isCancelled(email3))
14784
- return null;
14792
+ return { status: "cancelled" };
14785
14793
  if (email3) {
14786
14794
  const s = Y2();
14787
14795
  s.start("Validating email...");
14788
14796
  const result = await validateEmailWithTimeback(environment, clientId, clientSecret, email3);
14789
14797
  if (!result.valid) {
14790
- s.stop(red("Email validation failed"));
14791
- M2.error(result.error ?? "Unknown error");
14798
+ const errorMsg = result.error ?? "Email validation failed";
14799
+ s.stop(red(errorMsg));
14792
14800
  M2.info("Please contact a Timeback admin to set up your account.");
14793
- return null;
14801
+ return {
14802
+ status: "error",
14803
+ error: errorMsg
14804
+ };
14794
14805
  }
14795
14806
  s.stop(green("Email validated"));
14796
14807
  }
14797
- return { clientId, clientSecret, email: email3 || undefined };
14808
+ return {
14809
+ status: "ok",
14810
+ credentials: { clientId, clientSecret, email: email3 || undefined }
14811
+ };
14798
14812
  }
14799
14813
  async function ensureCredentials(options) {
14800
14814
  const { env: env2, credentials, introTitle = "Timeback", skipIntro = false } = options;
14801
14815
  const existing = credentials[env2];
14802
- if (existing)
14803
- return existing;
14816
+ if (existing) {
14817
+ return { status: "ok", credentials: existing, source: "existing" };
14818
+ }
14804
14819
  if (!skipIntro) {
14805
14820
  intro(introTitle);
14806
14821
  }
14807
14822
  Me(`No credentials configured for ${env2}.`, "Credential setup required");
14808
- const newCreds = await promptForCredentials(env2);
14809
- if (!newCreds)
14810
- return null;
14823
+ const promptResult = await promptForCredentials(env2);
14824
+ if (promptResult.status === "cancelled") {
14825
+ outro.cancelled();
14826
+ return { status: "cancelled" };
14827
+ }
14828
+ if (promptResult.status === "error") {
14829
+ outro.error("Credential setup failed");
14830
+ return { status: "error", error: promptResult.error };
14831
+ }
14832
+ const newCreds = promptResult.credentials;
14811
14833
  const saved = await saveCredentials(env2, newCreds);
14812
14834
  if (saved) {
14813
14835
  M2.success(`${env2} credentials saved`);
@@ -14816,7 +14838,7 @@ async function ensureCredentials(options) {
14816
14838
  M2.warn(`Credentials not saved`);
14817
14839
  }
14818
14840
  credentials[env2] = newCreds;
14819
- return newCreds;
14841
+ return { status: "ok", credentials: newCreds, source: "prompted" };
14820
14842
  }
14821
14843
  // ../internal/cli-infra/src/config/playcademy.ts
14822
14844
  var FILE_PATTERNS = ["playcademy.config.ts", "playcademy.config.js", "playcademy.config.json"];
@@ -17483,8 +17505,8 @@ async function addCredentials(options = {}) {
17483
17505
  }
17484
17506
  isOverwriting = true;
17485
17507
  }
17486
- const creds = await promptForCredentials(env2);
17487
- if (creds === null) {
17508
+ const result = await promptForCredentials(env2);
17509
+ if (result.status === "cancelled") {
17488
17510
  if (!inline) {
17489
17511
  if (isOverwriting) {
17490
17512
  outro.info("Existing credentials unchanged");
@@ -17492,11 +17514,21 @@ async function addCredentials(options = {}) {
17492
17514
  outro.cancelled();
17493
17515
  }
17494
17516
  }
17495
- if (exitOnComplete)
17517
+ if (exitOnComplete) {
17496
17518
  process.exit(0);
17519
+ }
17520
+ return;
17521
+ }
17522
+ if (result.status === "error") {
17523
+ if (!inline) {
17524
+ outro.error("Credential setup failed");
17525
+ }
17526
+ if (exitOnComplete) {
17527
+ process.exit(1);
17528
+ }
17497
17529
  return;
17498
17530
  }
17499
- await saveCredentials(env2, creds);
17531
+ await saveCredentials(env2, result.credentials);
17500
17532
  M2.success(`${env2} credentials saved`);
17501
17533
  savedCount++;
17502
17534
  }
@@ -17763,14 +17795,21 @@ async function promptInitialCredentials(options = {}) {
17763
17795
  outro.cancelled();
17764
17796
  process.exit(0);
17765
17797
  }
17798
+ const configuredEnvs = [];
17766
17799
  for (const env2 of environments) {
17767
- const creds = await promptForCredentials(env2);
17768
- if (creds) {
17769
- await saveCredentials(env2, creds);
17770
- M2.success(`${env2} credentials saved`);
17800
+ const result = await promptForCredentials(env2);
17801
+ if (result.status === "cancelled") {
17802
+ outro.cancelled();
17803
+ process.exit(0);
17771
17804
  }
17805
+ if (result.status === "error") {
17806
+ outro.error("Credential setup failed");
17807
+ process.exit(1);
17808
+ }
17809
+ await saveCredentials(env2, result.credentials);
17810
+ M2.success(`${env2} credentials saved`);
17811
+ configuredEnvs.push(env2);
17772
17812
  }
17773
- const configuredEnvs = environments;
17774
17813
  let selectedEnv;
17775
17814
  if (configuredEnvs.length === 1) {
17776
17815
  selectedEnv = configuredEnvs[0];
@@ -17948,10 +17987,11 @@ function buildUserConfigFromCourses(courses) {
17948
17987
  };
17949
17988
  }
17950
17989
  async function resolveFromCourseIds(courseIds, env2, credentials, configuredEnvs) {
17951
- const creds = await ensureCredentials({ env: env2, credentials, skipIntro: true });
17952
- if (!creds) {
17990
+ const ensureResult = await ensureCredentials({ env: env2, credentials, skipIntro: true });
17991
+ if (ensureResult.status !== "ok") {
17953
17992
  return null;
17954
17993
  }
17994
+ const creds = ensureResult.credentials;
17955
17995
  const courses = await fetchCourses(creds, env2, courseIds);
17956
17996
  if (courses.length === 0) {
17957
17997
  M2.warn("No courses found for the provided IDs.");
@@ -21330,7 +21370,6 @@ function createEnvMiddleware(ctx, manager) {
21330
21370
  }, 400);
21331
21371
  }
21332
21372
  if (!manager.has(env2)) {
21333
- log7.warn("Environment not configured", { env: env2 });
21334
21373
  const error48 = createStudioError("ENV_NOT_CONFIGURED", `Environment '${env2}' not configured`);
21335
21374
  return c.json({
21336
21375
  success: false,
@@ -21860,10 +21899,14 @@ function startServer(ctx, serverConfig, configFile) {
21860
21899
 
21861
21900
  // src/cli/commands/serve/index.ts
21862
21901
  async function launchServer(serverConfig, userConfig, credentials, env2, opts, configFile) {
21863
- const creds = await ensureCredentials({ env: env2, credentials, skipIntro: true });
21864
- if (!creds) {
21902
+ const ensureResult = await ensureCredentials({ env: env2, credentials, skipIntro: true });
21903
+ if (ensureResult.status === "cancelled") {
21904
+ process.exit(0);
21905
+ }
21906
+ if (ensureResult.status === "error") {
21865
21907
  process.exit(1);
21866
21908
  }
21909
+ const creds = ensureResult.credentials;
21867
21910
  const derivedSensors = await resolveSensors({
21868
21911
  config: userConfig,
21869
21912
  env: env2,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "timeback-studio",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -27,7 +27,7 @@
27
27
  "dependencies": {
28
28
  "@clack/prompts": "^0.11.0",
29
29
  "@hono/node-server": "^1.19.7",
30
- "@timeback/core": "0.1.3",
30
+ "@timeback/core": "0.1.4",
31
31
  "c12": "^3.3.3",
32
32
  "colorette": "^2.0.20",
33
33
  "commander": "^14.0.2",