timeback 0.2.2-beta.20260401223329 → 0.2.2-beta.20260402000431
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/cli.js +281 -120
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -35125,8 +35125,13 @@ var CredentialsSchema = exports_external.object({
|
|
|
35125
35125
|
clientSecret: NonEmptyString2,
|
|
35126
35126
|
email: exports_external.email("Valid email is required").optional()
|
|
35127
35127
|
});
|
|
35128
|
-
var
|
|
35129
|
-
|
|
35128
|
+
var StoredCredentialsSchema = exports_external.object({
|
|
35129
|
+
clientId: NonEmptyString2.optional(),
|
|
35130
|
+
clientSecret: NonEmptyString2.optional(),
|
|
35131
|
+
email: exports_external.email("Valid email is required").optional()
|
|
35132
|
+
}).refine((value) => value.email || value.clientId && value.clientSecret, "Saved credentials must include an email or a client ID/client secret pair").refine((value) => Boolean(value.clientId) === Boolean(value.clientSecret), "Saved credentials must include both client ID and client secret");
|
|
35133
|
+
var ClientIdSchema = exports_external.string().min(1, "Client ID is required").max(128, "Client ID must be at most 128 characters").regex(/^[\w+]+$/, "Client ID must contain only letters, digits, underscores, or +");
|
|
35134
|
+
var ClientSecretSchema = exports_external.string().min(24, "Client secret must be at least 24 characters").max(64, "Client secret must be at most 64 characters").regex(/^[\w+]+$/, "Client secret must contain only letters, digits, underscores, or +");
|
|
35130
35135
|
var MasteryTrackCredentialsSchema = exports_external.object({
|
|
35131
35136
|
apiKey: NonEmptyString2,
|
|
35132
35137
|
email: exports_external.email("Valid email is required")
|
|
@@ -36358,24 +36363,41 @@ async function writeCredentialsStore(store) {
|
|
|
36358
36363
|
await writeFile(CREDENTIALS_FILE, JSON.stringify(cleaned, null, 2), { mode: 384 });
|
|
36359
36364
|
return true;
|
|
36360
36365
|
}
|
|
36361
|
-
|
|
36362
|
-
const store = await readCredentialsStore();
|
|
36366
|
+
function getStoredCredentialsFromStore(store, environment) {
|
|
36363
36367
|
const namespaced = store.timeback?.[environment];
|
|
36364
36368
|
if (namespaced) {
|
|
36365
|
-
const result =
|
|
36369
|
+
const result = StoredCredentialsSchema.safeParse(namespaced);
|
|
36366
36370
|
if (result.success) {
|
|
36367
36371
|
return result.data;
|
|
36368
36372
|
}
|
|
36369
36373
|
}
|
|
36370
36374
|
const legacy = store[environment];
|
|
36371
36375
|
if (legacy) {
|
|
36372
|
-
const result =
|
|
36376
|
+
const result = StoredCredentialsSchema.safeParse(legacy);
|
|
36373
36377
|
if (result.success) {
|
|
36374
36378
|
return result.data;
|
|
36375
36379
|
}
|
|
36376
36380
|
}
|
|
36377
36381
|
return null;
|
|
36378
36382
|
}
|
|
36383
|
+
async function getStoredCredentials(environment) {
|
|
36384
|
+
const store = await readCredentialsStore();
|
|
36385
|
+
return getStoredCredentialsFromStore(store, environment);
|
|
36386
|
+
}
|
|
36387
|
+
function toCredentials(stored) {
|
|
36388
|
+
if (stored?.clientId && stored.clientSecret) {
|
|
36389
|
+
return {
|
|
36390
|
+
clientId: stored.clientId,
|
|
36391
|
+
clientSecret: stored.clientSecret,
|
|
36392
|
+
email: stored.email
|
|
36393
|
+
};
|
|
36394
|
+
}
|
|
36395
|
+
return null;
|
|
36396
|
+
}
|
|
36397
|
+
async function getSavedCredentials(environment) {
|
|
36398
|
+
const stored = await getStoredCredentials(environment);
|
|
36399
|
+
return toCredentials(stored);
|
|
36400
|
+
}
|
|
36379
36401
|
async function saveCredentials(environment, credentials) {
|
|
36380
36402
|
const store = await readCredentialsStore();
|
|
36381
36403
|
store.timeback = store.timeback ?? {};
|
|
@@ -36383,6 +36405,44 @@ async function saveCredentials(environment, credentials) {
|
|
|
36383
36405
|
delete store[environment];
|
|
36384
36406
|
return writeCredentialsStore(store);
|
|
36385
36407
|
}
|
|
36408
|
+
async function saveCredentialEmail(environment, email3) {
|
|
36409
|
+
const store = await readCredentialsStore();
|
|
36410
|
+
const existing = getStoredCredentialsFromStore(store, environment);
|
|
36411
|
+
if (email3) {
|
|
36412
|
+
store.timeback = store.timeback ?? {};
|
|
36413
|
+
store.timeback[environment] = {
|
|
36414
|
+
...existing ?? {},
|
|
36415
|
+
email: email3
|
|
36416
|
+
};
|
|
36417
|
+
} else if (existing?.clientId && existing.clientSecret) {
|
|
36418
|
+
store.timeback = store.timeback ?? {};
|
|
36419
|
+
store.timeback[environment] = {
|
|
36420
|
+
clientId: existing.clientId,
|
|
36421
|
+
clientSecret: existing.clientSecret
|
|
36422
|
+
};
|
|
36423
|
+
} else {
|
|
36424
|
+
delete store.timeback?.[environment];
|
|
36425
|
+
if (store.timeback && Object.keys(store.timeback).length === 0) {
|
|
36426
|
+
delete store.timeback;
|
|
36427
|
+
}
|
|
36428
|
+
}
|
|
36429
|
+
delete store[environment];
|
|
36430
|
+
return writeCredentialsStore(store);
|
|
36431
|
+
}
|
|
36432
|
+
async function getStoredCredentialEmail(environment) {
|
|
36433
|
+
const stored = await getStoredCredentials(environment);
|
|
36434
|
+
return stored?.email;
|
|
36435
|
+
}
|
|
36436
|
+
async function getEnvironmentsWithStoredEmail() {
|
|
36437
|
+
const store = await readCredentialsStore();
|
|
36438
|
+
const environments = [];
|
|
36439
|
+
for (const env2 of ENVIRONMENTS) {
|
|
36440
|
+
if (getStoredCredentialsFromStore(store, env2)?.email) {
|
|
36441
|
+
environments.push(env2);
|
|
36442
|
+
}
|
|
36443
|
+
}
|
|
36444
|
+
return environments;
|
|
36445
|
+
}
|
|
36386
36446
|
async function clearCredentials(environment) {
|
|
36387
36447
|
const store = await readCredentialsStore();
|
|
36388
36448
|
if (environment) {
|
|
@@ -36403,18 +36463,23 @@ async function getConfiguredEnvironments() {
|
|
|
36403
36463
|
const store = await readCredentialsStore();
|
|
36404
36464
|
const configured = [];
|
|
36405
36465
|
for (const env2 of ENVIRONMENTS) {
|
|
36406
|
-
|
|
36407
|
-
if (namespaced && CredentialsSchema.safeParse(namespaced).success) {
|
|
36408
|
-
configured.push(env2);
|
|
36409
|
-
continue;
|
|
36410
|
-
}
|
|
36411
|
-
const legacy = store[env2];
|
|
36412
|
-
if (legacy && CredentialsSchema.safeParse(legacy).success) {
|
|
36466
|
+
if (toCredentials(getStoredCredentialsFromStore(store, env2))) {
|
|
36413
36467
|
configured.push(env2);
|
|
36414
36468
|
}
|
|
36415
36469
|
}
|
|
36416
36470
|
return configured;
|
|
36417
36471
|
}
|
|
36472
|
+
async function getAvailableEnvironments() {
|
|
36473
|
+
const available = await getConfiguredEnvironments();
|
|
36474
|
+
if (getEnvCredentials()) {
|
|
36475
|
+
for (const env2 of ENVIRONMENTS) {
|
|
36476
|
+
if (!available.includes(env2)) {
|
|
36477
|
+
available.push(env2);
|
|
36478
|
+
}
|
|
36479
|
+
}
|
|
36480
|
+
}
|
|
36481
|
+
return available;
|
|
36482
|
+
}
|
|
36418
36483
|
function getEnvCredentials() {
|
|
36419
36484
|
const clientId = process.env.TIMEBACK_API_CLIENT_ID ?? process.env.TIMEBACK_CLIENT_ID;
|
|
36420
36485
|
const clientSecret = process.env.TIMEBACK_API_CLIENT_SECRET ?? process.env.TIMEBACK_CLIENT_SECRET;
|
|
@@ -36428,10 +36493,12 @@ function getEnvCredentials() {
|
|
|
36428
36493
|
}
|
|
36429
36494
|
async function loadCredentials(environment) {
|
|
36430
36495
|
const envCreds = getEnvCredentials();
|
|
36496
|
+
const storedCreds = await getStoredCredentials(environment);
|
|
36431
36497
|
if (envCreds) {
|
|
36432
|
-
|
|
36498
|
+
const merged = storedCreds?.email ? { ...envCreds, email: storedCreds.email } : envCreds;
|
|
36499
|
+
return { success: true, credentials: merged, source: "env" };
|
|
36433
36500
|
}
|
|
36434
|
-
const savedCreds =
|
|
36501
|
+
const savedCreds = toCredentials(storedCreds);
|
|
36435
36502
|
if (savedCreds) {
|
|
36436
36503
|
return { success: true, credentials: savedCreds, source: "saved" };
|
|
36437
36504
|
}
|
|
@@ -39316,7 +39383,7 @@ You need to configure your Timeback API credentials first.`, "Credentials Setup"
|
|
|
39316
39383
|
}
|
|
39317
39384
|
async function runImportFlow(opts = {}) {
|
|
39318
39385
|
const { format: format2 = true } = opts;
|
|
39319
|
-
let configuredEnvs = await
|
|
39386
|
+
let configuredEnvs = await getAvailableEnvironments();
|
|
39320
39387
|
if (configuredEnvs.length === 0) {
|
|
39321
39388
|
const setupResult = await setupCredentials();
|
|
39322
39389
|
if (setupResult === "cancelled") {
|
|
@@ -39325,7 +39392,7 @@ async function runImportFlow(opts = {}) {
|
|
|
39325
39392
|
if (setupResult === "error") {
|
|
39326
39393
|
return { success: false, error: "Credential validation failed" };
|
|
39327
39394
|
}
|
|
39328
|
-
configuredEnvs = await
|
|
39395
|
+
configuredEnvs = await getAvailableEnvironments();
|
|
39329
39396
|
if (configuredEnvs.length === 0) {
|
|
39330
39397
|
return { success: false, error: "Failed to configure credentials" };
|
|
39331
39398
|
}
|
|
@@ -47245,11 +47312,15 @@ function exitCancelled(isOverwriting, exitOnComplete) {
|
|
|
47245
47312
|
// src/commands/credentials/email.ts
|
|
47246
47313
|
async function updateEmail(options = {}) {
|
|
47247
47314
|
const { exitOnComplete = true, env: envOption } = options;
|
|
47248
|
-
const
|
|
47249
|
-
|
|
47250
|
-
|
|
47251
|
-
|
|
47252
|
-
|
|
47315
|
+
const emailEnvs = await getEnvironmentsWithStoredEmail();
|
|
47316
|
+
const requestedEnv = envOption && ENVIRONMENTS.includes(envOption) ? envOption : undefined;
|
|
47317
|
+
let configuredEnvs;
|
|
47318
|
+
if (requestedEnv) {
|
|
47319
|
+
const hasStoredEmail = await getStoredCredentialEmail(requestedEnv);
|
|
47320
|
+
const hasUsableCredentials = (await loadCredentials(requestedEnv)).success;
|
|
47321
|
+
configuredEnvs = hasStoredEmail || hasUsableCredentials ? [requestedEnv] : [];
|
|
47322
|
+
} else {
|
|
47323
|
+
configuredEnvs = [...new Set([...await getAvailableEnvironments(), ...emailEnvs])];
|
|
47253
47324
|
}
|
|
47254
47325
|
if (configuredEnvs.length === 0) {
|
|
47255
47326
|
M2.warn("No credentials configured");
|
|
@@ -47279,15 +47350,8 @@ async function updateEmail(options = {}) {
|
|
|
47279
47350
|
}
|
|
47280
47351
|
targetEnv = selected;
|
|
47281
47352
|
}
|
|
47282
|
-
const
|
|
47283
|
-
|
|
47284
|
-
M2.error("Credentials not found");
|
|
47285
|
-
outro.error("Credentials not found");
|
|
47286
|
-
if (exitOnComplete)
|
|
47287
|
-
process.exit(0);
|
|
47288
|
-
return false;
|
|
47289
|
-
}
|
|
47290
|
-
const currentEmail = currentCreds.email;
|
|
47353
|
+
const currentCredsResult = await loadCredentials(targetEnv);
|
|
47354
|
+
const currentEmail = currentCredsResult.success ? currentCredsResult.credentials.email : await getStoredCredentialEmail(targetEnv);
|
|
47291
47355
|
const message = currentEmail ? `Email ${dim(`(current: ${currentEmail})`)}` : `Email ${dim("(not set)")}`;
|
|
47292
47356
|
const email3 = await he({
|
|
47293
47357
|
message,
|
|
@@ -47316,6 +47380,14 @@ async function updateEmail(options = {}) {
|
|
|
47316
47380
|
return true;
|
|
47317
47381
|
}
|
|
47318
47382
|
if (normalizedEmail) {
|
|
47383
|
+
if (!currentCredsResult.success) {
|
|
47384
|
+
M2.error("Credentials required to set email");
|
|
47385
|
+
outro.error("Credentials required to set email");
|
|
47386
|
+
if (exitOnComplete)
|
|
47387
|
+
process.exit(0);
|
|
47388
|
+
return false;
|
|
47389
|
+
}
|
|
47390
|
+
const currentCreds = currentCredsResult.credentials;
|
|
47319
47391
|
const s = Y2();
|
|
47320
47392
|
s.start("Validating email...");
|
|
47321
47393
|
const result = await validateEmailWithTimeback(targetEnv, currentCreds.clientId, currentCreds.clientSecret, normalizedEmail);
|
|
@@ -47336,10 +47408,7 @@ async function updateEmail(options = {}) {
|
|
|
47336
47408
|
email: normalizedEmail
|
|
47337
47409
|
});
|
|
47338
47410
|
if (accountCreated) {
|
|
47339
|
-
await
|
|
47340
|
-
...currentCreds,
|
|
47341
|
-
email: normalizedEmail
|
|
47342
|
-
});
|
|
47411
|
+
await saveCredentialEmail(targetEnv, normalizedEmail);
|
|
47343
47412
|
M2.success(`Email saved for ${targetEnv}`);
|
|
47344
47413
|
outro.success();
|
|
47345
47414
|
} else {
|
|
@@ -47349,20 +47418,14 @@ async function updateEmail(options = {}) {
|
|
|
47349
47418
|
process.exit(0);
|
|
47350
47419
|
return accountCreated;
|
|
47351
47420
|
}
|
|
47352
|
-
await
|
|
47353
|
-
...currentCreds,
|
|
47354
|
-
email: normalizedEmail
|
|
47355
|
-
});
|
|
47421
|
+
await saveCredentialEmail(targetEnv, normalizedEmail);
|
|
47356
47422
|
s.stop(green(`Email updated for ${targetEnv}`));
|
|
47357
47423
|
outro.success();
|
|
47358
47424
|
if (exitOnComplete)
|
|
47359
47425
|
process.exit(0);
|
|
47360
47426
|
return true;
|
|
47361
47427
|
}
|
|
47362
|
-
await
|
|
47363
|
-
...currentCreds,
|
|
47364
|
-
email: undefined
|
|
47365
|
-
});
|
|
47428
|
+
await saveCredentialEmail(targetEnv, undefined);
|
|
47366
47429
|
outro.success(`Email cleared for ${targetEnv}`);
|
|
47367
47430
|
if (exitOnComplete)
|
|
47368
47431
|
process.exit(0);
|
|
@@ -47380,13 +47443,19 @@ async function listCredentials(options = {}) {
|
|
|
47380
47443
|
const { exitOnComplete = true } = options;
|
|
47381
47444
|
const timebackLines = [];
|
|
47382
47445
|
const masteryTrackLines = [];
|
|
47446
|
+
const envCreds = getEnvCredentials();
|
|
47383
47447
|
let hasAnyTimeback = false;
|
|
47384
47448
|
let hasAnyMasteryTrack = false;
|
|
47449
|
+
let hasSavedTimeback = false;
|
|
47450
|
+
const hasEnvTimeback = Boolean(envCreds);
|
|
47385
47451
|
for (const env2 of ENVIRONMENTS) {
|
|
47386
47452
|
const creds = await getSavedCredentials(env2);
|
|
47387
|
-
if (creds) {
|
|
47453
|
+
if (creds && hasEnvTimeback) {
|
|
47454
|
+
timebackLines.push(`${dim("●")} ${env2}: ${creds.clientId}`);
|
|
47455
|
+
hasSavedTimeback = true;
|
|
47456
|
+
} else if (creds) {
|
|
47388
47457
|
timebackLines.push(`${green("●")} ${env2}: ${creds.clientId}`);
|
|
47389
|
-
|
|
47458
|
+
hasSavedTimeback = true;
|
|
47390
47459
|
} else {
|
|
47391
47460
|
timebackLines.push(`${dim("○")} ${env2}: not configured`);
|
|
47392
47461
|
}
|
|
@@ -47398,12 +47467,26 @@ async function listCredentials(options = {}) {
|
|
|
47398
47467
|
masteryTrackLines.push(`${dim("○")} ${env2}: not configured`);
|
|
47399
47468
|
}
|
|
47400
47469
|
}
|
|
47470
|
+
hasAnyTimeback = hasSavedTimeback || hasEnvTimeback;
|
|
47471
|
+
if (envCreds) {
|
|
47472
|
+
timebackLines.push(`${green("●")} env: ${envCreds.clientId} ${yellow("[override]")}`);
|
|
47473
|
+
}
|
|
47401
47474
|
Me(timebackLines.join(`
|
|
47402
47475
|
`), "Timeback");
|
|
47403
47476
|
Me(masteryTrackLines.join(`
|
|
47404
47477
|
`), "MasteryTrack");
|
|
47405
47478
|
if (hasAnyTimeback || hasAnyMasteryTrack) {
|
|
47406
|
-
|
|
47479
|
+
const details = [];
|
|
47480
|
+
if (hasSavedTimeback || hasAnyMasteryTrack) {
|
|
47481
|
+
details.push(`Credentials stored in ${blueBright(underline(tildify(getCredentialsPath())))}`);
|
|
47482
|
+
}
|
|
47483
|
+
if (hasEnvTimeback) {
|
|
47484
|
+
details.push("Timeback env credentials are active");
|
|
47485
|
+
}
|
|
47486
|
+
if (details.length > 0) {
|
|
47487
|
+
outro.info(details.join(`
|
|
47488
|
+
`));
|
|
47489
|
+
}
|
|
47407
47490
|
} else {
|
|
47408
47491
|
const shouldAdd = await ye({
|
|
47409
47492
|
message: "No credentials configured. Add now?"
|
|
@@ -47445,7 +47528,7 @@ async function removeCredentials(options = {}) {
|
|
|
47445
47528
|
configuredEnvs.push(env2);
|
|
47446
47529
|
}
|
|
47447
47530
|
if (configuredEnvs.length === 0) {
|
|
47448
|
-
M2.warn(provider === "masterytrack" ? "No MasteryTrack credentials configured" : "No credentials configured");
|
|
47531
|
+
M2.warn(provider === "masterytrack" ? "No saved MasteryTrack credentials configured" : "No saved credentials configured");
|
|
47449
47532
|
if (exitOnComplete)
|
|
47450
47533
|
process.exit(0);
|
|
47451
47534
|
return;
|
|
@@ -48264,7 +48347,7 @@ You need to configure your Timeback API credentials first.`, "Credentials Setup"
|
|
|
48264
48347
|
return "ok";
|
|
48265
48348
|
}
|
|
48266
48349
|
async function ensureConfiguredEnvironments(targetEnv) {
|
|
48267
|
-
let configuredEnvs = await
|
|
48350
|
+
let configuredEnvs = await getAvailableEnvironments();
|
|
48268
48351
|
const needsSetup = targetEnv ? !configuredEnvs.includes(targetEnv) : configuredEnvs.length === 0;
|
|
48269
48352
|
if (needsSetup) {
|
|
48270
48353
|
const setupResult = await setupCredentials2(targetEnv);
|
|
@@ -48274,7 +48357,7 @@ async function ensureConfiguredEnvironments(targetEnv) {
|
|
|
48274
48357
|
if (setupResult === "error") {
|
|
48275
48358
|
return { status: "error" };
|
|
48276
48359
|
}
|
|
48277
|
-
configuredEnvs = await
|
|
48360
|
+
configuredEnvs = await getAvailableEnvironments();
|
|
48278
48361
|
const stillMissing = targetEnv ? !configuredEnvs.includes(targetEnv) : configuredEnvs.length === 0;
|
|
48279
48362
|
if (stillMissing) {
|
|
48280
48363
|
return { status: "error" };
|
|
@@ -52619,8 +52702,8 @@ async function installAndDisplay(options) {
|
|
|
52619
52702
|
|
|
52620
52703
|
// src/commands/init/lib/postInit.ts
|
|
52621
52704
|
async function ensureSyncEmail(targetEnv) {
|
|
52622
|
-
const
|
|
52623
|
-
if (
|
|
52705
|
+
const result = await loadCredentials(targetEnv);
|
|
52706
|
+
if (result.success && !result.credentials.email) {
|
|
52624
52707
|
return await updateEmail({ exitOnComplete: false, env: targetEnv });
|
|
52625
52708
|
}
|
|
52626
52709
|
return true;
|
|
@@ -78345,8 +78428,13 @@ var CredentialsSchema2 = exports_external2.object({
|
|
|
78345
78428
|
clientSecret: NonEmptyString4,
|
|
78346
78429
|
email: exports_external2.email("Valid email is required").optional()
|
|
78347
78430
|
});
|
|
78348
|
-
var
|
|
78349
|
-
|
|
78431
|
+
var StoredCredentialsSchema2 = exports_external2.object({
|
|
78432
|
+
clientId: NonEmptyString4.optional(),
|
|
78433
|
+
clientSecret: NonEmptyString4.optional(),
|
|
78434
|
+
email: exports_external2.email("Valid email is required").optional()
|
|
78435
|
+
}).refine((value) => value.email || value.clientId && value.clientSecret, "Saved credentials must include an email or a client ID/client secret pair").refine((value) => Boolean(value.clientId) === Boolean(value.clientSecret), "Saved credentials must include both client ID and client secret");
|
|
78436
|
+
var ClientIdSchema2 = exports_external2.string().min(1, "Client ID is required").max(128, "Client ID must be at most 128 characters").regex(/^[\w+]+$/, "Client ID must contain only letters, digits, underscores, or +");
|
|
78437
|
+
var ClientSecretSchema2 = exports_external2.string().min(24, "Client secret must be at least 24 characters").max(64, "Client secret must be at most 64 characters").regex(/^[\w+]+$/, "Client secret must contain only letters, digits, underscores, or +");
|
|
78350
78438
|
var MasteryTrackCredentialsSchema2 = exports_external2.object({
|
|
78351
78439
|
apiKey: NonEmptyString4,
|
|
78352
78440
|
email: exports_external2.email("Valid email is required")
|
|
@@ -78667,24 +78755,41 @@ async function writeCredentialsStore2(store) {
|
|
|
78667
78755
|
await writeFile3(CREDENTIALS_FILE2, JSON.stringify(cleaned, null, 2), { mode: 384 });
|
|
78668
78756
|
return true;
|
|
78669
78757
|
}
|
|
78670
|
-
|
|
78671
|
-
const store = await readCredentialsStore2();
|
|
78758
|
+
function getStoredCredentialsFromStore2(store, environment) {
|
|
78672
78759
|
const namespaced = store.timeback?.[environment];
|
|
78673
78760
|
if (namespaced) {
|
|
78674
|
-
const result =
|
|
78761
|
+
const result = StoredCredentialsSchema2.safeParse(namespaced);
|
|
78675
78762
|
if (result.success) {
|
|
78676
78763
|
return result.data;
|
|
78677
78764
|
}
|
|
78678
78765
|
}
|
|
78679
78766
|
const legacy = store[environment];
|
|
78680
78767
|
if (legacy) {
|
|
78681
|
-
const result =
|
|
78768
|
+
const result = StoredCredentialsSchema2.safeParse(legacy);
|
|
78682
78769
|
if (result.success) {
|
|
78683
78770
|
return result.data;
|
|
78684
78771
|
}
|
|
78685
78772
|
}
|
|
78686
78773
|
return null;
|
|
78687
78774
|
}
|
|
78775
|
+
async function getStoredCredentials2(environment) {
|
|
78776
|
+
const store = await readCredentialsStore2();
|
|
78777
|
+
return getStoredCredentialsFromStore2(store, environment);
|
|
78778
|
+
}
|
|
78779
|
+
function toCredentials2(stored) {
|
|
78780
|
+
if (stored?.clientId && stored.clientSecret) {
|
|
78781
|
+
return {
|
|
78782
|
+
clientId: stored.clientId,
|
|
78783
|
+
clientSecret: stored.clientSecret,
|
|
78784
|
+
email: stored.email
|
|
78785
|
+
};
|
|
78786
|
+
}
|
|
78787
|
+
return null;
|
|
78788
|
+
}
|
|
78789
|
+
async function getSavedCredentials2(environment) {
|
|
78790
|
+
const stored = await getStoredCredentials2(environment);
|
|
78791
|
+
return toCredentials2(stored);
|
|
78792
|
+
}
|
|
78688
78793
|
async function saveCredentials2(environment, credentials) {
|
|
78689
78794
|
const store = await readCredentialsStore2();
|
|
78690
78795
|
store.timeback = store.timeback ?? {};
|
|
@@ -78692,6 +78797,44 @@ async function saveCredentials2(environment, credentials) {
|
|
|
78692
78797
|
delete store[environment];
|
|
78693
78798
|
return writeCredentialsStore2(store);
|
|
78694
78799
|
}
|
|
78800
|
+
async function saveCredentialEmail2(environment, email32) {
|
|
78801
|
+
const store = await readCredentialsStore2();
|
|
78802
|
+
const existing = getStoredCredentialsFromStore2(store, environment);
|
|
78803
|
+
if (email32) {
|
|
78804
|
+
store.timeback = store.timeback ?? {};
|
|
78805
|
+
store.timeback[environment] = {
|
|
78806
|
+
...existing ?? {},
|
|
78807
|
+
email: email32
|
|
78808
|
+
};
|
|
78809
|
+
} else if (existing?.clientId && existing.clientSecret) {
|
|
78810
|
+
store.timeback = store.timeback ?? {};
|
|
78811
|
+
store.timeback[environment] = {
|
|
78812
|
+
clientId: existing.clientId,
|
|
78813
|
+
clientSecret: existing.clientSecret
|
|
78814
|
+
};
|
|
78815
|
+
} else {
|
|
78816
|
+
delete store.timeback?.[environment];
|
|
78817
|
+
if (store.timeback && Object.keys(store.timeback).length === 0) {
|
|
78818
|
+
delete store.timeback;
|
|
78819
|
+
}
|
|
78820
|
+
}
|
|
78821
|
+
delete store[environment];
|
|
78822
|
+
return writeCredentialsStore2(store);
|
|
78823
|
+
}
|
|
78824
|
+
async function getStoredCredentialEmail2(environment) {
|
|
78825
|
+
const stored = await getStoredCredentials2(environment);
|
|
78826
|
+
return stored?.email;
|
|
78827
|
+
}
|
|
78828
|
+
async function getEnvironmentsWithStoredEmail2() {
|
|
78829
|
+
const store = await readCredentialsStore2();
|
|
78830
|
+
const environments = [];
|
|
78831
|
+
for (const env22 of ENVIRONMENTS2) {
|
|
78832
|
+
if (getStoredCredentialsFromStore2(store, env22)?.email) {
|
|
78833
|
+
environments.push(env22);
|
|
78834
|
+
}
|
|
78835
|
+
}
|
|
78836
|
+
return environments;
|
|
78837
|
+
}
|
|
78695
78838
|
async function clearCredentials2(environment) {
|
|
78696
78839
|
const store = await readCredentialsStore2();
|
|
78697
78840
|
if (environment) {
|
|
@@ -78712,18 +78855,23 @@ async function getConfiguredEnvironments2() {
|
|
|
78712
78855
|
const store = await readCredentialsStore2();
|
|
78713
78856
|
const configured = [];
|
|
78714
78857
|
for (const env22 of ENVIRONMENTS2) {
|
|
78715
|
-
|
|
78716
|
-
if (namespaced && CredentialsSchema2.safeParse(namespaced).success) {
|
|
78717
|
-
configured.push(env22);
|
|
78718
|
-
continue;
|
|
78719
|
-
}
|
|
78720
|
-
const legacy = store[env22];
|
|
78721
|
-
if (legacy && CredentialsSchema2.safeParse(legacy).success) {
|
|
78858
|
+
if (toCredentials2(getStoredCredentialsFromStore2(store, env22))) {
|
|
78722
78859
|
configured.push(env22);
|
|
78723
78860
|
}
|
|
78724
78861
|
}
|
|
78725
78862
|
return configured;
|
|
78726
78863
|
}
|
|
78864
|
+
async function getAvailableEnvironments2() {
|
|
78865
|
+
const available = await getConfiguredEnvironments2();
|
|
78866
|
+
if (getEnvCredentials2()) {
|
|
78867
|
+
for (const env22 of ENVIRONMENTS2) {
|
|
78868
|
+
if (!available.includes(env22)) {
|
|
78869
|
+
available.push(env22);
|
|
78870
|
+
}
|
|
78871
|
+
}
|
|
78872
|
+
}
|
|
78873
|
+
return available;
|
|
78874
|
+
}
|
|
78727
78875
|
function getEnvCredentials2() {
|
|
78728
78876
|
const clientId = process.env.TIMEBACK_API_CLIENT_ID ?? process.env.TIMEBACK_CLIENT_ID;
|
|
78729
78877
|
const clientSecret = process.env.TIMEBACK_API_CLIENT_SECRET ?? process.env.TIMEBACK_CLIENT_SECRET;
|
|
@@ -78737,10 +78885,12 @@ function getEnvCredentials2() {
|
|
|
78737
78885
|
}
|
|
78738
78886
|
async function loadCredentials2(environment) {
|
|
78739
78887
|
const envCreds = getEnvCredentials2();
|
|
78888
|
+
const storedCreds = await getStoredCredentials2(environment);
|
|
78740
78889
|
if (envCreds) {
|
|
78741
|
-
|
|
78890
|
+
const merged = storedCreds?.email ? { ...envCreds, email: storedCreds.email } : envCreds;
|
|
78891
|
+
return { success: true, credentials: merged, source: "env" };
|
|
78742
78892
|
}
|
|
78743
|
-
const savedCreds =
|
|
78893
|
+
const savedCreds = toCredentials2(storedCreds);
|
|
78744
78894
|
if (savedCreds) {
|
|
78745
78895
|
return { success: true, credentials: savedCreds, source: "saved" };
|
|
78746
78896
|
}
|
|
@@ -78750,7 +78900,7 @@ async function loadCredentials2(environment) {
|
|
|
78750
78900
|
};
|
|
78751
78901
|
}
|
|
78752
78902
|
async function loadAllCredentials2() {
|
|
78753
|
-
const configuredEnvs = await
|
|
78903
|
+
const configuredEnvs = await getAvailableEnvironments2();
|
|
78754
78904
|
const credentials = {};
|
|
78755
78905
|
for (const env22 of configuredEnvs) {
|
|
78756
78906
|
const result = await loadCredentials2(env22);
|
|
@@ -80264,21 +80414,40 @@ async function addCredentials2(options = {}) {
|
|
|
80264
80414
|
async function listCredentials2(options = {}) {
|
|
80265
80415
|
const { exitOnComplete = true, inline = false } = options;
|
|
80266
80416
|
const lines = [];
|
|
80417
|
+
const envCreds = getEnvCredentials2();
|
|
80267
80418
|
let hasAny = false;
|
|
80419
|
+
let hasSaved = false;
|
|
80420
|
+
const hasEnv = Boolean(envCreds);
|
|
80268
80421
|
for (const env22 of ENVIRONMENTS2) {
|
|
80269
80422
|
const creds = await getSavedCredentials2(env22);
|
|
80270
|
-
if (creds) {
|
|
80423
|
+
if (creds && hasEnv) {
|
|
80424
|
+
lines.push(`${dim2("●")} ${env22}: ${creds.clientId}`);
|
|
80425
|
+
hasSaved = true;
|
|
80426
|
+
} else if (creds) {
|
|
80271
80427
|
lines.push(`${green2("●")} ${env22}: ${creds.clientId}`);
|
|
80272
|
-
|
|
80428
|
+
hasSaved = true;
|
|
80273
80429
|
} else {
|
|
80274
|
-
lines.push(`${dim2("○")} ${env22}:
|
|
80430
|
+
lines.push(`${dim2("○")} ${env22}: not configured`);
|
|
80275
80431
|
}
|
|
80276
80432
|
}
|
|
80433
|
+
hasAny = hasSaved || hasEnv;
|
|
80434
|
+
if (envCreds) {
|
|
80435
|
+
lines.push(`${green2("●")} env: ${envCreds.clientId} ${yellow2("[override]")}`);
|
|
80436
|
+
}
|
|
80277
80437
|
Me2(lines.join(`
|
|
80278
80438
|
`), "Configured environments");
|
|
80279
80439
|
if (hasAny) {
|
|
80280
|
-
|
|
80281
|
-
|
|
80440
|
+
const details = [];
|
|
80441
|
+
if (hasSaved) {
|
|
80442
|
+
details.push(`Credentials stored in ${blueBright2(underline2(getCredentialsPath2()))}`);
|
|
80443
|
+
}
|
|
80444
|
+
if (hasEnv) {
|
|
80445
|
+
details.push("Timeback env credentials are active");
|
|
80446
|
+
}
|
|
80447
|
+
if (details.length > 0 && !inline) {
|
|
80448
|
+
outro2.info(details.join(`
|
|
80449
|
+
`));
|
|
80450
|
+
}
|
|
80282
80451
|
} else {
|
|
80283
80452
|
const shouldAdd = await ye2({
|
|
80284
80453
|
message: "No credentials configured. Add now?"
|
|
@@ -80295,12 +80464,12 @@ async function listCredentials2(options = {}) {
|
|
|
80295
80464
|
}
|
|
80296
80465
|
async function updateEmail2(options = {}) {
|
|
80297
80466
|
const { exitOnComplete = true, inline = false } = options;
|
|
80298
|
-
const configuredEnvs = [
|
|
80299
|
-
|
|
80300
|
-
|
|
80301
|
-
|
|
80302
|
-
|
|
80303
|
-
|
|
80467
|
+
const configuredEnvs = [
|
|
80468
|
+
...new Set([
|
|
80469
|
+
...await getAvailableEnvironments2(),
|
|
80470
|
+
...await getEnvironmentsWithStoredEmail2()
|
|
80471
|
+
])
|
|
80472
|
+
];
|
|
80304
80473
|
if (configuredEnvs.length === 0) {
|
|
80305
80474
|
M22.warn("No credentials configured");
|
|
80306
80475
|
if (!inline)
|
|
@@ -80329,16 +80498,8 @@ async function updateEmail2(options = {}) {
|
|
|
80329
80498
|
}
|
|
80330
80499
|
targetEnv = selected;
|
|
80331
80500
|
}
|
|
80332
|
-
const
|
|
80333
|
-
|
|
80334
|
-
M22.error("Credentials not found");
|
|
80335
|
-
if (!inline)
|
|
80336
|
-
outro2.error("Credentials not found");
|
|
80337
|
-
if (exitOnComplete)
|
|
80338
|
-
process.exit(0);
|
|
80339
|
-
return;
|
|
80340
|
-
}
|
|
80341
|
-
const currentEmail = currentCreds.email;
|
|
80501
|
+
const currentCredsResult = await loadCredentials2(targetEnv);
|
|
80502
|
+
const currentEmail = currentCredsResult.success ? currentCredsResult.credentials.email : await getStoredCredentialEmail2(targetEnv);
|
|
80342
80503
|
const message = currentEmail ? `Email ${dim2(`(current: ${currentEmail})`)}` : `Email ${dim2("(not set)")}`;
|
|
80343
80504
|
const email32 = await he2({
|
|
80344
80505
|
message,
|
|
@@ -80361,7 +80522,23 @@ async function updateEmail2(options = {}) {
|
|
|
80361
80522
|
}
|
|
80362
80523
|
const normalized = email32 ? normalizeEmail2(email32) : undefined;
|
|
80363
80524
|
const emailUnchanged = (normalized ?? "") === (currentEmail ?? "");
|
|
80525
|
+
if (emailUnchanged) {
|
|
80526
|
+
if (!inline)
|
|
80527
|
+
outro2.info("Email unchanged");
|
|
80528
|
+
if (exitOnComplete)
|
|
80529
|
+
process.exit(0);
|
|
80530
|
+
return;
|
|
80531
|
+
}
|
|
80364
80532
|
if (normalized) {
|
|
80533
|
+
if (!currentCredsResult.success) {
|
|
80534
|
+
M22.error("Credentials required to set email");
|
|
80535
|
+
if (!inline)
|
|
80536
|
+
outro2.error("Credentials required to set email");
|
|
80537
|
+
if (exitOnComplete)
|
|
80538
|
+
process.exit(0);
|
|
80539
|
+
return;
|
|
80540
|
+
}
|
|
80541
|
+
const currentCreds = currentCredsResult.credentials;
|
|
80365
80542
|
const s = Y22();
|
|
80366
80543
|
s.start("Checking account...");
|
|
80367
80544
|
const result = await validateEmailWithTimeback2(targetEnv, currentCreds.clientId, currentCreds.clientSecret, normalized);
|
|
@@ -80382,11 +80559,8 @@ async function updateEmail2(options = {}) {
|
|
|
80382
80559
|
clientSecret: currentCreds.clientSecret,
|
|
80383
80560
|
email: normalized
|
|
80384
80561
|
});
|
|
80385
|
-
if (accountCreated
|
|
80386
|
-
await
|
|
80387
|
-
...currentCreds,
|
|
80388
|
-
email: normalized
|
|
80389
|
-
});
|
|
80562
|
+
if (accountCreated) {
|
|
80563
|
+
await saveCredentialEmail2(targetEnv, normalized);
|
|
80390
80564
|
M22.success(`Email saved for ${targetEnv}`);
|
|
80391
80565
|
}
|
|
80392
80566
|
if (!inline) {
|
|
@@ -80401,17 +80575,7 @@ async function updateEmail2(options = {}) {
|
|
|
80401
80575
|
return;
|
|
80402
80576
|
}
|
|
80403
80577
|
s.stop(green2("Account verified"));
|
|
80404
|
-
|
|
80405
|
-
if (!inline)
|
|
80406
|
-
outro2.info("Email unchanged");
|
|
80407
|
-
if (exitOnComplete)
|
|
80408
|
-
process.exit(0);
|
|
80409
|
-
return;
|
|
80410
|
-
}
|
|
80411
|
-
await saveCredentials2(targetEnv, {
|
|
80412
|
-
...currentCreds,
|
|
80413
|
-
email: normalized
|
|
80414
|
-
});
|
|
80578
|
+
await saveCredentialEmail2(targetEnv, normalized);
|
|
80415
80579
|
M22.success(`Email updated for ${targetEnv}`);
|
|
80416
80580
|
if (!inline)
|
|
80417
80581
|
outro2.success();
|
|
@@ -80419,10 +80583,7 @@ async function updateEmail2(options = {}) {
|
|
|
80419
80583
|
process.exit(0);
|
|
80420
80584
|
return;
|
|
80421
80585
|
}
|
|
80422
|
-
await
|
|
80423
|
-
...currentCreds,
|
|
80424
|
-
email: undefined
|
|
80425
|
-
});
|
|
80586
|
+
await saveCredentialEmail2(targetEnv, undefined);
|
|
80426
80587
|
if (!inline)
|
|
80427
80588
|
outro2.success(`Email cleared for ${targetEnv}`);
|
|
80428
80589
|
if (exitOnComplete)
|
|
@@ -80437,7 +80598,7 @@ async function removeCredentials2(options = {}) {
|
|
|
80437
80598
|
configuredEnvs.push(env22);
|
|
80438
80599
|
}
|
|
80439
80600
|
if (configuredEnvs.length === 0) {
|
|
80440
|
-
M22.warn("No credentials configured");
|
|
80601
|
+
M22.warn("No saved credentials configured");
|
|
80441
80602
|
if (exitOnComplete)
|
|
80442
80603
|
process.exit(0);
|
|
80443
80604
|
return;
|
|
@@ -80643,7 +80804,7 @@ async function promptNoConfig(credentials, configuredEnvs, opts = {}) {
|
|
|
80643
80804
|
}
|
|
80644
80805
|
async function resolveConfigSource(courseIds, opts, defaultEnvironment) {
|
|
80645
80806
|
const parserOpts = { playcademy: opts.playcademy };
|
|
80646
|
-
let configuredEnvs = await
|
|
80807
|
+
let configuredEnvs = await getAvailableEnvironments2();
|
|
80647
80808
|
let credentials = {};
|
|
80648
80809
|
if (courseIds.length > 0) {
|
|
80649
80810
|
if (configuredEnvs.length > 0) {
|
|
@@ -82973,8 +83134,8 @@ var cors = (options) => {
|
|
|
82973
83134
|
async function handleBootstrap(c, ctx) {
|
|
82974
83135
|
const { bootstrap } = c.get("services");
|
|
82975
83136
|
const env22 = c.get("env");
|
|
82976
|
-
const freshCredentials = await
|
|
82977
|
-
const email32 = freshCredentials
|
|
83137
|
+
const freshCredentials = await loadCredentials2(env22);
|
|
83138
|
+
const email32 = freshCredentials.success ? freshCredentials.credentials.email : undefined;
|
|
82978
83139
|
const courseIds = ctx.userConfig.courseIds[env22];
|
|
82979
83140
|
const result = await bootstrap.getBootstrap({ email: email32, courseIds });
|
|
82980
83141
|
return c.json(result);
|
|
@@ -84003,18 +84164,18 @@ class StatusService {
|
|
|
84003
84164
|
this.ctx = ctx;
|
|
84004
84165
|
}
|
|
84005
84166
|
async getStatus() {
|
|
84006
|
-
const configuredEnvironments = await
|
|
84167
|
+
const configuredEnvironments = await getAvailableEnvironments2();
|
|
84007
84168
|
const [stagingCreds, productionCreds] = await Promise.all([
|
|
84008
|
-
|
|
84009
|
-
|
|
84169
|
+
loadCredentials2("staging"),
|
|
84170
|
+
loadCredentials2("production")
|
|
84010
84171
|
]);
|
|
84011
|
-
const defaultCreds = this.ctx.defaultEnvironment === "staging" ? stagingCreds : this.ctx.defaultEnvironment === "production" ? productionCreds : null;
|
|
84012
|
-
const clientId = defaultCreds?.clientId ?? stagingCreds
|
|
84172
|
+
const defaultCreds = this.ctx.defaultEnvironment === "staging" ? stagingCreds.success ? stagingCreds.credentials : null : this.ctx.defaultEnvironment === "production" ? productionCreds.success ? productionCreds.credentials : null : null;
|
|
84173
|
+
const clientId = defaultCreds?.clientId ?? (stagingCreds.success ? stagingCreds.credentials.clientId : undefined) ?? (productionCreds.success ? productionCreds.credentials.clientId : undefined) ?? this.ctx.credentials.staging?.clientId ?? this.ctx.credentials.production?.clientId;
|
|
84013
84174
|
return {
|
|
84014
84175
|
config: this.ctx.userConfig,
|
|
84015
84176
|
environment: this.ctx.defaultEnvironment,
|
|
84016
84177
|
configuredEnvironments,
|
|
84017
|
-
hasEmail:
|
|
84178
|
+
hasEmail: stagingCreds.success && Boolean(stagingCreds.credentials.email) || productionCreds.success && Boolean(productionCreds.credentials.email),
|
|
84018
84179
|
clientId,
|
|
84019
84180
|
sensors: this.ctx.derivedSensors
|
|
84020
84181
|
};
|
|
@@ -84664,7 +84825,7 @@ async function launchServer(serverConfig, userConfig, credentials, env22, opts,
|
|
|
84664
84825
|
}
|
|
84665
84826
|
async function handleCourseIdsCase(courseIds, opts, serverConfig) {
|
|
84666
84827
|
const env22 = opts.env ?? await promptEnvironmentForCourseIds(courseIds.length);
|
|
84667
|
-
let configuredEnvs = await
|
|
84828
|
+
let configuredEnvs = await getAvailableEnvironments2();
|
|
84668
84829
|
let credentials = configuredEnvs.length > 0 ? await loadAllCredentials2() : {};
|
|
84669
84830
|
const resolvedResult = await resolveConfigSource(courseIds, opts, env22);
|
|
84670
84831
|
if (!resolvedResult) {
|
|
@@ -84716,7 +84877,7 @@ async function handleConfigFileCase(userConfig, configPath, opts, serverConfig)
|
|
|
84716
84877
|
await launchServer(serverConfig, userConfig, credentials, env22, opts, configPath);
|
|
84717
84878
|
}
|
|
84718
84879
|
async function handleImportFlowCase(opts, serverConfig) {
|
|
84719
|
-
let configuredEnvs = await
|
|
84880
|
+
let configuredEnvs = await getAvailableEnvironments2();
|
|
84720
84881
|
let credentials = {};
|
|
84721
84882
|
let selectedEnv;
|
|
84722
84883
|
if (configuredEnvs.length === 0) {
|