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 +97 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +97 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -312,9 +312,11 @@ interface Workspace {
|
|
|
312
312
|
*/
|
|
313
313
|
branch: BranchInfo;
|
|
314
314
|
/**
|
|
315
|
-
* Credential for this workspace
|
|
315
|
+
* Credential for this workspace.
|
|
316
|
+
* Optional for public repositories (read-only operations).
|
|
317
|
+
* Required for write operations (push, PR creation).
|
|
316
318
|
*/
|
|
317
|
-
credential
|
|
319
|
+
credential?: GitCredential;
|
|
318
320
|
/**
|
|
319
321
|
* When the workspace was provisioned
|
|
320
322
|
*/
|
|
@@ -1233,6 +1235,10 @@ declare class WorkspaceService {
|
|
|
1233
1235
|
* Clean up all workspaces for an execution
|
|
1234
1236
|
*/
|
|
1235
1237
|
cleanupForExecution(executionId: string): Promise<void>;
|
|
1238
|
+
/**
|
|
1239
|
+
* Try to clone a public repository without authentication
|
|
1240
|
+
*/
|
|
1241
|
+
private tryUnauthenticatedClone;
|
|
1236
1242
|
private cloneRepo;
|
|
1237
1243
|
private createBranch;
|
|
1238
1244
|
private addWorktreeFromParent;
|
package/dist/index.d.ts
CHANGED
|
@@ -312,9 +312,11 @@ interface Workspace {
|
|
|
312
312
|
*/
|
|
313
313
|
branch: BranchInfo;
|
|
314
314
|
/**
|
|
315
|
-
* Credential for this workspace
|
|
315
|
+
* Credential for this workspace.
|
|
316
|
+
* Optional for public repositories (read-only operations).
|
|
317
|
+
* Required for write operations (push, PR creation).
|
|
316
318
|
*/
|
|
317
|
-
credential
|
|
319
|
+
credential?: GitCredential;
|
|
318
320
|
/**
|
|
319
321
|
* When the workspace was provisioned
|
|
320
322
|
*/
|
|
@@ -1233,6 +1235,10 @@ declare class WorkspaceService {
|
|
|
1233
1235
|
* Clean up all workspaces for an execution
|
|
1234
1236
|
*/
|
|
1235
1237
|
cleanupForExecution(executionId: string): Promise<void>;
|
|
1238
|
+
/**
|
|
1239
|
+
* Try to clone a public repository without authentication
|
|
1240
|
+
*/
|
|
1241
|
+
private tryUnauthenticatedClone;
|
|
1236
1242
|
private cloneRepo;
|
|
1237
1243
|
private createBranch;
|
|
1238
1244
|
private addWorktreeFromParent;
|
package/dist/index.js
CHANGED
|
@@ -299,24 +299,28 @@ var WorkspaceService = class {
|
|
|
299
299
|
credential = parent.credential;
|
|
300
300
|
} else {
|
|
301
301
|
await fs3.mkdir(workspacePath, { recursive: true });
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
302
|
+
if (config.userCredentials) {
|
|
303
|
+
credential = await this.credentialService.getCredentials({
|
|
304
|
+
repo: config.repo,
|
|
305
|
+
access: "write",
|
|
306
|
+
context: {
|
|
307
|
+
executionId: config.execution.id,
|
|
308
|
+
taskId: config.task.id,
|
|
309
|
+
userId: config.user?.id,
|
|
310
|
+
reason: `Workspace for ${config.task.role} in ${config.execution.patternName}`
|
|
311
|
+
},
|
|
312
|
+
userProvided: config.userCredentials
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (credential) {
|
|
316
|
+
await this.emitEvent({
|
|
317
|
+
type: "credential:granted",
|
|
318
|
+
workspaceId,
|
|
319
|
+
credentialId: credential.id,
|
|
306
320
|
executionId: config.execution.id,
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
},
|
|
311
|
-
userProvided: config.userCredentials
|
|
312
|
-
});
|
|
313
|
-
await this.emitEvent({
|
|
314
|
-
type: "credential:granted",
|
|
315
|
-
workspaceId,
|
|
316
|
-
credentialId: credential.id,
|
|
317
|
-
executionId: config.execution.id,
|
|
318
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
319
|
-
});
|
|
321
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
322
|
+
});
|
|
323
|
+
}
|
|
320
324
|
}
|
|
321
325
|
const branchInfo = createBranchInfo(
|
|
322
326
|
{
|
|
@@ -333,6 +337,7 @@ var WorkspaceService = class {
|
|
|
333
337
|
repo: config.repo,
|
|
334
338
|
branch: branchInfo,
|
|
335
339
|
credential,
|
|
340
|
+
// Will be set before any write operations
|
|
336
341
|
provisionedAt: /* @__PURE__ */ new Date(),
|
|
337
342
|
status: "provisioning",
|
|
338
343
|
strategy,
|
|
@@ -348,7 +353,38 @@ var WorkspaceService = class {
|
|
|
348
353
|
try {
|
|
349
354
|
if (strategy === "clone") {
|
|
350
355
|
this.updateProgress(workspace, "cloning", "Cloning repository");
|
|
351
|
-
|
|
356
|
+
if (!credential) {
|
|
357
|
+
const cloneResult = await this.tryUnauthenticatedClone(workspace);
|
|
358
|
+
if (!cloneResult.success) {
|
|
359
|
+
this.log(
|
|
360
|
+
"info",
|
|
361
|
+
{ workspaceId, error: cloneResult.error },
|
|
362
|
+
"Unauthenticated clone failed, requesting credentials"
|
|
363
|
+
);
|
|
364
|
+
credential = await this.credentialService.getCredentials({
|
|
365
|
+
repo: config.repo,
|
|
366
|
+
access: "write",
|
|
367
|
+
context: {
|
|
368
|
+
executionId: config.execution.id,
|
|
369
|
+
taskId: config.task.id,
|
|
370
|
+
userId: config.user?.id,
|
|
371
|
+
reason: `Workspace for ${config.task.role} in ${config.execution.patternName}`
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
workspace.credential = credential;
|
|
375
|
+
this.workspaces.set(workspaceId, workspace);
|
|
376
|
+
await this.emitEvent({
|
|
377
|
+
type: "credential:granted",
|
|
378
|
+
workspaceId,
|
|
379
|
+
credentialId: credential.id,
|
|
380
|
+
executionId: config.execution.id,
|
|
381
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
382
|
+
});
|
|
383
|
+
await this.cloneRepo(workspace, credential.token);
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
await this.cloneRepo(workspace, credential.token);
|
|
387
|
+
}
|
|
352
388
|
this.updateProgress(workspace, "creating_branch", "Creating branch");
|
|
353
389
|
await this.createBranch(workspace);
|
|
354
390
|
} else {
|
|
@@ -569,7 +605,7 @@ var WorkspaceService = class {
|
|
|
569
605
|
});
|
|
570
606
|
}
|
|
571
607
|
}
|
|
572
|
-
if (workspace.strategy === "clone") {
|
|
608
|
+
if (workspace.strategy === "clone" && workspace.credential) {
|
|
573
609
|
await this.credentialService.revokeCredential(workspace.credential.id);
|
|
574
610
|
await this.emitEvent({
|
|
575
611
|
type: "credential:revoked",
|
|
@@ -605,6 +641,36 @@ var WorkspaceService = class {
|
|
|
605
641
|
// ─────────────────────────────────────────────────────────────
|
|
606
642
|
// Private Methods
|
|
607
643
|
// ─────────────────────────────────────────────────────────────
|
|
644
|
+
/**
|
|
645
|
+
* Try to clone a public repository without authentication
|
|
646
|
+
*/
|
|
647
|
+
async tryUnauthenticatedClone(workspace) {
|
|
648
|
+
let cloneUrl = workspace.repo;
|
|
649
|
+
if (cloneUrl.startsWith("git@github.com:")) {
|
|
650
|
+
cloneUrl = cloneUrl.replace("git@github.com:", "https://github.com/");
|
|
651
|
+
}
|
|
652
|
+
if (!cloneUrl.endsWith(".git")) {
|
|
653
|
+
cloneUrl = `${cloneUrl}.git`;
|
|
654
|
+
}
|
|
655
|
+
if (!cloneUrl.startsWith("https://")) {
|
|
656
|
+
cloneUrl = `https://${cloneUrl}`;
|
|
657
|
+
}
|
|
658
|
+
try {
|
|
659
|
+
await this.execInDir(
|
|
660
|
+
workspace.path,
|
|
661
|
+
`git clone --depth 1 --branch ${workspace.branch.baseBranch} ${cloneUrl} .`
|
|
662
|
+
);
|
|
663
|
+
this.log("info", { workspaceId: workspace.id }, "Public repository cloned without authentication");
|
|
664
|
+
return { success: true };
|
|
665
|
+
} catch (error) {
|
|
666
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
667
|
+
const isAuthError = errorMessage.includes("401") || errorMessage.includes("403") || errorMessage.includes("Authentication failed") || errorMessage.includes("could not read Username") || errorMessage.includes("terminal prompts disabled");
|
|
668
|
+
if (isAuthError) {
|
|
669
|
+
return { success: false, error: "Authentication required" };
|
|
670
|
+
}
|
|
671
|
+
throw error;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
608
674
|
async cloneRepo(workspace, token) {
|
|
609
675
|
const cloneUrl = token ? this.buildAuthenticatedUrl(workspace.repo, token) : workspace.repo;
|
|
610
676
|
await this.execInDir(
|
|
@@ -628,11 +694,11 @@ var WorkspaceService = class {
|
|
|
628
694
|
async configureGit(workspace) {
|
|
629
695
|
await this.execInDir(workspace.path, 'git config user.name "Workspace Agent"');
|
|
630
696
|
await this.execInDir(workspace.path, 'git config user.email "agent@workspace.local"');
|
|
631
|
-
if (!workspace.credential.token) {
|
|
697
|
+
if (!workspace.credential || !workspace.credential.token) {
|
|
632
698
|
this.log(
|
|
633
699
|
"debug",
|
|
634
700
|
{ workspaceId: workspace.id },
|
|
635
|
-
"Using SSH authentication, skipping credential helper"
|
|
701
|
+
workspace.credential ? "Using SSH authentication, skipping credential helper" : "No credentials (public repo), skipping credential helper"
|
|
636
702
|
);
|
|
637
703
|
return;
|
|
638
704
|
}
|
|
@@ -658,9 +724,19 @@ var WorkspaceService = class {
|
|
|
658
724
|
);
|
|
659
725
|
}
|
|
660
726
|
async pushBranch(workspace) {
|
|
727
|
+
if (!workspace.credential) {
|
|
728
|
+
throw new Error(
|
|
729
|
+
"Push requires authentication. This workspace was cloned from a public repository without credentials."
|
|
730
|
+
);
|
|
731
|
+
}
|
|
661
732
|
await this.execInDir(workspace.path, `git push -u origin ${workspace.branch.name}`);
|
|
662
733
|
}
|
|
663
734
|
async createPullRequest(workspace, config) {
|
|
735
|
+
if (!workspace.credential) {
|
|
736
|
+
throw new Error(
|
|
737
|
+
"Pull request creation requires authentication. This workspace was cloned from a public repository without credentials."
|
|
738
|
+
);
|
|
739
|
+
}
|
|
664
740
|
const repoInfo = this.parseRepo(workspace.repo);
|
|
665
741
|
if (!repoInfo) {
|
|
666
742
|
throw new Error(`Invalid repository format: ${workspace.repo}`);
|