git-workspace-service 0.3.0 → 0.3.1

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/index.cjs CHANGED
@@ -324,24 +324,28 @@ var WorkspaceService = class {
324
324
  credential = parent.credential;
325
325
  } else {
326
326
  await fs3__namespace.mkdir(workspacePath, { recursive: true });
327
- credential = await this.credentialService.getCredentials({
328
- repo: config.repo,
329
- access: "write",
330
- context: {
327
+ if (config.userCredentials) {
328
+ credential = await this.credentialService.getCredentials({
329
+ repo: config.repo,
330
+ access: "write",
331
+ context: {
332
+ executionId: config.execution.id,
333
+ taskId: config.task.id,
334
+ userId: config.user?.id,
335
+ reason: `Workspace for ${config.task.role} in ${config.execution.patternName}`
336
+ },
337
+ userProvided: config.userCredentials
338
+ });
339
+ }
340
+ if (credential) {
341
+ await this.emitEvent({
342
+ type: "credential:granted",
343
+ workspaceId,
344
+ credentialId: credential.id,
331
345
  executionId: config.execution.id,
332
- taskId: config.task.id,
333
- userId: config.user?.id,
334
- reason: `Workspace for ${config.task.role} in ${config.execution.patternName}`
335
- },
336
- userProvided: config.userCredentials
337
- });
338
- await this.emitEvent({
339
- type: "credential:granted",
340
- workspaceId,
341
- credentialId: credential.id,
342
- executionId: config.execution.id,
343
- timestamp: /* @__PURE__ */ new Date()
344
- });
346
+ timestamp: /* @__PURE__ */ new Date()
347
+ });
348
+ }
345
349
  }
346
350
  const branchInfo = createBranchInfo(
347
351
  {
@@ -358,6 +362,7 @@ var WorkspaceService = class {
358
362
  repo: config.repo,
359
363
  branch: branchInfo,
360
364
  credential,
365
+ // Will be set before any write operations
361
366
  provisionedAt: /* @__PURE__ */ new Date(),
362
367
  status: "provisioning",
363
368
  strategy,
@@ -373,7 +378,38 @@ var WorkspaceService = class {
373
378
  try {
374
379
  if (strategy === "clone") {
375
380
  this.updateProgress(workspace, "cloning", "Cloning repository");
376
- await this.cloneRepo(workspace, credential.token);
381
+ if (!credential) {
382
+ const cloneResult = await this.tryUnauthenticatedClone(workspace);
383
+ if (!cloneResult.success) {
384
+ this.log(
385
+ "info",
386
+ { workspaceId, error: cloneResult.error },
387
+ "Unauthenticated clone failed, requesting credentials"
388
+ );
389
+ credential = await this.credentialService.getCredentials({
390
+ repo: config.repo,
391
+ access: "write",
392
+ context: {
393
+ executionId: config.execution.id,
394
+ taskId: config.task.id,
395
+ userId: config.user?.id,
396
+ reason: `Workspace for ${config.task.role} in ${config.execution.patternName}`
397
+ }
398
+ });
399
+ workspace.credential = credential;
400
+ this.workspaces.set(workspaceId, workspace);
401
+ await this.emitEvent({
402
+ type: "credential:granted",
403
+ workspaceId,
404
+ credentialId: credential.id,
405
+ executionId: config.execution.id,
406
+ timestamp: /* @__PURE__ */ new Date()
407
+ });
408
+ await this.cloneRepo(workspace, credential.token);
409
+ }
410
+ } else {
411
+ await this.cloneRepo(workspace, credential.token);
412
+ }
377
413
  this.updateProgress(workspace, "creating_branch", "Creating branch");
378
414
  await this.createBranch(workspace);
379
415
  } else {
@@ -594,7 +630,7 @@ var WorkspaceService = class {
594
630
  });
595
631
  }
596
632
  }
597
- if (workspace.strategy === "clone") {
633
+ if (workspace.strategy === "clone" && workspace.credential) {
598
634
  await this.credentialService.revokeCredential(workspace.credential.id);
599
635
  await this.emitEvent({
600
636
  type: "credential:revoked",
@@ -630,6 +666,36 @@ var WorkspaceService = class {
630
666
  // ─────────────────────────────────────────────────────────────
631
667
  // Private Methods
632
668
  // ─────────────────────────────────────────────────────────────
669
+ /**
670
+ * Try to clone a public repository without authentication
671
+ */
672
+ async tryUnauthenticatedClone(workspace) {
673
+ let cloneUrl = workspace.repo;
674
+ if (cloneUrl.startsWith("git@github.com:")) {
675
+ cloneUrl = cloneUrl.replace("git@github.com:", "https://github.com/");
676
+ }
677
+ if (!cloneUrl.endsWith(".git")) {
678
+ cloneUrl = `${cloneUrl}.git`;
679
+ }
680
+ if (!cloneUrl.startsWith("https://")) {
681
+ cloneUrl = `https://${cloneUrl}`;
682
+ }
683
+ try {
684
+ await this.execInDir(
685
+ workspace.path,
686
+ `git clone --depth 1 --branch ${workspace.branch.baseBranch} ${cloneUrl} .`
687
+ );
688
+ this.log("info", { workspaceId: workspace.id }, "Public repository cloned without authentication");
689
+ return { success: true };
690
+ } catch (error) {
691
+ const errorMessage = error instanceof Error ? error.message : String(error);
692
+ const isAuthError = errorMessage.includes("401") || errorMessage.includes("403") || errorMessage.includes("Authentication failed") || errorMessage.includes("could not read Username") || errorMessage.includes("terminal prompts disabled");
693
+ if (isAuthError) {
694
+ return { success: false, error: "Authentication required" };
695
+ }
696
+ throw error;
697
+ }
698
+ }
633
699
  async cloneRepo(workspace, token) {
634
700
  const cloneUrl = token ? this.buildAuthenticatedUrl(workspace.repo, token) : workspace.repo;
635
701
  await this.execInDir(
@@ -653,11 +719,11 @@ var WorkspaceService = class {
653
719
  async configureGit(workspace) {
654
720
  await this.execInDir(workspace.path, 'git config user.name "Workspace Agent"');
655
721
  await this.execInDir(workspace.path, 'git config user.email "agent@workspace.local"');
656
- if (!workspace.credential.token) {
722
+ if (!workspace.credential || !workspace.credential.token) {
657
723
  this.log(
658
724
  "debug",
659
725
  { workspaceId: workspace.id },
660
- "Using SSH authentication, skipping credential helper"
726
+ workspace.credential ? "Using SSH authentication, skipping credential helper" : "No credentials (public repo), skipping credential helper"
661
727
  );
662
728
  return;
663
729
  }
@@ -683,9 +749,19 @@ var WorkspaceService = class {
683
749
  );
684
750
  }
685
751
  async pushBranch(workspace) {
752
+ if (!workspace.credential) {
753
+ throw new Error(
754
+ "Push requires authentication. This workspace was cloned from a public repository without credentials."
755
+ );
756
+ }
686
757
  await this.execInDir(workspace.path, `git push -u origin ${workspace.branch.name}`);
687
758
  }
688
759
  async createPullRequest(workspace, config) {
760
+ if (!workspace.credential) {
761
+ throw new Error(
762
+ "Pull request creation requires authentication. This workspace was cloned from a public repository without credentials."
763
+ );
764
+ }
689
765
  const repoInfo = this.parseRepo(workspace.repo);
690
766
  if (!repoInfo) {
691
767
  throw new Error(`Invalid repository format: ${workspace.repo}`);