@snappedly-tools/shipyard 0.7.0-staging.36082887921 → 0.7.0
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/README.md +41 -34
- package/dist/MountConfig-bZoCs4Dd.d.ts +26 -0
- package/dist/SandboxProvider-XJQqEdSf.d.ts +261 -0
- package/dist/{chunk-JI3HDDMS.js → chunk-56TDSWFU.js} +3 -3
- package/dist/{chunk-JI3HDDMS.js.map → chunk-56TDSWFU.js.map} +1 -1
- package/dist/chunk-ACD46ZM4.js +136 -0
- package/dist/chunk-ACD46ZM4.js.map +1 -0
- package/dist/{chunk-L6PX5QTU.js → chunk-FDYOTN55.js} +887 -262
- package/dist/chunk-FDYOTN55.js.map +1 -0
- package/dist/chunk-KMGNFXKN.js +38 -0
- package/dist/chunk-KMGNFXKN.js.map +1 -0
- package/dist/chunk-SOJTAJTF.js +78 -0
- package/dist/chunk-SOJTAJTF.js.map +1 -0
- package/dist/{chunk-44I2BL6E.js → chunk-WAXEMZUV.js} +33 -225
- package/dist/chunk-WAXEMZUV.js.map +1 -0
- package/dist/index.d.ts +97 -117
- package/dist/index.js +344 -322
- package/dist/index.js.map +1 -1
- package/dist/main.js +139 -235
- package/dist/main.js.map +1 -1
- package/dist/sandboxes/docker.d.ts +3 -1
- package/dist/sandboxes/docker.js +4 -2
- package/dist/sandboxes/no-sandbox.d.ts +37 -0
- package/dist/sandboxes/no-sandbox.js +4 -0
- package/dist/sandboxes/no-sandbox.js.map +1 -0
- package/dist/sandboxes/vercel.d.ts +104 -0
- package/dist/sandboxes/vercel.js +166 -0
- package/dist/sandboxes/vercel.js.map +1 -0
- package/dist/templates/blank/main.mts +13 -0
- package/dist/templates/blank/prompt.md +12 -0
- package/dist/templates/blank/template.json +4 -0
- package/dist/templates/parallel-planner/main.mts +9 -67
- package/dist/templates/parallel-planner-with-review/main.mts +12 -70
- package/dist/templates/sequential-reviewer/main.mts +3 -52
- package/dist/templates/simple-loop/main.mts +2 -51
- package/package.json +17 -1
- package/dist/MountConfig-BHnKnA4h.d.ts +0 -145
- package/dist/chunk-44I2BL6E.js.map +0 -1
- package/dist/chunk-L6PX5QTU.js.map +0 -1
- package/dist/templates/parallel-planner/planner-branch.mts +0 -151
- package/dist/templates/parallel-planner-with-review/planner-branch.mts +0 -151
package/dist/main.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { CODEX_MODELS, purgeRunLogs, withFriendlyErrors, DEFAULT_LOG_RETENTION_DAYS, resolveEnv } from './chunk-
|
|
3
|
-
import { buildImage, removeImage,
|
|
4
|
-
import { SANDBOX_REPO_DIR, Display, InitError, ACTIVATION_LABEL, PRODUCT_NAME, CONFIG_DIR, CLI_NAME, ClackDisplay,
|
|
2
|
+
import { CODEX_MODELS, purgeRunLogs, withFriendlyErrors, DEFAULT_LOG_RETENTION_DAYS, resolveEnv } from './chunk-56TDSWFU.js';
|
|
3
|
+
import { buildImage, removeImage, validateExistingRepositoryRunner, RunnerLifecycleError, RUNNER_INSTALL_METADATA, removeRepositoryRunner, RUNNER_CONTROLLER_LOCK, recoverRepositoryRunner, REPOSITORY_RUNNER_OWNER_ENV, removeOwnedRepositoryRunnerContainers, RUNNER_WORK_DIR } from './chunk-WAXEMZUV.js';
|
|
4
|
+
import { SANDBOX_REPO_DIR, Display, InitError, ACTIVATION_LABEL, PRODUCT_NAME, CONFIG_DIR, CLI_NAME, ClackDisplay, RUNNER_DIR, RUNNER_SANDBOX_MASK_DIR, assertProtectedDirectoryIdentities, repositoryRunnerEnvironment, ConfigDirError, defaultImageName, ExecHostError, LOCKS_DIR } from './chunk-FDYOTN55.js';
|
|
5
|
+
import './chunk-KMGNFXKN.js';
|
|
6
|
+
import './chunk-ACD46ZM4.js';
|
|
5
7
|
import { NodeContext, NodeRuntime, NodeFileSystem } from '@effect/platform-node';
|
|
6
|
-
import { Effect, Layer, Option
|
|
8
|
+
import { Effect, Layer, Option } from 'effect';
|
|
7
9
|
import { Options, Command } from '@effect/cli';
|
|
8
|
-
import * as
|
|
10
|
+
import * as clack from '@clack/prompts';
|
|
9
11
|
import { execFile, execSync, spawn, execFileSync } from 'child_process';
|
|
10
12
|
import { existsSync, constants, statSync } from 'fs';
|
|
11
13
|
import { join, resolve, dirname } from 'path';
|
|
@@ -17,12 +19,18 @@ import { createHash } from 'crypto';
|
|
|
17
19
|
import { chmod, lstat, realpath, rm, mkdir, writeFile, readFile, access } from 'fs/promises';
|
|
18
20
|
import 'module';
|
|
19
21
|
|
|
20
|
-
var
|
|
22
|
+
var pathExists = (path) => Effect.gen(function* () {
|
|
21
23
|
const fs = yield* FileSystem.FileSystem;
|
|
22
|
-
return yield* fs.exists(
|
|
24
|
+
return yield* fs.exists(path).pipe(Effect.catchAll(() => Effect.succeed(false)));
|
|
25
|
+
});
|
|
26
|
+
var getConfigDirState = (repoDir) => Effect.gen(function* () {
|
|
27
|
+
const canonicalExists = yield* pathExists(join(repoDir, CONFIG_DIR));
|
|
28
|
+
if (canonicalExists) return "canonical";
|
|
29
|
+
return "missing";
|
|
23
30
|
});
|
|
24
31
|
var requireCanonicalConfigDir = (repoDir) => Effect.gen(function* () {
|
|
25
|
-
|
|
32
|
+
const state = yield* getConfigDirState(repoDir);
|
|
33
|
+
if (state === "missing") {
|
|
26
34
|
return yield* Effect.fail(
|
|
27
35
|
new ConfigDirError({
|
|
28
36
|
message: `No ${CONFIG_DIR}/ found. Run \`${CLI_NAME} init\` first.`
|
|
@@ -32,7 +40,8 @@ var requireCanonicalConfigDir = (repoDir) => Effect.gen(function* () {
|
|
|
32
40
|
return join(repoDir, CONFIG_DIR);
|
|
33
41
|
});
|
|
34
42
|
var assertConfigDirAvailable = (repoDir) => Effect.gen(function* () {
|
|
35
|
-
|
|
43
|
+
const state = yield* getConfigDirState(repoDir);
|
|
44
|
+
if (state === "canonical") {
|
|
36
45
|
return yield* Effect.fail(
|
|
37
46
|
new ConfigDirError({
|
|
38
47
|
message: `${CONFIG_DIR}/ directory already exists. ${CLI_NAME} did not modify it. Use the existing configuration or remove it deliberately after preserving any active worktrees before re-initializing.`
|
|
@@ -62,16 +71,11 @@ var CODEX_CHATGPT_AUTH_OPTIONS = `{
|
|
|
62
71
|
},
|
|
63
72
|
],
|
|
64
73
|
}`;
|
|
65
|
-
var roleModelEnvExample = (agentName) => agentName === "codex" ? `# Optional model choices for Shipyard workflows.
|
|
66
|
-
# Choose models available to your Codex CLI account: https://learn.chatgpt.com/docs/models
|
|
67
|
-
# The provider must support each value. Aliases can change their target over time.
|
|
68
|
-
# SHIPYARD_ROUTINE_MODEL=gpt-6-luna
|
|
69
|
-
# SHIPYARD_STRONG_MODEL=gpt-6-sol` : `# Optional model choices for Shipyard workflows.
|
|
70
|
-
# Choose aliases or model IDs available to your Claude Code provider: https://code.claude.com/docs/en/model-config
|
|
71
|
-
# The provider must support each value. Aliases can change their target over time.
|
|
72
|
-
# SHIPYARD_ROUTINE_MODEL=sonnet
|
|
73
|
-
# SHIPYARD_STRONG_MODEL=opus`;
|
|
74
74
|
var TEMPLATES = [
|
|
75
|
+
{
|
|
76
|
+
name: "blank",
|
|
77
|
+
description: "Bare scaffold \u2014 write your own prompt and orchestration"
|
|
78
|
+
},
|
|
75
79
|
{
|
|
76
80
|
name: "simple-loop",
|
|
77
81
|
description: "Implements standalone issues one by one and opens review PRs"
|
|
@@ -171,7 +175,7 @@ RUN apt-get update && apt-get install -y \\
|
|
|
171
175
|
|
|
172
176
|
# Build-args for UID/GID alignment: shipyard docker build-image
|
|
173
177
|
# defaults these to the host user's UID/GID so image-built files
|
|
174
|
-
# and
|
|
178
|
+
# and bind-mounted files share an owner without runtime chown.
|
|
175
179
|
ARG AGENT_UID=1000
|
|
176
180
|
ARG AGENT_GID=1000
|
|
177
181
|
|
|
@@ -187,8 +191,8 @@ ENV PATH="/home/agent/.local/bin:$PATH"
|
|
|
187
191
|
|
|
188
192
|
WORKDIR /home/agent
|
|
189
193
|
|
|
190
|
-
#
|
|
191
|
-
# and
|
|
194
|
+
# In worktree sandbox mode, Shipyard bind-mounts the git worktree at ${SANDBOX_REPO_DIR}
|
|
195
|
+
# and overrides the working directory to ${SANDBOX_REPO_DIR} at container start.
|
|
192
196
|
# Structure your Dockerfile so that ${SANDBOX_REPO_DIR} can serve as the project root.
|
|
193
197
|
ENTRYPOINT ["sleep", "infinity"]
|
|
194
198
|
`;
|
|
@@ -205,7 +209,7 @@ RUN apt-get update && apt-get install -y \\
|
|
|
205
209
|
|
|
206
210
|
# Build-args for UID/GID alignment: shipyard docker build-image
|
|
207
211
|
# defaults these to the host user's UID/GID so image-built files
|
|
208
|
-
# and
|
|
212
|
+
# and bind-mounted files share an owner without runtime chown.
|
|
209
213
|
ARG AGENT_UID=1000
|
|
210
214
|
ARG AGENT_GID=1000
|
|
211
215
|
|
|
@@ -219,8 +223,8 @@ USER \${AGENT_UID}:\${AGENT_GID}
|
|
|
219
223
|
|
|
220
224
|
WORKDIR /home/agent
|
|
221
225
|
|
|
222
|
-
#
|
|
223
|
-
# and
|
|
226
|
+
# In worktree sandbox mode, Shipyard bind-mounts the git worktree at ${SANDBOX_REPO_DIR}
|
|
227
|
+
# and overrides the working directory to ${SANDBOX_REPO_DIR} at container start.
|
|
224
228
|
# Structure your Dockerfile so that ${SANDBOX_REPO_DIR} can serve as the project root.
|
|
225
229
|
ENTRYPOINT ["sleep", "infinity"]
|
|
226
230
|
`;
|
|
@@ -335,26 +339,34 @@ var copyTemplateFiles = (templateDir, destDir, mainFilename) => Effect.gen(funct
|
|
|
335
339
|
{ concurrency: "unbounded" }
|
|
336
340
|
);
|
|
337
341
|
});
|
|
338
|
-
var rewriteMainTs = (configDir, agent, model,
|
|
342
|
+
var rewriteMainTs = (configDir, agent, model, sandboxProvider, mainFilename, codexAuth) => Effect.gen(function* () {
|
|
339
343
|
const fs = yield* FileSystem.FileSystem;
|
|
340
344
|
const mainTsPath = join(configDir, mainFilename);
|
|
341
345
|
const exists = yield* fs.exists(mainTsPath).pipe(Effect.mapError((e) => new Error(e.message)));
|
|
342
346
|
if (!exists) return;
|
|
343
347
|
let content = yield* fs.readFileString(mainTsPath).pipe(Effect.mapError((e) => new Error(e.message)));
|
|
348
|
+
if (mainFilename === "main.ts") {
|
|
349
|
+
content = content.replace(/main\.mts/g, "main.ts");
|
|
350
|
+
}
|
|
344
351
|
content = content.replace(
|
|
345
352
|
new RegExp(`\\b${TEMPLATE_AGENT_FACTORY}\\b`, "g"),
|
|
346
353
|
agent.factoryImport
|
|
347
354
|
);
|
|
355
|
+
const factoryCallRe = new RegExp(
|
|
356
|
+
`${agent.factoryImport}\\(([^)\\n]*)\\)`,
|
|
357
|
+
"g"
|
|
358
|
+
);
|
|
348
359
|
content = content.replace(
|
|
349
|
-
|
|
350
|
-
|
|
360
|
+
factoryCallRe,
|
|
361
|
+
(match, modelExpression) => {
|
|
362
|
+
const keepsConfiguredModel = agent.name === "codex" && model === CODEX_MODELS.routine.model && /^(?:[A-Za-z_$][\w$]*\.)*CODEX_MODELS\.[A-Za-z_$][\w$]*$/.test(
|
|
363
|
+
modelExpression.trim()
|
|
364
|
+
);
|
|
365
|
+
return keepsConfiguredModel ? match : `${agent.factoryImport}("${model}")`;
|
|
366
|
+
}
|
|
351
367
|
);
|
|
352
|
-
if (agent.name !== "codex" ||
|
|
353
|
-
|
|
354
|
-
content = content.replace(
|
|
355
|
-
/shipyard\.CODEX_MODELS\.(?:routine|strong)/g,
|
|
356
|
-
modelLiteral
|
|
357
|
-
);
|
|
368
|
+
if (agent.name !== "codex" || model !== CODEX_MODELS.routine.model) {
|
|
369
|
+
content = content.replace(/\bCODEX_MODELS,\s*/g, "");
|
|
358
370
|
}
|
|
359
371
|
if (agent.name === "codex" && codexAuth === "chatgpt") {
|
|
360
372
|
content = content.replace(
|
|
@@ -422,20 +434,13 @@ var scaffold = (repoDir, options) => Effect.gen(function* () {
|
|
|
422
434
|
const {
|
|
423
435
|
agent,
|
|
424
436
|
model,
|
|
425
|
-
|
|
426
|
-
templateName = "simple-loop",
|
|
437
|
+
templateName = "blank",
|
|
427
438
|
issueTracker = ISSUE_TRACKER_REGISTRY[0],
|
|
428
439
|
// default: github-issues
|
|
429
440
|
sandboxProvider = SANDBOX_PROVIDER_REGISTRY[0],
|
|
430
441
|
// default: docker
|
|
431
442
|
codexAuth = "api-key"
|
|
432
443
|
} = options;
|
|
433
|
-
const totalProgressSteps = 5;
|
|
434
|
-
const reportProgress2 = (current, message) => options.onProgress?.({
|
|
435
|
-
current,
|
|
436
|
-
total: totalProgressSteps,
|
|
437
|
-
message
|
|
438
|
-
});
|
|
439
444
|
if (codexAuth === "chatgpt" && agent.name !== "codex") {
|
|
440
445
|
return yield* Effect.fail(
|
|
441
446
|
new Error(
|
|
@@ -447,14 +452,11 @@ var scaffold = (repoDir, options) => Effect.gen(function* () {
|
|
|
447
452
|
yield* assertConfigDirAvailable(repoDir);
|
|
448
453
|
const configDir = join(repoDir, CONFIG_DIR);
|
|
449
454
|
const mainFilename = yield* detectMainFilename(repoDir);
|
|
450
|
-
reportProgress2(1, "Validated repository and configuration");
|
|
451
455
|
yield* fs.makeDirectory(configDir, { recursive: false }).pipe(Effect.mapError((e) => new Error(e.message)));
|
|
452
456
|
const templateDir = yield* getTemplateDir(templateName);
|
|
453
|
-
reportProgress2(2, `Prepared ${CONFIG_DIR}/ directory`);
|
|
454
457
|
const envExampleParts = [
|
|
455
458
|
agent.name === "codex" && codexAuth === "chatgpt" ? CODEX_CHATGPT_ENV_EXAMPLE : agent.envExample
|
|
456
459
|
];
|
|
457
|
-
envExampleParts.push(roleModelEnvExample(agent.name));
|
|
458
460
|
if (issueTracker.envExample) {
|
|
459
461
|
envExampleParts.push(issueTracker.envExample);
|
|
460
462
|
}
|
|
@@ -473,19 +475,15 @@ var scaffold = (repoDir, options) => Effect.gen(function* () {
|
|
|
473
475
|
],
|
|
474
476
|
{ concurrency: "unbounded" }
|
|
475
477
|
);
|
|
476
|
-
reportProgress2(3, "Wrote configuration and template files");
|
|
477
478
|
yield* rewriteMainTs(
|
|
478
479
|
configDir,
|
|
479
480
|
agent,
|
|
480
481
|
model,
|
|
481
|
-
modelExplicit,
|
|
482
482
|
sandboxProvider,
|
|
483
483
|
mainFilename,
|
|
484
484
|
codexAuth
|
|
485
485
|
);
|
|
486
|
-
reportProgress2(4, "Configured selected agent and sandbox");
|
|
487
486
|
yield* substituteTemplateArgs(configDir, issueTracker);
|
|
488
|
-
reportProgress2(5, "Applied issue tracker settings");
|
|
489
487
|
return { mainFilename };
|
|
490
488
|
});
|
|
491
489
|
var resolveCodexAuthMode = async (options) => {
|
|
@@ -564,11 +562,6 @@ var ensureCodexChatGptAuth = (options) => {
|
|
|
564
562
|
});
|
|
565
563
|
}
|
|
566
564
|
};
|
|
567
|
-
var runEffectPromise = async (effect) => {
|
|
568
|
-
const exit = await Effect.runPromiseExit(effect);
|
|
569
|
-
if (Exit.isFailure(exit)) throw Cause.squash(exit.cause);
|
|
570
|
-
return exit.value;
|
|
571
|
-
};
|
|
572
565
|
var REPOSITORY_RUNNER_WORKFLOW_PATH = ".github/workflows/shipyard-wake.yml";
|
|
573
566
|
var REPOSITORY_RUNNER_WAKE_EXECUTABLE = "shipyard-wake";
|
|
574
567
|
var REPOSITORY_RUNNER_WORKFLOW = `name: Shipyard wake-up
|
|
@@ -763,22 +756,6 @@ var RELEASES_API = "https://api.github.com/repos/actions/runner/releases/latest"
|
|
|
763
756
|
var RunnerInstallError = class extends Error {
|
|
764
757
|
name = "RunnerInstallError";
|
|
765
758
|
};
|
|
766
|
-
var RunnerInstallConflictError = class extends RunnerInstallError {
|
|
767
|
-
constructor(repository, runners) {
|
|
768
|
-
const names = runners.map(({ name }) => name).join(", ");
|
|
769
|
-
super(
|
|
770
|
-
`A repository runner labeled \`${ACTIVATION_LABEL}\` is already registered for ${repository}: ${names}. Remove it before installing another.`
|
|
771
|
-
);
|
|
772
|
-
this.repository = repository;
|
|
773
|
-
this.runners = runners;
|
|
774
|
-
const details = runners.map(
|
|
775
|
-
({ id, name, status, busy }) => `${name}${id === void 0 ? "" : ` (#${id})`} (${status}${busy ? ", busy" : ""})`
|
|
776
|
-
).join(", ");
|
|
777
|
-
this.confirmationMessage = `GitHub has ${runners.length} repository runner${runners.length === 1 ? "" : "s"} labeled \`${ACTIVATION_LABEL}\` for ${repository}: ${details}. Delete ${runners.length === 1 ? "this registration" : "these registrations"} and install a replacement?`;
|
|
778
|
-
}
|
|
779
|
-
name = "RunnerInstallConflictError";
|
|
780
|
-
confirmationMessage;
|
|
781
|
-
};
|
|
782
759
|
var defaultAdapters = {
|
|
783
760
|
platform: () => process.platform,
|
|
784
761
|
arch: () => process.arch,
|
|
@@ -902,7 +879,9 @@ var selectArm64Asset = (release) => {
|
|
|
902
879
|
var appendRunnerIgnores = async (gitignorePath, adapters) => {
|
|
903
880
|
const current = await adapters.exists(gitignorePath) ? await adapters.readText(gitignorePath) : "";
|
|
904
881
|
const lines = new Set(current.split(/\r?\n/));
|
|
905
|
-
const additions = [`${RUNNER_DIR}/`].filter(
|
|
882
|
+
const additions = [`${RUNNER_DIR}/`, `${RUNNER_SANDBOX_MASK_DIR}/`].filter(
|
|
883
|
+
(line) => !lines.has(line)
|
|
884
|
+
);
|
|
906
885
|
if (additions.length === 0) return;
|
|
907
886
|
const prefix = current.length === 0 || current.endsWith("\n") ? current : `${current}
|
|
908
887
|
`;
|
|
@@ -913,88 +892,6 @@ var commandFailure = (purpose, error) => new RunnerInstallError(
|
|
|
913
892
|
`${purpose} failed: ${error instanceof Error ? error.message : String(error)}`
|
|
914
893
|
);
|
|
915
894
|
var reportProgress = (options, current, total, message) => options.onProgress?.({ current, total, message });
|
|
916
|
-
var parseExistingRunnerRegistrations = (stdout) => {
|
|
917
|
-
const lines = stdout.trim().split(/\r?\n/).filter(Boolean);
|
|
918
|
-
return lines.map((line) => {
|
|
919
|
-
let value;
|
|
920
|
-
try {
|
|
921
|
-
value = JSON.parse(line);
|
|
922
|
-
} catch {
|
|
923
|
-
return { id: void 0, name: line, status: "unknown", busy: false };
|
|
924
|
-
}
|
|
925
|
-
if (typeof value !== "object" || value === null) {
|
|
926
|
-
throw new RunnerInstallError(
|
|
927
|
-
"GitHub returned invalid repository runner information."
|
|
928
|
-
);
|
|
929
|
-
}
|
|
930
|
-
const registration = value;
|
|
931
|
-
if (typeof registration.id !== "number" || !Number.isInteger(registration.id) || registration.id <= 0 || typeof registration.name !== "string" || registration.name.length === 0 || typeof registration.status !== "string" || typeof registration.busy !== "boolean") {
|
|
932
|
-
throw new RunnerInstallError(
|
|
933
|
-
"GitHub returned invalid repository runner information."
|
|
934
|
-
);
|
|
935
|
-
}
|
|
936
|
-
return {
|
|
937
|
-
id: registration.id,
|
|
938
|
-
name: registration.name,
|
|
939
|
-
status: registration.status,
|
|
940
|
-
busy: registration.busy
|
|
941
|
-
};
|
|
942
|
-
});
|
|
943
|
-
};
|
|
944
|
-
var removeExistingRepositoryRunnerRegistrations = async (options, adapters = defaultAdapters) => {
|
|
945
|
-
if (options.conflict.runners.some(({ id }) => id === void 0)) {
|
|
946
|
-
throw new RunnerInstallError(
|
|
947
|
-
"GitHub did not provide runner IDs, so Shipyard cannot safely remove the existing registrations. Remove them in GitHub Settings > Actions > Runners, then retry."
|
|
948
|
-
);
|
|
949
|
-
}
|
|
950
|
-
const environment = adapters.environment();
|
|
951
|
-
const removed = [];
|
|
952
|
-
for (const runner of options.conflict.runners) {
|
|
953
|
-
try {
|
|
954
|
-
await adapters.run(
|
|
955
|
-
"gh",
|
|
956
|
-
[
|
|
957
|
-
"api",
|
|
958
|
-
"--method",
|
|
959
|
-
"DELETE",
|
|
960
|
-
`repos/${options.conflict.repository}/actions/runners/${runner.id}`
|
|
961
|
-
],
|
|
962
|
-
{ cwd: options.repoDir, env: environment }
|
|
963
|
-
);
|
|
964
|
-
removed.push(runner.name);
|
|
965
|
-
} catch (error) {
|
|
966
|
-
const previous = removed.length === 0 ? "" : ` Earlier registrations were already removed: ${removed.join(", ")}.`;
|
|
967
|
-
throw new RunnerInstallError(
|
|
968
|
-
`${commandFailure(
|
|
969
|
-
`Removing GitHub runner ${runner.name} (#${runner.id}) from ${options.conflict.repository}`,
|
|
970
|
-
error
|
|
971
|
-
).message}${previous}`
|
|
972
|
-
);
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
};
|
|
976
|
-
var installRepositoryRunnerWithReplacement = async (options, adapters = defaultAdapters) => {
|
|
977
|
-
try {
|
|
978
|
-
return await options.install();
|
|
979
|
-
} catch (error) {
|
|
980
|
-
if (!(error instanceof RunnerInstallConflictError) || !options.interactive) {
|
|
981
|
-
throw error;
|
|
982
|
-
}
|
|
983
|
-
if (!await options.confirmReplacement(error)) {
|
|
984
|
-
throw new RunnerInstallError(
|
|
985
|
-
`${error.message} Existing runner registrations were left unchanged; installation cancelled.`
|
|
986
|
-
);
|
|
987
|
-
}
|
|
988
|
-
await removeExistingRepositoryRunnerRegistrations(
|
|
989
|
-
{
|
|
990
|
-
repoDir: options.repoDir,
|
|
991
|
-
conflict: error
|
|
992
|
-
},
|
|
993
|
-
adapters
|
|
994
|
-
);
|
|
995
|
-
return options.install();
|
|
996
|
-
}
|
|
997
|
-
};
|
|
998
895
|
var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
999
896
|
if (adapters.platform() !== "darwin" || adapters.arch() !== "arm64") {
|
|
1000
897
|
throw new RunnerInstallError(
|
|
@@ -1003,6 +900,7 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
1003
900
|
}
|
|
1004
901
|
const configDir = join(options.repoDir, CONFIG_DIR);
|
|
1005
902
|
const runnerDir = join(configDir, RUNNER_DIR);
|
|
903
|
+
const maskDir = join(configDir, RUNNER_SANDBOX_MASK_DIR);
|
|
1006
904
|
if (!await adapters.exists(configDir)) {
|
|
1007
905
|
throw new RunnerInstallError(
|
|
1008
906
|
`No ${CONFIG_DIR}/ found. Run \`shipyard init\` in this repository first.`
|
|
@@ -1058,6 +956,7 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
1058
956
|
{
|
|
1059
957
|
repoDir: options.repoDir,
|
|
1060
958
|
runnerDir,
|
|
959
|
+
maskDir,
|
|
1061
960
|
repository,
|
|
1062
961
|
runnerName
|
|
1063
962
|
},
|
|
@@ -1070,8 +969,10 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
1070
969
|
}
|
|
1071
970
|
await appendRunnerIgnores(join(configDir, ".gitignore"), adapters);
|
|
1072
971
|
await adapters.chmod(runnerDir, 448);
|
|
972
|
+
await adapters.chmod(maskDir, 448);
|
|
1073
973
|
await assertProtectedDirectoryIdentities(
|
|
1074
974
|
runnerDir,
|
|
975
|
+
maskDir,
|
|
1075
976
|
adapters.inspectDirectory
|
|
1076
977
|
).catch((error) => {
|
|
1077
978
|
throw commandFailure("Validating protected runner directories", error);
|
|
@@ -1102,7 +1003,7 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
1102
1003
|
"--paginate",
|
|
1103
1004
|
`repos/${repository}/actions/runners`,
|
|
1104
1005
|
"--jq",
|
|
1105
|
-
`.runners[] | select(any(.labels[]; .name == "${ACTIVATION_LABEL}")) |
|
|
1006
|
+
`.runners[] | select(any(.labels[]; .name == "${ACTIVATION_LABEL}")) | .name`
|
|
1106
1007
|
],
|
|
1107
1008
|
{ cwd: options.repoDir, env: hostEnv }
|
|
1108
1009
|
).catch((error) => {
|
|
@@ -1111,11 +1012,10 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
1111
1012
|
error
|
|
1112
1013
|
);
|
|
1113
1014
|
});
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
throw new RunnerInstallConflictError(repository, existingRegistrations);
|
|
1015
|
+
if (existing.stdout.trim().length > 0) {
|
|
1016
|
+
throw new RunnerInstallError(
|
|
1017
|
+
`A repository runner labeled \`${ACTIVATION_LABEL}\` is already registered for ${repository}. Remove it before installing another.`
|
|
1018
|
+
);
|
|
1119
1019
|
}
|
|
1120
1020
|
reportProgress(
|
|
1121
1021
|
options,
|
|
@@ -1188,13 +1088,18 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
1188
1088
|
await adapters.makeDirectory(runnerDir).catch((error) => {
|
|
1189
1089
|
throw commandFailure("Creating the protected runner directory", error);
|
|
1190
1090
|
});
|
|
1091
|
+
if (!await adapters.exists(maskDir)) {
|
|
1092
|
+
await adapters.makeDirectory(maskDir);
|
|
1093
|
+
}
|
|
1191
1094
|
await assertProtectedDirectoryIdentities(
|
|
1192
1095
|
runnerDir,
|
|
1096
|
+
maskDir,
|
|
1193
1097
|
adapters.inspectDirectory
|
|
1194
1098
|
).catch((error) => {
|
|
1195
1099
|
throw commandFailure("Validating protected runner directories", error);
|
|
1196
1100
|
});
|
|
1197
1101
|
await adapters.chmod(runnerDir, 448);
|
|
1102
|
+
await adapters.chmod(maskDir, 448);
|
|
1198
1103
|
reportProgress(
|
|
1199
1104
|
options,
|
|
1200
1105
|
5,
|
|
@@ -1234,12 +1139,15 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
1234
1139
|
{ cwd: runnerDir, env: repositoryRunnerEnvironment(hostEnv) }
|
|
1235
1140
|
);
|
|
1236
1141
|
} catch {
|
|
1237
|
-
const cleanup = await Promise.allSettled([
|
|
1142
|
+
const cleanup = await Promise.allSettled([
|
|
1143
|
+
adapters.remove(runnerDir),
|
|
1144
|
+
adapters.remove(maskDir)
|
|
1145
|
+
]);
|
|
1238
1146
|
const cleanupSucceeded = cleanup.every(
|
|
1239
1147
|
(result) => result.status === "fulfilled"
|
|
1240
1148
|
);
|
|
1241
1149
|
throw new RunnerInstallError(
|
|
1242
|
-
cleanupSucceeded ? `Registering ${runnerName} failed. Partial local runner files were removed so installation can be retried. Check GitHub Settings > Actions > Runners for an orphan registration; the one-time token was not stored.` : `Registering ${runnerName} failed and partial local runner files could not be fully removed. Remove only ${runnerDir}, check GitHub Settings > Actions > Runners for an orphan registration, then retry. The one-time token was not stored.`
|
|
1150
|
+
cleanupSucceeded ? `Registering ${runnerName} failed. Partial local runner files were removed so installation can be retried. Check GitHub Settings > Actions > Runners for an orphan registration; the one-time token was not stored.` : `Registering ${runnerName} failed and partial local runner files could not be fully removed. Remove only ${runnerDir} and ${maskDir}, check GitHub Settings > Actions > Runners for an orphan registration, then retry. The one-time token was not stored.`
|
|
1243
1151
|
);
|
|
1244
1152
|
}
|
|
1245
1153
|
reportProgress(options, 7, progressTotal, "Registered runner with GitHub");
|
|
@@ -1481,6 +1389,7 @@ var requireRunnerContext = async (repoDir, adapters) => {
|
|
|
1481
1389
|
}
|
|
1482
1390
|
const configDir = join(repoDir, CONFIG_DIR);
|
|
1483
1391
|
const runnerDir = join(configDir, RUNNER_DIR);
|
|
1392
|
+
const maskDir = join(configDir, RUNNER_SANDBOX_MASK_DIR);
|
|
1484
1393
|
if (!await adapters.exists(configDir)) {
|
|
1485
1394
|
throw new RunnerControlError(
|
|
1486
1395
|
`No ${CONFIG_DIR}/ found. Run \`shipyard init\` in this repository first.`
|
|
@@ -1630,6 +1539,7 @@ var requireRunnerContext = async (repoDir, adapters) => {
|
|
|
1630
1539
|
});
|
|
1631
1540
|
return {
|
|
1632
1541
|
runnerDir,
|
|
1542
|
+
maskDir,
|
|
1633
1543
|
repository,
|
|
1634
1544
|
metadata,
|
|
1635
1545
|
hostEnvironment,
|
|
@@ -1683,6 +1593,7 @@ var startRepositoryRunnerManaged = async (options, adapters = defaultAdapters2)
|
|
|
1683
1593
|
{
|
|
1684
1594
|
repoDir: options.repoDir,
|
|
1685
1595
|
runnerDir: context.runnerDir,
|
|
1596
|
+
maskDir: context.maskDir,
|
|
1686
1597
|
metadata: context.metadata,
|
|
1687
1598
|
runnerEnvironment: repositoryRunnerEnvironment(context.hostEnvironment),
|
|
1688
1599
|
dockerEnvironment: context.runtimeEnvironment
|
|
@@ -2212,38 +2123,19 @@ var installRunnerCommand = Command.make(
|
|
|
2212
2123
|
{ registrationToken: registrationTokenOption },
|
|
2213
2124
|
({ registrationToken }) => Effect.gen(function* () {
|
|
2214
2125
|
const display = yield* Display;
|
|
2215
|
-
const
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
}
|
|
2225
|
-
|
|
2226
|
-
},
|
|
2227
|
-
install: () => runEffectPromise(
|
|
2228
|
-
display.progress(
|
|
2229
|
-
"Installing repository runner",
|
|
2230
|
-
(report) => Effect.tryPromise({
|
|
2231
|
-
try: () => installRepositoryRunner({
|
|
2232
|
-
repoDir,
|
|
2233
|
-
registrationToken: registrationToken._tag === "Some" ? registrationToken.value : void 0,
|
|
2234
|
-
onProgress: report
|
|
2235
|
-
}),
|
|
2236
|
-
catch: (error) => error instanceof RunnerInstallError ? error : new InitError({
|
|
2237
|
-
message: `Repository runner installation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2238
|
-
})
|
|
2239
|
-
})
|
|
2240
|
-
)
|
|
2241
|
-
)
|
|
2242
|
-
}),
|
|
2243
|
-
catch: (error) => new InitError({
|
|
2244
|
-
message: error instanceof RunnerInstallError ? error.message : `Repository runner installation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2126
|
+
const result = yield* display.progress(
|
|
2127
|
+
"Installing repository runner",
|
|
2128
|
+
(report) => Effect.tryPromise({
|
|
2129
|
+
try: () => installRepositoryRunner({
|
|
2130
|
+
repoDir: process.cwd(),
|
|
2131
|
+
registrationToken: registrationToken._tag === "Some" ? registrationToken.value : void 0,
|
|
2132
|
+
onProgress: report
|
|
2133
|
+
}),
|
|
2134
|
+
catch: (error) => new InitError({
|
|
2135
|
+
message: error instanceof RunnerInstallError ? error.message : `Repository runner installation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2136
|
+
})
|
|
2245
2137
|
})
|
|
2246
|
-
|
|
2138
|
+
);
|
|
2247
2139
|
yield* display.status(
|
|
2248
2140
|
`Installed ${result.name} for ${result.repository}.`,
|
|
2249
2141
|
"success"
|
|
@@ -2367,7 +2259,7 @@ var runnerCommand = Command.make(
|
|
|
2367
2259
|
purgeRunnerCommand
|
|
2368
2260
|
])
|
|
2369
2261
|
);
|
|
2370
|
-
var VERSION = "0.7.0
|
|
2262
|
+
var VERSION = "0.7.0" ;
|
|
2371
2263
|
|
|
2372
2264
|
// src/cli.ts
|
|
2373
2265
|
var imageNameOption = Options.text("image-name").pipe(
|
|
@@ -2535,7 +2427,7 @@ var runCommand2 = Command.make(
|
|
|
2535
2427
|
);
|
|
2536
2428
|
var templateOption = Options.text("template").pipe(
|
|
2537
2429
|
Options.withDescription(
|
|
2538
|
-
"Template to scaffold (e.g. simple-loop, parallel-planner)"
|
|
2430
|
+
"Template to scaffold (e.g. blank, simple-loop, parallel-planner)"
|
|
2539
2431
|
),
|
|
2540
2432
|
Options.optional
|
|
2541
2433
|
);
|
|
@@ -2669,12 +2561,12 @@ var initCommand = Command.make(
|
|
|
2669
2561
|
yield* failIfNonInteractive(params.flag);
|
|
2670
2562
|
}
|
|
2671
2563
|
const confirmed = yield* Effect.promise(
|
|
2672
|
-
() =>
|
|
2564
|
+
() => clack.confirm({
|
|
2673
2565
|
message: params.promptMessage,
|
|
2674
2566
|
initialValue: true
|
|
2675
2567
|
})
|
|
2676
2568
|
);
|
|
2677
|
-
if (
|
|
2569
|
+
if (clack.isCancel(confirmed)) {
|
|
2678
2570
|
yield* Effect.fail(
|
|
2679
2571
|
new InitError({ message: params.cancelMessage })
|
|
2680
2572
|
);
|
|
@@ -2699,7 +2591,7 @@ var initCommand = Command.make(
|
|
|
2699
2591
|
yield* failIfNonInteractive("--agent");
|
|
2700
2592
|
}
|
|
2701
2593
|
const selected = yield* Effect.promise(
|
|
2702
|
-
() =>
|
|
2594
|
+
() => clack.select({
|
|
2703
2595
|
message: "Select an agent:",
|
|
2704
2596
|
initialValue: DEFAULT_AGENT_NAME,
|
|
2705
2597
|
options: agents.map((a) => ({
|
|
@@ -2709,7 +2601,7 @@ var initCommand = Command.make(
|
|
|
2709
2601
|
}))
|
|
2710
2602
|
})
|
|
2711
2603
|
);
|
|
2712
|
-
if (
|
|
2604
|
+
if (clack.isCancel(selected)) {
|
|
2713
2605
|
yield* Effect.fail(
|
|
2714
2606
|
new InitError({ message: "Agent selection cancelled." })
|
|
2715
2607
|
);
|
|
@@ -2722,7 +2614,7 @@ var initCommand = Command.make(
|
|
|
2722
2614
|
requested: codexAuthFlag._tag === "Some" ? codexAuthFlag.value : void 0,
|
|
2723
2615
|
interactive: isInteractive,
|
|
2724
2616
|
select: async () => {
|
|
2725
|
-
const selected = await
|
|
2617
|
+
const selected = await clack.select({
|
|
2726
2618
|
message: "How will you authenticate Codex?",
|
|
2727
2619
|
initialValue: "chatgpt",
|
|
2728
2620
|
options: [
|
|
@@ -2738,7 +2630,7 @@ var initCommand = Command.make(
|
|
|
2738
2630
|
}
|
|
2739
2631
|
]
|
|
2740
2632
|
});
|
|
2741
|
-
return
|
|
2633
|
+
return clack.isCancel(selected) ? void 0 : selected;
|
|
2742
2634
|
}
|
|
2743
2635
|
}),
|
|
2744
2636
|
catch: (error) => error instanceof InitError ? error : new InitError({
|
|
@@ -2757,7 +2649,32 @@ var initCommand = Command.make(
|
|
|
2757
2649
|
})
|
|
2758
2650
|
});
|
|
2759
2651
|
}
|
|
2760
|
-
const
|
|
2652
|
+
const sandboxProviders = listSandboxProviders();
|
|
2653
|
+
let selectedSandboxProvider;
|
|
2654
|
+
if (sandboxFlag._tag === "Some") {
|
|
2655
|
+
selectedSandboxProvider = getSandboxProvider(sandboxFlag.value);
|
|
2656
|
+
} else {
|
|
2657
|
+
if (!isInteractive) {
|
|
2658
|
+
yield* failIfNonInteractive("--sandbox");
|
|
2659
|
+
}
|
|
2660
|
+
const selected = yield* Effect.promise(
|
|
2661
|
+
() => clack.select({
|
|
2662
|
+
message: "Select a sandbox provider:",
|
|
2663
|
+
options: sandboxProviders.map((p) => ({
|
|
2664
|
+
value: p.name,
|
|
2665
|
+
label: p.label
|
|
2666
|
+
}))
|
|
2667
|
+
})
|
|
2668
|
+
);
|
|
2669
|
+
if (clack.isCancel(selected)) {
|
|
2670
|
+
yield* Effect.fail(
|
|
2671
|
+
new InitError({
|
|
2672
|
+
message: "Sandbox provider selection cancelled."
|
|
2673
|
+
})
|
|
2674
|
+
);
|
|
2675
|
+
}
|
|
2676
|
+
selectedSandboxProvider = getSandboxProvider(selected);
|
|
2677
|
+
}
|
|
2761
2678
|
const issueTrackers = listIssueTrackers();
|
|
2762
2679
|
let selectedIssueTracker;
|
|
2763
2680
|
if (issueTrackerFlag._tag === "Some") {
|
|
@@ -2767,7 +2684,7 @@ var initCommand = Command.make(
|
|
|
2767
2684
|
yield* failIfNonInteractive("--issue-tracker");
|
|
2768
2685
|
}
|
|
2769
2686
|
const selected = yield* Effect.promise(
|
|
2770
|
-
() =>
|
|
2687
|
+
() => clack.select({
|
|
2771
2688
|
message: "Select an issue tracker:",
|
|
2772
2689
|
initialValue: "github-issues",
|
|
2773
2690
|
options: issueTrackers.map((b) => ({
|
|
@@ -2776,7 +2693,7 @@ var initCommand = Command.make(
|
|
|
2776
2693
|
}))
|
|
2777
2694
|
})
|
|
2778
2695
|
);
|
|
2779
|
-
if (
|
|
2696
|
+
if (clack.isCancel(selected)) {
|
|
2780
2697
|
yield* Effect.fail(
|
|
2781
2698
|
new InitError({
|
|
2782
2699
|
message: "Issue tracker selection cancelled."
|
|
@@ -2793,9 +2710,9 @@ var initCommand = Command.make(
|
|
|
2793
2710
|
yield* failIfNonInteractive("--template");
|
|
2794
2711
|
}
|
|
2795
2712
|
const selected = yield* Effect.promise(
|
|
2796
|
-
() =>
|
|
2713
|
+
() => clack.select({
|
|
2797
2714
|
message: "Select a template:",
|
|
2798
|
-
initialValue: "
|
|
2715
|
+
initialValue: "blank",
|
|
2799
2716
|
options: templates.map((tmpl) => ({
|
|
2800
2717
|
value: tmpl.name,
|
|
2801
2718
|
label: tmpl.name,
|
|
@@ -2803,7 +2720,7 @@ var initCommand = Command.make(
|
|
|
2803
2720
|
}))
|
|
2804
2721
|
})
|
|
2805
2722
|
);
|
|
2806
|
-
if (
|
|
2723
|
+
if (clack.isCancel(selected)) {
|
|
2807
2724
|
yield* Effect.fail(
|
|
2808
2725
|
new InitError({ message: "Template selection cancelled." })
|
|
2809
2726
|
);
|
|
@@ -2855,17 +2772,15 @@ var initCommand = Command.make(
|
|
|
2855
2772
|
console.warn(message);
|
|
2856
2773
|
}
|
|
2857
2774
|
}
|
|
2858
|
-
yield* d.
|
|
2775
|
+
yield* d.spinner(
|
|
2859
2776
|
`Scaffolding ${CONFIG_DIR}/ config directory...`,
|
|
2860
|
-
|
|
2777
|
+
scaffold(cwd, {
|
|
2861
2778
|
agent: selectedAgent,
|
|
2862
2779
|
model: selectedModel,
|
|
2863
|
-
modelExplicit: modelFlag._tag === "Some",
|
|
2864
2780
|
templateName: selectedTemplate,
|
|
2865
2781
|
issueTracker: selectedIssueTracker,
|
|
2866
2782
|
sandboxProvider: selectedSandboxProvider,
|
|
2867
|
-
codexAuth: selectedCodexAuth
|
|
2868
|
-
onProgress: report
|
|
2783
|
+
codexAuth: selectedCodexAuth
|
|
2869
2784
|
}).pipe(
|
|
2870
2785
|
Effect.mapError(
|
|
2871
2786
|
(e) => new InitError({
|
|
@@ -2928,39 +2843,28 @@ var initCommand = Command.make(
|
|
|
2928
2843
|
interactive: isInteractive,
|
|
2929
2844
|
requested: installRunnerChoice._tag === "Some" ? installRunnerChoice.value : void 0,
|
|
2930
2845
|
confirm: async ({ message, initialValue }) => {
|
|
2931
|
-
const confirmed = await
|
|
2932
|
-
if (
|
|
2846
|
+
const confirmed = await clack.confirm({ message, initialValue });
|
|
2847
|
+
if (clack.isCancel(confirmed)) {
|
|
2933
2848
|
throw new InitError({
|
|
2934
2849
|
message: "Repository-runner installation selection cancelled."
|
|
2935
2850
|
});
|
|
2936
2851
|
}
|
|
2937
2852
|
return confirmed === true;
|
|
2938
2853
|
},
|
|
2939
|
-
install: () =>
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
install: () => runEffectPromise(
|
|
2950
|
-
d.progress(
|
|
2951
|
-
"Installing repository runner",
|
|
2952
|
-
(report) => Effect.tryPromise({
|
|
2953
|
-
try: () => installRepositoryRunner({
|
|
2954
|
-
repoDir: cwd,
|
|
2955
|
-
onProgress: report
|
|
2956
|
-
}),
|
|
2957
|
-
catch: (error) => error instanceof RunnerInstallError ? error : new InitError({
|
|
2958
|
-
message: `Repository runner installation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2959
|
-
})
|
|
2854
|
+
install: () => Effect.runPromise(
|
|
2855
|
+
d.progress(
|
|
2856
|
+
"Installing repository runner",
|
|
2857
|
+
(report) => Effect.tryPromise({
|
|
2858
|
+
try: () => installRepositoryRunner({
|
|
2859
|
+
repoDir: cwd,
|
|
2860
|
+
onProgress: report
|
|
2861
|
+
}),
|
|
2862
|
+
catch: (error) => new InitError({
|
|
2863
|
+
message: error instanceof RunnerInstallError ? error.message : `Repository runner installation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2960
2864
|
})
|
|
2961
|
-
)
|
|
2865
|
+
})
|
|
2962
2866
|
)
|
|
2963
|
-
|
|
2867
|
+
)
|
|
2964
2868
|
}),
|
|
2965
2869
|
catch: (error) => error instanceof InitError ? error : new InitError({
|
|
2966
2870
|
message: `Repository-runner setup failed: ${error instanceof Error ? error.message : String(error)}`
|