timeback 0.1.9 → 0.1.10

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 (2) hide show
  1. package/dist/cli.js +275 -35
  2. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -17383,6 +17383,8 @@ var ActivityCompletedInput = exports_external.object({
17383
17383
  metricsId: exports_external.string().optional(),
17384
17384
  id: exports_external.string().optional(),
17385
17385
  extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional(),
17386
+ edApp: exports_external.union([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]).optional(),
17387
+ session: exports_external.union([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]).optional(),
17386
17388
  attempt: exports_external.number().int().min(1).optional(),
17387
17389
  generatedExtensions: exports_external.object({
17388
17390
  pctCompleteApp: exports_external.number().optional()
@@ -17395,7 +17397,9 @@ var TimeSpentInput = exports_external.object({
17395
17397
  eventTime: IsoDateTimeString.optional(),
17396
17398
  metricsId: exports_external.string().optional(),
17397
17399
  id: exports_external.string().optional(),
17398
- extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
17400
+ extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional(),
17401
+ edApp: exports_external.union([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]).optional(),
17402
+ session: exports_external.union([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]).optional()
17399
17403
  }).strict();
17400
17404
  var TimebackEvent = exports_external.union([TimebackActivityEvent, TimebackTimeSpentEvent]);
17401
17405
  var CaliperEnvelope = exports_external.object({
@@ -17583,7 +17587,7 @@ var TimebackConfig = exports_external.object({
17583
17587
  message: "Each course must have an effective sensor. Either set `sensor` explicitly (top-level or per-course), or provide a `launchUrl` so sensor can be derived from its origin.",
17584
17588
  path: ["courses"]
17585
17589
  });
17586
- var EdubridgeDateString = IsoDateTimeString;
17590
+ var EdubridgeDateString = exports_external.union([IsoDateTimeString, IsoDateString]);
17587
17591
  var EduBridgeEnrollment = exports_external.object({
17588
17592
  id: exports_external.string(),
17589
17593
  role: exports_external.string(),
@@ -18578,7 +18582,9 @@ function createActivityEvent(input) {
18578
18582
  ...input.attempt === undefined ? {} : { attempt: input.attempt },
18579
18583
  ...input.generatedExtensions ? { extensions: input.generatedExtensions } : {}
18580
18584
  },
18581
- extensions: input.extensions
18585
+ extensions: input.extensions,
18586
+ ...input.edApp === undefined ? {} : { edApp: input.edApp },
18587
+ ...input.session === undefined ? {} : { session: input.session }
18582
18588
  };
18583
18589
  }
18584
18590
  function createTimeSpentEvent(input) {
@@ -18598,7 +18604,9 @@ function createTimeSpentEvent(input) {
18598
18604
  type: "TimebackTimeSpentMetricsCollection",
18599
18605
  items: input.metrics
18600
18606
  },
18601
- extensions: input.extensions
18607
+ extensions: input.extensions,
18608
+ ...input.edApp === undefined ? {} : { edApp: input.edApp },
18609
+ ...input.session === undefined ? {} : { session: input.session }
18602
18610
  };
18603
18611
  }
18604
18612
 
@@ -33811,6 +33819,8 @@ var ActivityCompletedInput2 = exports_external2.object({
33811
33819
  metricsId: exports_external2.string().optional(),
33812
33820
  id: exports_external2.string().optional(),
33813
33821
  extensions: exports_external2.record(exports_external2.string(), exports_external2.unknown()).optional(),
33822
+ edApp: exports_external2.union([exports_external2.string(), exports_external2.record(exports_external2.string(), exports_external2.unknown())]).optional(),
33823
+ session: exports_external2.union([exports_external2.string(), exports_external2.record(exports_external2.string(), exports_external2.unknown())]).optional(),
33814
33824
  attempt: exports_external2.number().int().min(1).optional(),
33815
33825
  generatedExtensions: exports_external2.object({
33816
33826
  pctCompleteApp: exports_external2.number().optional()
@@ -33823,7 +33833,9 @@ var TimeSpentInput2 = exports_external2.object({
33823
33833
  eventTime: IsoDateTimeString2.optional(),
33824
33834
  metricsId: exports_external2.string().optional(),
33825
33835
  id: exports_external2.string().optional(),
33826
- extensions: exports_external2.record(exports_external2.string(), exports_external2.unknown()).optional()
33836
+ extensions: exports_external2.record(exports_external2.string(), exports_external2.unknown()).optional(),
33837
+ edApp: exports_external2.union([exports_external2.string(), exports_external2.record(exports_external2.string(), exports_external2.unknown())]).optional(),
33838
+ session: exports_external2.union([exports_external2.string(), exports_external2.record(exports_external2.string(), exports_external2.unknown())]).optional()
33827
33839
  }).strict();
33828
33840
  var TimebackEvent2 = exports_external2.union([TimebackActivityEvent2, TimebackTimeSpentEvent2]);
33829
33841
  var CaliperEnvelope2 = exports_external2.object({
@@ -34011,7 +34023,7 @@ var TimebackConfig2 = exports_external2.object({
34011
34023
  message: "Each course must have an effective sensor. Either set `sensor` explicitly (top-level or per-course), or provide a `launchUrl` so sensor can be derived from its origin.",
34012
34024
  path: ["courses"]
34013
34025
  });
34014
- var EdubridgeDateString2 = IsoDateTimeString2;
34026
+ var EdubridgeDateString2 = exports_external2.union([IsoDateTimeString2, IsoDateString2]);
34015
34027
  var EduBridgeEnrollment2 = exports_external2.object({
34016
34028
  id: exports_external2.string(),
34017
34029
  role: exports_external2.string(),
@@ -51297,6 +51309,8 @@ var ActivityCompletedInput3 = exports_external3.object({
51297
51309
  metricsId: exports_external3.string().optional(),
51298
51310
  id: exports_external3.string().optional(),
51299
51311
  extensions: exports_external3.record(exports_external3.string(), exports_external3.unknown()).optional(),
51312
+ edApp: exports_external3.union([exports_external3.string(), exports_external3.record(exports_external3.string(), exports_external3.unknown())]).optional(),
51313
+ session: exports_external3.union([exports_external3.string(), exports_external3.record(exports_external3.string(), exports_external3.unknown())]).optional(),
51300
51314
  attempt: exports_external3.number().int().min(1).optional(),
51301
51315
  generatedExtensions: exports_external3.object({
51302
51316
  pctCompleteApp: exports_external3.number().optional()
@@ -51309,7 +51323,9 @@ var TimeSpentInput3 = exports_external3.object({
51309
51323
  eventTime: IsoDateTimeString3.optional(),
51310
51324
  metricsId: exports_external3.string().optional(),
51311
51325
  id: exports_external3.string().optional(),
51312
- extensions: exports_external3.record(exports_external3.string(), exports_external3.unknown()).optional()
51326
+ extensions: exports_external3.record(exports_external3.string(), exports_external3.unknown()).optional(),
51327
+ edApp: exports_external3.union([exports_external3.string(), exports_external3.record(exports_external3.string(), exports_external3.unknown())]).optional(),
51328
+ session: exports_external3.union([exports_external3.string(), exports_external3.record(exports_external3.string(), exports_external3.unknown())]).optional()
51313
51329
  }).strict();
51314
51330
  var TimebackEvent3 = exports_external3.union([TimebackActivityEvent3, TimebackTimeSpentEvent3]);
51315
51331
  var CaliperEnvelope3 = exports_external3.object({
@@ -51497,7 +51513,7 @@ var TimebackConfig3 = exports_external3.object({
51497
51513
  message: "Each course must have an effective sensor. Either set `sensor` explicitly (top-level or per-course), or provide a `launchUrl` so sensor can be derived from its origin.",
51498
51514
  path: ["courses"]
51499
51515
  });
51500
- var EdubridgeDateString3 = IsoDateTimeString3;
51516
+ var EdubridgeDateString3 = exports_external3.union([IsoDateTimeString3, IsoDateString3]);
51501
51517
  var EduBridgeEnrollment3 = exports_external3.object({
51502
51518
  id: exports_external3.string(),
51503
51519
  role: exports_external3.string(),
@@ -68960,6 +68976,8 @@ var ActivityCompletedInput4 = exports_external4.object({
68960
68976
  metricsId: exports_external4.string().optional(),
68961
68977
  id: exports_external4.string().optional(),
68962
68978
  extensions: exports_external4.record(exports_external4.string(), exports_external4.unknown()).optional(),
68979
+ edApp: exports_external4.union([exports_external4.string(), exports_external4.record(exports_external4.string(), exports_external4.unknown())]).optional(),
68980
+ session: exports_external4.union([exports_external4.string(), exports_external4.record(exports_external4.string(), exports_external4.unknown())]).optional(),
68963
68981
  attempt: exports_external4.number().int().min(1).optional(),
68964
68982
  generatedExtensions: exports_external4.object({
68965
68983
  pctCompleteApp: exports_external4.number().optional()
@@ -68972,7 +68990,9 @@ var TimeSpentInput4 = exports_external4.object({
68972
68990
  eventTime: IsoDateTimeString4.optional(),
68973
68991
  metricsId: exports_external4.string().optional(),
68974
68992
  id: exports_external4.string().optional(),
68975
- extensions: exports_external4.record(exports_external4.string(), exports_external4.unknown()).optional()
68993
+ extensions: exports_external4.record(exports_external4.string(), exports_external4.unknown()).optional(),
68994
+ edApp: exports_external4.union([exports_external4.string(), exports_external4.record(exports_external4.string(), exports_external4.unknown())]).optional(),
68995
+ session: exports_external4.union([exports_external4.string(), exports_external4.record(exports_external4.string(), exports_external4.unknown())]).optional()
68976
68996
  }).strict();
68977
68997
  var TimebackEvent4 = exports_external4.union([TimebackActivityEvent4, TimebackTimeSpentEvent4]);
68978
68998
  var CaliperEnvelope4 = exports_external4.object({
@@ -69160,7 +69180,7 @@ var TimebackConfig4 = exports_external4.object({
69160
69180
  message: "Each course must have an effective sensor. Either set `sensor` explicitly (top-level or per-course), or provide a `launchUrl` so sensor can be derived from its origin.",
69161
69181
  path: ["courses"]
69162
69182
  });
69163
- var EdubridgeDateString4 = IsoDateTimeString4;
69183
+ var EdubridgeDateString4 = exports_external4.union([IsoDateTimeString4, IsoDateString4]);
69164
69184
  var EduBridgeEnrollment4 = exports_external4.object({
69165
69185
  id: exports_external4.string(),
69166
69186
  role: exports_external4.string(),
@@ -85647,6 +85667,8 @@ var ActivityCompletedInput5 = exports_external5.object({
85647
85667
  metricsId: exports_external5.string().optional(),
85648
85668
  id: exports_external5.string().optional(),
85649
85669
  extensions: exports_external5.record(exports_external5.string(), exports_external5.unknown()).optional(),
85670
+ edApp: exports_external5.union([exports_external5.string(), exports_external5.record(exports_external5.string(), exports_external5.unknown())]).optional(),
85671
+ session: exports_external5.union([exports_external5.string(), exports_external5.record(exports_external5.string(), exports_external5.unknown())]).optional(),
85650
85672
  attempt: exports_external5.number().int().min(1).optional(),
85651
85673
  generatedExtensions: exports_external5.object({
85652
85674
  pctCompleteApp: exports_external5.number().optional()
@@ -85659,7 +85681,9 @@ var TimeSpentInput5 = exports_external5.object({
85659
85681
  eventTime: IsoDateTimeString5.optional(),
85660
85682
  metricsId: exports_external5.string().optional(),
85661
85683
  id: exports_external5.string().optional(),
85662
- extensions: exports_external5.record(exports_external5.string(), exports_external5.unknown()).optional()
85684
+ extensions: exports_external5.record(exports_external5.string(), exports_external5.unknown()).optional(),
85685
+ edApp: exports_external5.union([exports_external5.string(), exports_external5.record(exports_external5.string(), exports_external5.unknown())]).optional(),
85686
+ session: exports_external5.union([exports_external5.string(), exports_external5.record(exports_external5.string(), exports_external5.unknown())]).optional()
85663
85687
  }).strict();
85664
85688
  var TimebackEvent5 = exports_external5.union([TimebackActivityEvent5, TimebackTimeSpentEvent5]);
85665
85689
  var CaliperEnvelope5 = exports_external5.object({
@@ -85847,7 +85871,7 @@ var TimebackConfig5 = exports_external5.object({
85847
85871
  message: "Each course must have an effective sensor. Either set `sensor` explicitly (top-level or per-course), or provide a `launchUrl` so sensor can be derived from its origin.",
85848
85872
  path: ["courses"]
85849
85873
  });
85850
- var EdubridgeDateString5 = IsoDateTimeString5;
85874
+ var EdubridgeDateString5 = exports_external5.union([IsoDateTimeString5, IsoDateString5]);
85851
85875
  var EduBridgeEnrollment5 = exports_external5.object({
85852
85876
  id: exports_external5.string(),
85853
85877
  role: exports_external5.string(),
@@ -102072,13 +102096,14 @@ async function validateEmailWithTimeback(environment, clientId, clientSecret, em
102072
102096
  if (page.data.length === 0) {
102073
102097
  return {
102074
102098
  valid: false,
102099
+ reason: "not_found",
102075
102100
  error: `No user found with email "${email9}" in ${environment}`
102076
102101
  };
102077
102102
  }
102078
102103
  return { valid: true };
102079
102104
  } catch (error58) {
102080
102105
  const message = error58 instanceof Error ? error58.message : "Unknown error";
102081
- return { valid: false, error: `Failed to validate email: ${message}` };
102106
+ return { valid: false, reason: "api_error", error: `Failed to validate email: ${message}` };
102082
102107
  }
102083
102108
  }
102084
102109
 
@@ -102318,6 +102343,8 @@ var ActivityCompletedInput6 = exports_external6.object({
102318
102343
  metricsId: exports_external6.string().optional(),
102319
102344
  id: exports_external6.string().optional(),
102320
102345
  extensions: exports_external6.record(exports_external6.string(), exports_external6.unknown()).optional(),
102346
+ edApp: exports_external6.union([exports_external6.string(), exports_external6.record(exports_external6.string(), exports_external6.unknown())]).optional(),
102347
+ session: exports_external6.union([exports_external6.string(), exports_external6.record(exports_external6.string(), exports_external6.unknown())]).optional(),
102321
102348
  attempt: exports_external6.number().int().min(1).optional(),
102322
102349
  generatedExtensions: exports_external6.object({
102323
102350
  pctCompleteApp: exports_external6.number().optional()
@@ -102330,7 +102357,9 @@ var TimeSpentInput6 = exports_external6.object({
102330
102357
  eventTime: IsoDateTimeString6.optional(),
102331
102358
  metricsId: exports_external6.string().optional(),
102332
102359
  id: exports_external6.string().optional(),
102333
- extensions: exports_external6.record(exports_external6.string(), exports_external6.unknown()).optional()
102360
+ extensions: exports_external6.record(exports_external6.string(), exports_external6.unknown()).optional(),
102361
+ edApp: exports_external6.union([exports_external6.string(), exports_external6.record(exports_external6.string(), exports_external6.unknown())]).optional(),
102362
+ session: exports_external6.union([exports_external6.string(), exports_external6.record(exports_external6.string(), exports_external6.unknown())]).optional()
102334
102363
  }).strict();
102335
102364
  var TimebackEvent6 = exports_external6.union([TimebackActivityEvent6, TimebackTimeSpentEvent6]);
102336
102365
  var CaliperEnvelope6 = exports_external6.object({
@@ -102520,7 +102549,7 @@ var TimebackConfig6 = exports_external6.object({
102520
102549
  path: ["courses"]
102521
102550
  });
102522
102551
  // ../types/src/zod/edubridge.ts
102523
- var EdubridgeDateString6 = IsoDateTimeString6;
102552
+ var EdubridgeDateString6 = exports_external6.union([IsoDateTimeString6, IsoDateString6]);
102524
102553
  var EduBridgeEnrollment6 = exports_external6.object({
102525
102554
  id: exports_external6.string(),
102526
102555
  role: exports_external6.string(),
@@ -105596,7 +105625,7 @@ function logErrors(errors3) {
105596
105625
  }
105597
105626
 
105598
105627
  // src/version.ts
105599
- var cliVersion = "0.1.9";
105628
+ var cliVersion = "0.1.10";
105600
105629
 
105601
105630
  // src/lib/metadata.ts
105602
105631
  function deepMerge(base, override) {
@@ -112336,6 +112365,7 @@ import { spawn as spawn2 } from "node:child_process";
112336
112365
  import { existsSync as existsSync22 } from "node:fs";
112337
112366
  import { resolve as resolve42 } from "node:path";
112338
112367
  import { readFile as readFile32, writeFile as writeFile22 } from "node:fs/promises";
112368
+ import { randomUUID } from "node:crypto";
112339
112369
  import { basename as basename22 } from "node:path";
112340
112370
  import { createServer as createServerHTTP } from "http";
112341
112371
  import { Http2ServerRequest as Http2ServerRequest2 } from "http2";
@@ -126965,13 +126995,14 @@ async function validateEmailWithTimeback2(environment, clientId, clientSecret, e
126965
126995
  if (page.data.length === 0) {
126966
126996
  return {
126967
126997
  valid: false,
126998
+ reason: "not_found",
126968
126999
  error: `No user found with email "${email32}" in ${environment}`
126969
127000
  };
126970
127001
  }
126971
127002
  return { valid: true };
126972
127003
  } catch (error482) {
126973
127004
  const message = error482 instanceof Error ? error482.message : "Unknown error";
126974
- return { valid: false, error: `Failed to validate email: ${message}` };
127005
+ return { valid: false, reason: "api_error", error: `Failed to validate email: ${message}` };
126975
127006
  }
126976
127007
  }
126977
127008
  async function promptForCredentials2(environment) {
@@ -127264,6 +127295,8 @@ var ActivityCompletedInput8 = exports_external8.object({
127264
127295
  metricsId: exports_external8.string().optional(),
127265
127296
  id: exports_external8.string().optional(),
127266
127297
  extensions: exports_external8.record(exports_external8.string(), exports_external8.unknown()).optional(),
127298
+ edApp: exports_external8.union([exports_external8.string(), exports_external8.record(exports_external8.string(), exports_external8.unknown())]).optional(),
127299
+ session: exports_external8.union([exports_external8.string(), exports_external8.record(exports_external8.string(), exports_external8.unknown())]).optional(),
127267
127300
  attempt: exports_external8.number().int().min(1).optional(),
127268
127301
  generatedExtensions: exports_external8.object({
127269
127302
  pctCompleteApp: exports_external8.number().optional()
@@ -127276,7 +127309,9 @@ var TimeSpentInput8 = exports_external8.object({
127276
127309
  eventTime: IsoDateTimeString8.optional(),
127277
127310
  metricsId: exports_external8.string().optional(),
127278
127311
  id: exports_external8.string().optional(),
127279
- extensions: exports_external8.record(exports_external8.string(), exports_external8.unknown()).optional()
127312
+ extensions: exports_external8.record(exports_external8.string(), exports_external8.unknown()).optional(),
127313
+ edApp: exports_external8.union([exports_external8.string(), exports_external8.record(exports_external8.string(), exports_external8.unknown())]).optional(),
127314
+ session: exports_external8.union([exports_external8.string(), exports_external8.record(exports_external8.string(), exports_external8.unknown())]).optional()
127280
127315
  }).strict();
127281
127316
  var TimebackEvent8 = exports_external8.union([TimebackActivityEvent8, TimebackTimeSpentEvent8]);
127282
127317
  var CaliperEnvelope8 = exports_external8.object({
@@ -127464,7 +127499,7 @@ var TimebackConfig8 = exports_external8.object({
127464
127499
  message: "Each course must have an effective sensor. Either set `sensor` explicitly (top-level or per-course), or provide a `launchUrl` so sensor can be derived from its origin.",
127465
127500
  path: ["courses"]
127466
127501
  });
127467
- var EdubridgeDateString8 = IsoDateTimeString8;
127502
+ var EdubridgeDateString8 = exports_external8.union([IsoDateTimeString8, IsoDateString8]);
127468
127503
  var EduBridgeEnrollment8 = exports_external8.object({
127469
127504
  id: exports_external8.string(),
127470
127505
  role: exports_external8.string(),
@@ -129700,6 +129735,180 @@ async function addCredentials2(options = {}) {
129700
129735
  if (exitOnComplete)
129701
129736
  process.exit(0);
129702
129737
  }
129738
+ async function promptName(label) {
129739
+ const value = await he2({
129740
+ message: label,
129741
+ placeholder: "Enter name",
129742
+ validate: (v2) => {
129743
+ if (!v2?.trim())
129744
+ return `${label} is required`;
129745
+ }
129746
+ });
129747
+ if (isCancelled2(value))
129748
+ return null;
129749
+ return value.trim();
129750
+ }
129751
+ async function searchOrganizations(client, query) {
129752
+ const allOrgs = await client.oneroster.orgs.listAll({
129753
+ where: { status: "active" },
129754
+ max: 100
129755
+ });
129756
+ if (!query.trim())
129757
+ return allOrgs;
129758
+ const lowerQuery = query.toLowerCase().trim();
129759
+ return allOrgs.filter((org) => org.name?.toLowerCase().includes(lowerQuery));
129760
+ }
129761
+ async function searchForOrganization(client) {
129762
+ const query = await he2({
129763
+ message: "Search for organization",
129764
+ placeholder: "Enter organization name to search"
129765
+ });
129766
+ if (isCancelled2(query))
129767
+ return null;
129768
+ const s = Y22();
129769
+ s.start("Searching organizations...");
129770
+ let results;
129771
+ try {
129772
+ results = await searchOrganizations(client, query);
129773
+ s.stop(green2(`Found ${results.length} result${results.length === 1 ? "" : "s"}`));
129774
+ } catch (error482) {
129775
+ s.stop(red2("Failed to search organizations"));
129776
+ M22.error(error482 instanceof Error ? error482.message : "Unknown error");
129777
+ return null;
129778
+ }
129779
+ if (results.length === 0) {
129780
+ M22.warn("No organizations found matching your search");
129781
+ return promptOrganization(client);
129782
+ }
129783
+ const validResults = results.filter((org) => org.sourcedId);
129784
+ if (validResults.length === 0) {
129785
+ M22.warn("No valid organizations found (missing IDs)");
129786
+ return promptOrganization(client);
129787
+ }
129788
+ const options = validResults.map((org) => ({
129789
+ value: org.sourcedId,
129790
+ label: org.name ?? "Unnamed Organization"
129791
+ }));
129792
+ options.push({ value: "__back__", label: `${dim2("←")} Back to options` });
129793
+ const selection = await ve2({
129794
+ message: "Select an organization",
129795
+ options
129796
+ });
129797
+ if (isCancelled2(selection))
129798
+ return null;
129799
+ if (selection === "__back__") {
129800
+ return promptOrganization(client);
129801
+ }
129802
+ return validResults.find((org) => org.sourcedId === selection) ?? null;
129803
+ }
129804
+ async function promptOrganization(client) {
129805
+ const action = await ve2({
129806
+ message: "Organization",
129807
+ options: [
129808
+ { value: "search", label: "Search for existing organization" },
129809
+ { value: "create", label: "Create new organization" }
129810
+ ]
129811
+ });
129812
+ if (isCancelled2(action))
129813
+ return null;
129814
+ if (action === "create") {
129815
+ return createNewOrganization(client);
129816
+ }
129817
+ return searchForOrganization(client);
129818
+ }
129819
+ async function createNewOrganization(client) {
129820
+ const name = await he2({
129821
+ message: "Organization name",
129822
+ placeholder: "Enter organization name",
129823
+ validate: (v2) => {
129824
+ if (!v2?.trim())
129825
+ return "Organization name is required";
129826
+ }
129827
+ });
129828
+ if (isCancelled2(name))
129829
+ return null;
129830
+ const s = Y22();
129831
+ s.start("Creating organization...");
129832
+ const sourcedId = randomUUID();
129833
+ try {
129834
+ await client.oneroster.orgs.create({
129835
+ sourcedId,
129836
+ name: name.trim(),
129837
+ type: "school",
129838
+ status: "active"
129839
+ });
129840
+ const organization = await client.oneroster.orgs.get(sourcedId);
129841
+ s.stop(green2(`Organization "${name}" created`));
129842
+ return organization;
129843
+ } catch (error482) {
129844
+ s.stop(red2("Failed to create organization"));
129845
+ M22.error(error482 instanceof Error ? error482.message : "Unknown error");
129846
+ return null;
129847
+ }
129848
+ }
129849
+ async function createUser(client, email32, givenName, familyName, organizationId) {
129850
+ const s = Y22();
129851
+ s.start("Creating account...");
129852
+ const sourcedId = randomUUID();
129853
+ try {
129854
+ await client.oneroster.users.create({
129855
+ sourcedId,
129856
+ givenName,
129857
+ familyName,
129858
+ email: email32.toLowerCase(),
129859
+ enabledUser: true,
129860
+ status: "active",
129861
+ roles: [
129862
+ {
129863
+ roleType: "primary",
129864
+ role: "administrator",
129865
+ org: { sourcedId: organizationId }
129866
+ }
129867
+ ]
129868
+ });
129869
+ const user = await client.oneroster.users.get(sourcedId);
129870
+ s.stop(green2("Account created successfully"));
129871
+ return user;
129872
+ } catch (error482) {
129873
+ s.stop(red2("Failed to create account"));
129874
+ M22.error(error482 instanceof Error ? error482.message : "Unknown error");
129875
+ return null;
129876
+ }
129877
+ }
129878
+ async function createAccountFlow(options) {
129879
+ const { environment, clientId, clientSecret, email: email32 } = options;
129880
+ M22.info("");
129881
+ M22.info(`No account found for ${dim2(email32)} in ${environment}`);
129882
+ const shouldCreate = await ye2({
129883
+ message: "Would you like to create a new account?",
129884
+ initialValue: true
129885
+ });
129886
+ if (isCancelled2(shouldCreate)) {
129887
+ return { success: false };
129888
+ }
129889
+ if (!shouldCreate) {
129890
+ return { success: false, declined: true };
129891
+ }
129892
+ const client = new TimebackClient({
129893
+ env: environment,
129894
+ auth: { clientId, clientSecret }
129895
+ });
129896
+ const givenName = await promptName("First name");
129897
+ if (!givenName)
129898
+ return { success: false };
129899
+ const familyName = await promptName("Last name");
129900
+ if (!familyName)
129901
+ return { success: false };
129902
+ const organization = await promptOrganization(client);
129903
+ if (!organization?.sourcedId)
129904
+ return { success: false };
129905
+ const user = await createUser(client, email32, givenName, familyName, organization.sourcedId);
129906
+ if (!user) {
129907
+ return { success: false };
129908
+ }
129909
+ M22.success(`Account created for ${user.givenName} ${user.familyName}`);
129910
+ return { success: true };
129911
+ }
129703
129912
  async function updateEmail2(options = {}) {
129704
129913
  const { exitOnComplete = true, inline = false } = options;
129705
129914
  const configuredEnvs = [];
@@ -129767,32 +129976,58 @@ async function updateEmail2(options = {}) {
129767
129976
  return;
129768
129977
  }
129769
129978
  const emailUnchanged = email32 === (currentEmail ?? "");
129770
- if (emailUnchanged) {
129771
- if (!inline)
129772
- outro2.info("Email unchanged");
129773
- if (exitOnComplete)
129774
- process.exit(0);
129775
- return;
129776
- }
129777
129979
  if (email32) {
129778
129980
  const s = Y22();
129779
- s.start("Validating email...");
129981
+ s.start("Checking account...");
129780
129982
  const result = await validateEmailWithTimeback2(targetEnv, currentCreds.clientId, currentCreds.clientSecret, email32);
129781
129983
  if (!result.valid) {
129782
- s.stop(red2("Email validation failed"));
129783
- M22.error(result.error ?? "Unknown error");
129784
- M22.info("Please contact a Timeback admin to set up your account.");
129984
+ if (result.reason !== "not_found") {
129985
+ s.stop(red2("Account check failed"));
129986
+ M22.error(result.error ?? "Unknown error");
129987
+ if (!inline)
129988
+ outro2.error("Account check failed");
129989
+ if (exitOnComplete)
129990
+ process.exit(1);
129991
+ return;
129992
+ }
129993
+ s.stop(red2("No account found"));
129994
+ const { success: accountCreated, declined } = await createAccountFlow({
129995
+ environment: targetEnv,
129996
+ clientId: currentCreds.clientId,
129997
+ clientSecret: currentCreds.clientSecret,
129998
+ email: email32
129999
+ });
130000
+ if (!emailUnchanged) {
130001
+ await saveCredentials2(targetEnv, {
130002
+ ...currentCreds,
130003
+ email: email32
130004
+ });
130005
+ M22.success(`Email saved for ${targetEnv}`);
130006
+ }
130007
+ if (!inline) {
130008
+ if (accountCreated || declined) {
130009
+ outro2.success();
130010
+ } else {
130011
+ outro2.info("Setup incomplete - run this command again to finish");
130012
+ }
130013
+ }
130014
+ if (exitOnComplete)
130015
+ process.exit(0);
130016
+ return;
130017
+ }
130018
+ s.stop(green2("Account verified"));
130019
+ if (emailUnchanged) {
129785
130020
  if (!inline)
129786
- outro2.error("Email validation failed");
130021
+ outro2.info("Email unchanged");
129787
130022
  if (exitOnComplete)
129788
- process.exit(1);
130023
+ process.exit(0);
129789
130024
  return;
129790
130025
  }
129791
130026
  await saveCredentials2(targetEnv, {
129792
130027
  ...currentCreds,
129793
130028
  email: email32
129794
130029
  });
129795
- s.stop(green2(`Email updated for ${targetEnv}`));
130030
+ M22.success(`Email updated for ${targetEnv}`);
129796
130031
  if (!inline)
129797
130032
  outro2.success();
129798
130033
  if (exitOnComplete)
@@ -132385,7 +132620,8 @@ var cors = (options) => {
132385
132620
  async function handleBootstrap(c, ctx) {
132386
132621
  const { bootstrap } = c.get("services");
132387
132622
  const env22 = c.get("env");
132388
- const email32 = ctx.credentials[env22]?.email;
132623
+ const freshCredentials = await getSavedCredentials2(env22);
132624
+ const email32 = freshCredentials?.email;
132389
132625
  const courseIds = ctx.userConfig.courseIds[env22];
132390
132626
  const result = await bootstrap.getBootstrap({ email: email32, courseIds });
132391
132627
  return c.json(result);
@@ -133303,11 +133539,15 @@ class StatusService {
133303
133539
  }
133304
133540
  async getStatus() {
133305
133541
  const configuredEnvironments = await getConfiguredEnvironments2();
133542
+ const [stagingCreds, productionCreds] = await Promise.all([
133543
+ getSavedCredentials2("staging"),
133544
+ getSavedCredentials2("production")
133545
+ ]);
133306
133546
  return {
133307
133547
  config: this.ctx.userConfig,
133308
133548
  environment: this.ctx.defaultEnvironment,
133309
133549
  configuredEnvironments,
133310
- hasEmail: !!this.ctx.credentials.staging?.email || !!this.ctx.credentials.production?.email
133550
+ hasEmail: !!stagingCreds?.email || !!productionCreds?.email
133311
133551
  };
133312
133552
  }
133313
133553
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "timeback",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "timeback": "./dist/cli.js"
@@ -25,7 +25,7 @@
25
25
  "zod": "^4.2.1"
26
26
  },
27
27
  "devDependencies": {
28
- "@timeback/caliper": "0.1.3",
28
+ "@timeback/caliper": "0.1.4",
29
29
  "@timeback/core": "0.1.4",
30
30
  "@timeback/edubridge": "0.1.3",
31
31
  "@timeback/internal-cli-infra": "0.0.0",
@@ -37,7 +37,7 @@
37
37
  "@timeback/types": "0.0.0",
38
38
  "@types/bun": "latest",
39
39
  "bun-plugin-dts": "^0.3.0",
40
- "timeback-studio": "0.1.6"
40
+ "timeback-studio": "0.1.7"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "typescript": "^5"