@stage5/lumine 0.2.58 → 0.2.59

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.
@@ -234,7 +234,7 @@ async function reconcileExistingStateBeforeStart({
234
234
  }
235
235
  const preservedWorkspaces = dutyWorkspacePaths(existingState);
236
236
  if (preservedWorkspaces.length > 0) {
237
- const archivePath = await archiveSponsorState(options);
237
+ const archivePath = await archiveSponsorState(options, existingState);
238
238
  console.error(
239
239
  `lumine: preserved the expired duty record at ${archivePath}.`,
240
240
  );
@@ -408,7 +408,7 @@ async function changeDutyState(options, action) {
408
408
  const preservedWorkspaces = dutyWorkspacePaths(localState);
409
409
  const localArchive =
410
410
  preservedWorkspaces.length > 0
411
- ? await archiveSponsorState(options)
411
+ ? await archiveSponsorState(options, localState)
412
412
  : null;
413
413
  if (!localArchive) await removeSponsorState(options);
414
414
  printJsonOrLines(
@@ -472,7 +472,7 @@ async function changeDutyState(options, action) {
472
472
  if (nextState === "stopped") {
473
473
  preservedWorkspaces = dutyWorkspacePaths(localState);
474
474
  if (preservedWorkspaces.length > 0) {
475
- localArchive = await archiveSponsorState(options);
475
+ localArchive = await archiveSponsorState(options, localState);
476
476
  } else {
477
477
  await removeSponsorState(options);
478
478
  }
@@ -1412,7 +1412,17 @@ async function sponsorLockIsStale(lockPath) {
1412
1412
  }
1413
1413
  }
1414
1414
 
1415
- async function archiveSponsorState(options) {
1415
+ async function archiveSponsorState(options, state) {
1416
+ for (const jobState of Object.values(state.jobs || {})) {
1417
+ await scrubJobCredentials(jobState);
1418
+ }
1419
+ for (const preserved of Array.isArray(state.preservedWorkspaces)
1420
+ ? state.preservedWorkspaces
1421
+ : []) {
1422
+ await scrubPreservedWorkspaceCredential(preserved);
1423
+ }
1424
+ state.duty = { ...state.duty, leaseToken: null };
1425
+ await writeSponsorState(options, state);
1416
1426
  const filePath = sponsorDutyStatePath(options);
1417
1427
  const archivePath = `${filePath}.stopped-${Date.now()}`;
1418
1428
  await fs.rename(filePath, archivePath);
@@ -1422,6 +1432,7 @@ async function archiveSponsorState(options) {
1422
1432
 
1423
1433
  async function removeCompletedJob({ options, state, jobId }) {
1424
1434
  const jobState = requireJobState(state, jobId);
1435
+ await scrubJobCredentials(jobState);
1425
1436
  const nextJobs = { ...(state.jobs || {}) };
1426
1437
  delete nextJobs[String(jobId)];
1427
1438
  state.jobs = nextJobs;
@@ -1435,6 +1446,7 @@ async function removeCompletedJob({ options, state, jobId }) {
1435
1446
 
1436
1447
  async function preserveFailedJobWorkspace({ options, state, jobId, reason }) {
1437
1448
  const jobState = requireJobState(state, jobId);
1449
+ await scrubJobCredentials(jobState);
1438
1450
  const nextJobs = { ...(state.jobs || {}) };
1439
1451
  delete nextJobs[String(jobId)];
1440
1452
  state.jobs = nextJobs;
@@ -1449,6 +1461,7 @@ async function preserveFailedJobWorkspace({ options, state, jobId, reason }) {
1449
1461
  workspaceDir: jobState.workspaceDir,
1450
1462
  reason: reason.slice(0, 1000),
1451
1463
  preservedAt: new Date().toISOString(),
1464
+ credentialsRemovedAt: jobState.credentialsRemovedAt,
1452
1465
  },
1453
1466
  ];
1454
1467
  await writeSponsorState(options, state);
@@ -1473,6 +1486,50 @@ function dutyWorkspacePaths(state) {
1473
1486
  }
1474
1487
 
1475
1488
  async function cleanupJobFiles(jobState) {
1489
+ const tempDir = resolveJobTempDir(jobState);
1490
+ await fs.rm(tempDir, { recursive: true, force: true });
1491
+ }
1492
+
1493
+ async function scrubJobCredentials(jobState) {
1494
+ const tempDir = resolveJobTempDir(jobState);
1495
+ const expectedAuthFile = path.join(tempDir, "job-auth.json");
1496
+ const authFile = path.resolve(
1497
+ String(jobState?.authFile || expectedAuthFile),
1498
+ );
1499
+ if (authFile !== expectedAuthFile) {
1500
+ throw new Error(
1501
+ "Refused to remove an unrecognized Workshop credential file.",
1502
+ );
1503
+ }
1504
+ await fs.unlink(authFile).catch((error) => {
1505
+ if (error.code !== "ENOENT") throw error;
1506
+ });
1507
+ jobState.authFile = null;
1508
+ jobState.workspaceToken = null;
1509
+ if (jobState.attempt) {
1510
+ jobState.attempt = { ...jobState.attempt, token: null };
1511
+ }
1512
+ jobState.credentialsRemovedAt = new Date().toISOString();
1513
+ }
1514
+
1515
+ async function scrubPreservedWorkspaceCredential(preserved) {
1516
+ const workspaceDir = path.resolve(String(preserved?.workspaceDir || ""));
1517
+ if (path.basename(workspaceDir) !== "workspace") return;
1518
+ const tempDir = path.dirname(workspaceDir);
1519
+ try {
1520
+ resolveJobTempDir({ tempDir });
1521
+ } catch {
1522
+ return;
1523
+ }
1524
+ const credentialState = {
1525
+ tempDir,
1526
+ authFile: path.join(tempDir, "job-auth.json"),
1527
+ };
1528
+ await scrubJobCredentials(credentialState);
1529
+ preserved.credentialsRemovedAt = credentialState.credentialsRemovedAt;
1530
+ }
1531
+
1532
+ function resolveJobTempDir(jobState) {
1476
1533
  const tempRoot = path.resolve(os.tmpdir());
1477
1534
  const tempDir = path.resolve(String(jobState?.tempDir || ""));
1478
1535
  if (
@@ -1482,7 +1539,7 @@ async function cleanupJobFiles(jobState) {
1482
1539
  ) {
1483
1540
  throw new Error("Refused to remove an unrecognized Workshop directory.");
1484
1541
  }
1485
- await fs.rm(tempDir, { recursive: true, force: true });
1542
+ return tempDir;
1486
1543
  }
1487
1544
 
1488
1545
  export function detectSponsorAgentSession({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stage5/lumine",
3
- "version": "0.2.58",
3
+ "version": "0.2.59",
4
4
  "description": "Command line tools for launching Lumine builds on Twinkle.",
5
5
  "type": "module",
6
6
  "bin": {