@ricsam/r5d-worker 0.0.12 → 0.0.14

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/cjs/main.cjs CHANGED
@@ -31,6 +31,8 @@ var main_exports = {};
31
31
  __export(main_exports, {
32
32
  ensureVisibleGitCheckout: () => ensureVisibleGitCheckout,
33
33
  isArtifactEnvPath: () => isArtifactEnvPath,
34
+ prepareArtifactEnvForShell: () => prepareArtifactEnvForShell,
35
+ resolveProjectFilePath: () => resolveProjectFilePath,
34
36
  syncSessionArtifacts: () => syncSessionArtifacts
35
37
  });
36
38
  module.exports = __toCommonJS(main_exports);
@@ -410,6 +412,29 @@ function rejectArtifactWritePath(filePath) {
410
412
  throw new Error(`${R5D_ARTIFACTS_DIR_REF} attachments are read-only. Copy them into a project path before editing or creating files.`);
411
413
  }
412
414
  }
415
+ async function prepareArtifactEnvForShell(input) {
416
+ if (!input.sessionId) {
417
+ return {};
418
+ }
419
+ try {
420
+ await syncSessionArtifacts({
421
+ baseUrl: input.baseUrl,
422
+ token: input.token,
423
+ projectId: input.projectId,
424
+ branchName: input.branchName,
425
+ sessionId: input.sessionId,
426
+ artifactRoot: input.artifactRoot
427
+ });
428
+ } catch (error) {
429
+ process.stderr.write(`[r5d-worker] artifact sync skipped for ${input.sessionId}: ${error instanceof Error ? error.message : String(error)}
430
+ `);
431
+ }
432
+ const artifactsDir = sessionArtifactDir(input.artifactRoot, input.sessionId);
433
+ import_node_fs.default.mkdirSync(artifactsDir, { recursive: true });
434
+ return {
435
+ R5D_ARTIFACTS_DIR: artifactsDir
436
+ };
437
+ }
413
438
  async function verifyR5dctlAuth(baseUrl, token) {
414
439
  const statusUrl = new URL("/api/r5dctl/auth/status", baseUrl);
415
440
  let response;
@@ -524,9 +549,12 @@ function getInternalStatus(context) {
524
549
  function hasInternalWorktreeChanges(context) {
525
550
  return getInternalStatus(context).trim().length > 0;
526
551
  }
552
+ function isInsideBranchPath(branchPath, filePath) {
553
+ const relative = import_node_path.default.relative(import_node_path.default.resolve(branchPath), import_node_path.default.resolve(filePath));
554
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path.default.sep}`) && !import_node_path.default.isAbsolute(relative);
555
+ }
527
556
  function assertInsideBranch(branchPath, filePath) {
528
- const relative = import_node_path.default.relative(branchPath, filePath);
529
- if (relative === ".." || relative.startsWith(`..${import_node_path.default.sep}`) || import_node_path.default.isAbsolute(relative)) {
557
+ if (!isInsideBranchPath(branchPath, filePath)) {
530
558
  throw new Error("File path escapes the project branch");
531
559
  }
532
560
  }
@@ -534,17 +562,27 @@ function resolveProjectFilePath(branchPath, virtualPath) {
534
562
  if (typeof virtualPath !== "string" || !virtualPath.startsWith("/") || virtualPath.includes("\0")) {
535
563
  throw new Error(`File path must be an absolute project path starting with /. Got: ${JSON.stringify(virtualPath)}`);
536
564
  }
565
+ const checkoutAbsolutePath = import_node_path.default.resolve(virtualPath);
566
+ if (isInsideBranchPath(branchPath, checkoutAbsolutePath)) {
567
+ const checkoutVirtualPath = toVirtualPath(import_node_path.default.resolve(branchPath), checkoutAbsolutePath);
568
+ if (checkoutVirtualPath !== virtualPath) {
569
+ return resolveProjectFilePath(branchPath, checkoutVirtualPath);
570
+ }
571
+ }
572
+ if (virtualPath.split("/").includes("..")) {
573
+ throw new Error(`Invalid project file path: ${virtualPath}`);
574
+ }
537
575
  const normalized = import_node_path.default.posix.normalize(virtualPath);
538
- if (normalized === "/" || normalized.startsWith("/../")) {
576
+ if (normalized.startsWith("/../")) {
539
577
  throw new Error(`Invalid project file path: ${virtualPath}`);
540
578
  }
541
- const repoRelativePath = normalized.slice(1);
579
+ const repoRelativePath = normalized === "/" ? "" : normalized.slice(1);
542
580
  if (repoRelativePath === ".git" || repoRelativePath.startsWith(".git/")) {
543
581
  throw new Error("Refusing to access .git through worker file tools");
544
582
  }
545
- const absolutePath = import_node_path.default.resolve(branchPath, ...repoRelativePath.split("/"));
583
+ const absolutePath = repoRelativePath ? import_node_path.default.resolve(branchPath, ...repoRelativePath.split("/")) : import_node_path.default.resolve(branchPath);
546
584
  assertInsideBranch(branchPath, absolutePath);
547
- return { absolutePath, virtualPath: `/${repoRelativePath}`, repoRelativePath };
585
+ return { absolutePath, virtualPath: repoRelativePath ? `/${repoRelativePath}` : "/", repoRelativePath };
548
586
  }
549
587
  function branchLockKey(projectId, branchName) {
550
588
  return `${projectId}:${branchName}`;
@@ -1486,18 +1524,14 @@ async function executeCommand(input) {
1486
1524
  branchName: input.message.branchName,
1487
1525
  manifest: input.manifest
1488
1526
  });
1489
- let artifactProcessEnv = {};
1490
- if (input.message.sessionId) {
1491
- await syncSessionArtifacts({
1492
- baseUrl: input.baseUrl,
1493
- token: input.token,
1494
- projectId: input.projectId,
1495
- branchName: input.message.branchName,
1496
- sessionId: input.message.sessionId,
1497
- artifactRoot: input.artifactRoot
1498
- });
1499
- artifactProcessEnv = artifactEnv(input.artifactRoot, input.message.sessionId);
1500
- }
1527
+ const artifactProcessEnv = await prepareArtifactEnvForShell({
1528
+ baseUrl: input.baseUrl,
1529
+ token: input.token,
1530
+ projectId: input.projectId,
1531
+ branchName: input.message.branchName,
1532
+ sessionId: input.message.sessionId,
1533
+ artifactRoot: input.artifactRoot
1534
+ });
1501
1535
  const cwd = resolveCommandCwd(workspace.branchPath, input.message.cwd);
1502
1536
  const subprocess = Bun.spawn(input.message.argv, {
1503
1537
  cwd,
@@ -1571,7 +1605,7 @@ async function executeStreamingCommand(input) {
1571
1605
  branchName: input.message.branchName,
1572
1606
  manifest: input.manifest
1573
1607
  });
1574
- await syncSessionArtifacts({
1608
+ const artifactProcessEnv = await prepareArtifactEnvForShell({
1575
1609
  baseUrl: input.baseUrl,
1576
1610
  token: input.token,
1577
1611
  projectId: input.projectId,
@@ -1579,7 +1613,6 @@ async function executeStreamingCommand(input) {
1579
1613
  sessionId: input.message.sessionId,
1580
1614
  artifactRoot: input.artifactRoot
1581
1615
  });
1582
- const artifactProcessEnv = artifactEnv(input.artifactRoot, input.message.sessionId);
1583
1616
  const cwd = resolveCommandCwd(workspace.branchPath, input.message.cwd);
1584
1617
  const subprocess = Bun.spawn(input.message.argv, {
1585
1618
  cwd,
@@ -2218,5 +2251,7 @@ if (isCliEntrypoint()) {
2218
2251
  0 && (module.exports = {
2219
2252
  ensureVisibleGitCheckout,
2220
2253
  isArtifactEnvPath,
2254
+ prepareArtifactEnvForShell,
2255
+ resolveProjectFilePath,
2221
2256
  syncSessionArtifacts
2222
2257
  });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/main.mjs CHANGED
@@ -374,6 +374,29 @@ function rejectArtifactWritePath(filePath) {
374
374
  throw new Error(`${R5D_ARTIFACTS_DIR_REF} attachments are read-only. Copy them into a project path before editing or creating files.`);
375
375
  }
376
376
  }
377
+ async function prepareArtifactEnvForShell(input) {
378
+ if (!input.sessionId) {
379
+ return {};
380
+ }
381
+ try {
382
+ await syncSessionArtifacts({
383
+ baseUrl: input.baseUrl,
384
+ token: input.token,
385
+ projectId: input.projectId,
386
+ branchName: input.branchName,
387
+ sessionId: input.sessionId,
388
+ artifactRoot: input.artifactRoot
389
+ });
390
+ } catch (error) {
391
+ process.stderr.write(`[r5d-worker] artifact sync skipped for ${input.sessionId}: ${error instanceof Error ? error.message : String(error)}
392
+ `);
393
+ }
394
+ const artifactsDir = sessionArtifactDir(input.artifactRoot, input.sessionId);
395
+ fs.mkdirSync(artifactsDir, { recursive: true });
396
+ return {
397
+ R5D_ARTIFACTS_DIR: artifactsDir
398
+ };
399
+ }
377
400
  async function verifyR5dctlAuth(baseUrl, token) {
378
401
  const statusUrl = new URL("/api/r5dctl/auth/status", baseUrl);
379
402
  let response;
@@ -488,9 +511,12 @@ function getInternalStatus(context) {
488
511
  function hasInternalWorktreeChanges(context) {
489
512
  return getInternalStatus(context).trim().length > 0;
490
513
  }
514
+ function isInsideBranchPath(branchPath, filePath) {
515
+ const relative = path.relative(path.resolve(branchPath), path.resolve(filePath));
516
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
517
+ }
491
518
  function assertInsideBranch(branchPath, filePath) {
492
- const relative = path.relative(branchPath, filePath);
493
- if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
519
+ if (!isInsideBranchPath(branchPath, filePath)) {
494
520
  throw new Error("File path escapes the project branch");
495
521
  }
496
522
  }
@@ -498,17 +524,27 @@ function resolveProjectFilePath(branchPath, virtualPath) {
498
524
  if (typeof virtualPath !== "string" || !virtualPath.startsWith("/") || virtualPath.includes("\0")) {
499
525
  throw new Error(`File path must be an absolute project path starting with /. Got: ${JSON.stringify(virtualPath)}`);
500
526
  }
527
+ const checkoutAbsolutePath = path.resolve(virtualPath);
528
+ if (isInsideBranchPath(branchPath, checkoutAbsolutePath)) {
529
+ const checkoutVirtualPath = toVirtualPath(path.resolve(branchPath), checkoutAbsolutePath);
530
+ if (checkoutVirtualPath !== virtualPath) {
531
+ return resolveProjectFilePath(branchPath, checkoutVirtualPath);
532
+ }
533
+ }
534
+ if (virtualPath.split("/").includes("..")) {
535
+ throw new Error(`Invalid project file path: ${virtualPath}`);
536
+ }
501
537
  const normalized = path.posix.normalize(virtualPath);
502
- if (normalized === "/" || normalized.startsWith("/../")) {
538
+ if (normalized.startsWith("/../")) {
503
539
  throw new Error(`Invalid project file path: ${virtualPath}`);
504
540
  }
505
- const repoRelativePath = normalized.slice(1);
541
+ const repoRelativePath = normalized === "/" ? "" : normalized.slice(1);
506
542
  if (repoRelativePath === ".git" || repoRelativePath.startsWith(".git/")) {
507
543
  throw new Error("Refusing to access .git through worker file tools");
508
544
  }
509
- const absolutePath = path.resolve(branchPath, ...repoRelativePath.split("/"));
545
+ const absolutePath = repoRelativePath ? path.resolve(branchPath, ...repoRelativePath.split("/")) : path.resolve(branchPath);
510
546
  assertInsideBranch(branchPath, absolutePath);
511
- return { absolutePath, virtualPath: `/${repoRelativePath}`, repoRelativePath };
547
+ return { absolutePath, virtualPath: repoRelativePath ? `/${repoRelativePath}` : "/", repoRelativePath };
512
548
  }
513
549
  function branchLockKey(projectId, branchName) {
514
550
  return `${projectId}:${branchName}`;
@@ -1450,18 +1486,14 @@ async function executeCommand(input) {
1450
1486
  branchName: input.message.branchName,
1451
1487
  manifest: input.manifest
1452
1488
  });
1453
- let artifactProcessEnv = {};
1454
- if (input.message.sessionId) {
1455
- await syncSessionArtifacts({
1456
- baseUrl: input.baseUrl,
1457
- token: input.token,
1458
- projectId: input.projectId,
1459
- branchName: input.message.branchName,
1460
- sessionId: input.message.sessionId,
1461
- artifactRoot: input.artifactRoot
1462
- });
1463
- artifactProcessEnv = artifactEnv(input.artifactRoot, input.message.sessionId);
1464
- }
1489
+ const artifactProcessEnv = await prepareArtifactEnvForShell({
1490
+ baseUrl: input.baseUrl,
1491
+ token: input.token,
1492
+ projectId: input.projectId,
1493
+ branchName: input.message.branchName,
1494
+ sessionId: input.message.sessionId,
1495
+ artifactRoot: input.artifactRoot
1496
+ });
1465
1497
  const cwd = resolveCommandCwd(workspace.branchPath, input.message.cwd);
1466
1498
  const subprocess = Bun.spawn(input.message.argv, {
1467
1499
  cwd,
@@ -1535,7 +1567,7 @@ async function executeStreamingCommand(input) {
1535
1567
  branchName: input.message.branchName,
1536
1568
  manifest: input.manifest
1537
1569
  });
1538
- await syncSessionArtifacts({
1570
+ const artifactProcessEnv = await prepareArtifactEnvForShell({
1539
1571
  baseUrl: input.baseUrl,
1540
1572
  token: input.token,
1541
1573
  projectId: input.projectId,
@@ -1543,7 +1575,6 @@ async function executeStreamingCommand(input) {
1543
1575
  sessionId: input.message.sessionId,
1544
1576
  artifactRoot: input.artifactRoot
1545
1577
  });
1546
- const artifactProcessEnv = artifactEnv(input.artifactRoot, input.message.sessionId);
1547
1578
  const cwd = resolveCommandCwd(workspace.branchPath, input.message.cwd);
1548
1579
  const subprocess = Bun.spawn(input.message.argv, {
1549
1580
  cwd,
@@ -2181,5 +2212,7 @@ if (isCliEntrypoint()) {
2181
2212
  export {
2182
2213
  ensureVisibleGitCheckout,
2183
2214
  isArtifactEnvPath,
2215
+ prepareArtifactEnvForShell,
2216
+ resolveProjectFilePath,
2184
2217
  syncSessionArtifacts
2185
2218
  };
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "type": "module"
5
5
  }
@@ -8,10 +8,23 @@ export declare function syncSessionArtifacts(input: {
8
8
  sessionId: string;
9
9
  artifactRoot: string;
10
10
  }): Promise<string>;
11
+ export declare function prepareArtifactEnvForShell(input: {
12
+ baseUrl: string;
13
+ token: string;
14
+ projectId: string;
15
+ branchName: string;
16
+ sessionId?: string;
17
+ artifactRoot: string;
18
+ }): Promise<Record<string, string>>;
11
19
  type GitAuth = {
12
20
  extraHeaderUrl: string;
13
21
  header: string;
14
22
  };
23
+ export declare function resolveProjectFilePath(branchPath: string, virtualPath: string): {
24
+ absolutePath: string;
25
+ virtualPath: string;
26
+ repoRelativePath: string;
27
+ };
15
28
  export declare function ensureVisibleGitCheckout(input: {
16
29
  projectRoot: string;
17
30
  branchName: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/main.cjs",
6
6
  "module": "./dist/mjs/main.mjs",