@snappedly-tools/shipyard 0.6.0 → 0.7.0-staging.36082887921
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 +64 -165
- package/dist/MountConfig-BHnKnA4h.d.ts +145 -0
- package/dist/{chunk-WAXEMZUV.js → chunk-44I2BL6E.js} +225 -33
- package/dist/chunk-44I2BL6E.js.map +1 -0
- package/dist/{chunk-56TDSWFU.js → chunk-JI3HDDMS.js} +3 -3
- package/dist/{chunk-56TDSWFU.js.map → chunk-JI3HDDMS.js.map} +1 -1
- package/dist/{chunk-FDYOTN55.js → chunk-L6PX5QTU.js} +253 -878
- package/dist/chunk-L6PX5QTU.js.map +1 -0
- package/dist/index.d.ts +117 -97
- package/dist/index.js +322 -344
- package/dist/index.js.map +1 -1
- package/dist/main.js +235 -139
- package/dist/main.js.map +1 -1
- package/dist/sandboxes/docker.d.ts +1 -3
- package/dist/sandboxes/docker.js +2 -4
- package/dist/templates/parallel-planner/main.mts +67 -9
- package/dist/templates/parallel-planner/planner-branch.mts +151 -0
- package/dist/templates/parallel-planner-with-review/main.mts +70 -12
- package/dist/templates/parallel-planner-with-review/planner-branch.mts +151 -0
- package/dist/templates/sequential-reviewer/main.mts +52 -3
- package/dist/templates/simple-loop/main.mts +51 -2
- package/package.json +1 -17
- package/dist/MountConfig-bZoCs4Dd.d.ts +0 -26
- package/dist/SandboxProvider-XJQqEdSf.d.ts +0 -261
- package/dist/chunk-ACD46ZM4.js +0 -136
- package/dist/chunk-ACD46ZM4.js.map +0 -1
- package/dist/chunk-FDYOTN55.js.map +0 -1
- package/dist/chunk-KMGNFXKN.js +0 -38
- package/dist/chunk-KMGNFXKN.js.map +0 -1
- package/dist/chunk-SOJTAJTF.js +0 -78
- package/dist/chunk-SOJTAJTF.js.map +0 -1
- package/dist/chunk-WAXEMZUV.js.map +0 -1
- package/dist/sandboxes/no-sandbox.d.ts +0 -37
- package/dist/sandboxes/no-sandbox.js +0 -4
- package/dist/sandboxes/no-sandbox.js.map +0 -1
- package/dist/sandboxes/vercel.d.ts +0 -104
- package/dist/sandboxes/vercel.js +0 -166
- package/dist/sandboxes/vercel.js.map +0 -1
- package/dist/templates/blank/main.mts +0 -13
- package/dist/templates/blank/prompt.md +0 -12
- package/dist/templates/blank/template.json +0 -4
package/dist/main.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
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,
|
|
5
|
-
import './chunk-KMGNFXKN.js';
|
|
6
|
-
import './chunk-ACD46ZM4.js';
|
|
2
|
+
import { CODEX_MODELS, purgeRunLogs, withFriendlyErrors, DEFAULT_LOG_RETENTION_DAYS, resolveEnv } from './chunk-JI3HDDMS.js';
|
|
3
|
+
import { buildImage, removeImage, RunnerLifecycleError, removeRepositoryRunner, defaultImageName, validateExistingRepositoryRunner, RUNNER_INSTALL_METADATA, RUNNER_CONTROLLER_LOCK, recoverRepositoryRunner, REPOSITORY_RUNNER_OWNER_ENV, removeOwnedRepositoryRunnerContainers, RUNNER_WORK_DIR } from './chunk-44I2BL6E.js';
|
|
4
|
+
import { SANDBOX_REPO_DIR, Display, InitError, ACTIVATION_LABEL, PRODUCT_NAME, CONFIG_DIR, CLI_NAME, ClackDisplay, ConfigDirError, ExecHostError, LOCKS_DIR, RUNNER_DIR, assertProtectedDirectoryIdentities, repositoryRunnerEnvironment } from './chunk-L6PX5QTU.js';
|
|
7
5
|
import { NodeContext, NodeRuntime, NodeFileSystem } from '@effect/platform-node';
|
|
8
|
-
import { Effect, Layer, Option } from 'effect';
|
|
6
|
+
import { Effect, Layer, Option, Exit, Cause } from 'effect';
|
|
9
7
|
import { Options, Command } from '@effect/cli';
|
|
10
|
-
import * as
|
|
8
|
+
import * as clack2 from '@clack/prompts';
|
|
11
9
|
import { execFile, execSync, spawn, execFileSync } from 'child_process';
|
|
12
10
|
import { existsSync, constants, statSync } from 'fs';
|
|
13
11
|
import { join, resolve, dirname } from 'path';
|
|
@@ -19,18 +17,12 @@ import { createHash } from 'crypto';
|
|
|
19
17
|
import { chmod, lstat, realpath, rm, mkdir, writeFile, readFile, access } from 'fs/promises';
|
|
20
18
|
import 'module';
|
|
21
19
|
|
|
22
|
-
var
|
|
20
|
+
var configDirExists = (repoDir) => Effect.gen(function* () {
|
|
23
21
|
const fs = yield* FileSystem.FileSystem;
|
|
24
|
-
return yield* fs.exists(
|
|
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";
|
|
22
|
+
return yield* fs.exists(join(repoDir, CONFIG_DIR)).pipe(Effect.catchAll(() => Effect.succeed(false)));
|
|
30
23
|
});
|
|
31
24
|
var requireCanonicalConfigDir = (repoDir) => Effect.gen(function* () {
|
|
32
|
-
|
|
33
|
-
if (state === "missing") {
|
|
25
|
+
if (!(yield* configDirExists(repoDir))) {
|
|
34
26
|
return yield* Effect.fail(
|
|
35
27
|
new ConfigDirError({
|
|
36
28
|
message: `No ${CONFIG_DIR}/ found. Run \`${CLI_NAME} init\` first.`
|
|
@@ -40,8 +32,7 @@ var requireCanonicalConfigDir = (repoDir) => Effect.gen(function* () {
|
|
|
40
32
|
return join(repoDir, CONFIG_DIR);
|
|
41
33
|
});
|
|
42
34
|
var assertConfigDirAvailable = (repoDir) => Effect.gen(function* () {
|
|
43
|
-
|
|
44
|
-
if (state === "canonical") {
|
|
35
|
+
if (yield* configDirExists(repoDir)) {
|
|
45
36
|
return yield* Effect.fail(
|
|
46
37
|
new ConfigDirError({
|
|
47
38
|
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.`
|
|
@@ -71,11 +62,16 @@ var CODEX_CHATGPT_AUTH_OPTIONS = `{
|
|
|
71
62
|
},
|
|
72
63
|
],
|
|
73
64
|
}`;
|
|
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
|
-
},
|
|
79
75
|
{
|
|
80
76
|
name: "simple-loop",
|
|
81
77
|
description: "Implements standalone issues one by one and opens review PRs"
|
|
@@ -175,7 +171,7 @@ RUN apt-get update && apt-get install -y \\
|
|
|
175
171
|
|
|
176
172
|
# Build-args for UID/GID alignment: shipyard docker build-image
|
|
177
173
|
# defaults these to the host user's UID/GID so image-built files
|
|
178
|
-
# and
|
|
174
|
+
# and explicit mounted files share an owner without runtime chown.
|
|
179
175
|
ARG AGENT_UID=1000
|
|
180
176
|
ARG AGENT_GID=1000
|
|
181
177
|
|
|
@@ -191,8 +187,8 @@ ENV PATH="/home/agent/.local/bin:$PATH"
|
|
|
191
187
|
|
|
192
188
|
WORKDIR /home/agent
|
|
193
189
|
|
|
194
|
-
#
|
|
195
|
-
# and
|
|
190
|
+
# Shipyard syncs Git history into a sandbox-owned repository at ${SANDBOX_REPO_DIR}
|
|
191
|
+
# and runs agent commands from ${SANDBOX_REPO_DIR}.
|
|
196
192
|
# Structure your Dockerfile so that ${SANDBOX_REPO_DIR} can serve as the project root.
|
|
197
193
|
ENTRYPOINT ["sleep", "infinity"]
|
|
198
194
|
`;
|
|
@@ -209,7 +205,7 @@ RUN apt-get update && apt-get install -y \\
|
|
|
209
205
|
|
|
210
206
|
# Build-args for UID/GID alignment: shipyard docker build-image
|
|
211
207
|
# defaults these to the host user's UID/GID so image-built files
|
|
212
|
-
# and
|
|
208
|
+
# and explicit mounted files share an owner without runtime chown.
|
|
213
209
|
ARG AGENT_UID=1000
|
|
214
210
|
ARG AGENT_GID=1000
|
|
215
211
|
|
|
@@ -223,8 +219,8 @@ USER \${AGENT_UID}:\${AGENT_GID}
|
|
|
223
219
|
|
|
224
220
|
WORKDIR /home/agent
|
|
225
221
|
|
|
226
|
-
#
|
|
227
|
-
# and
|
|
222
|
+
# Shipyard syncs Git history into a sandbox-owned repository at ${SANDBOX_REPO_DIR}
|
|
223
|
+
# and runs agent commands from ${SANDBOX_REPO_DIR}.
|
|
228
224
|
# Structure your Dockerfile so that ${SANDBOX_REPO_DIR} can serve as the project root.
|
|
229
225
|
ENTRYPOINT ["sleep", "infinity"]
|
|
230
226
|
`;
|
|
@@ -339,34 +335,26 @@ var copyTemplateFiles = (templateDir, destDir, mainFilename) => Effect.gen(funct
|
|
|
339
335
|
{ concurrency: "unbounded" }
|
|
340
336
|
);
|
|
341
337
|
});
|
|
342
|
-
var rewriteMainTs = (configDir, agent, model, sandboxProvider, mainFilename, codexAuth) => Effect.gen(function* () {
|
|
338
|
+
var rewriteMainTs = (configDir, agent, model, modelExplicit, sandboxProvider, mainFilename, codexAuth) => Effect.gen(function* () {
|
|
343
339
|
const fs = yield* FileSystem.FileSystem;
|
|
344
340
|
const mainTsPath = join(configDir, mainFilename);
|
|
345
341
|
const exists = yield* fs.exists(mainTsPath).pipe(Effect.mapError((e) => new Error(e.message)));
|
|
346
342
|
if (!exists) return;
|
|
347
343
|
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
|
-
}
|
|
351
344
|
content = content.replace(
|
|
352
345
|
new RegExp(`\\b${TEMPLATE_AGENT_FACTORY}\\b`, "g"),
|
|
353
346
|
agent.factoryImport
|
|
354
347
|
);
|
|
355
|
-
const factoryCallRe = new RegExp(
|
|
356
|
-
`${agent.factoryImport}\\(([^)\\n]*)\\)`,
|
|
357
|
-
"g"
|
|
358
|
-
);
|
|
359
348
|
content = content.replace(
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
}
|
|
349
|
+
"const CODEX_PROVIDER = true;",
|
|
350
|
+
`const CODEX_PROVIDER = ${agent.name === "codex"};`
|
|
367
351
|
);
|
|
368
|
-
if (agent.name !== "codex" ||
|
|
369
|
-
|
|
352
|
+
if (agent.name !== "codex" || modelExplicit) {
|
|
353
|
+
const modelLiteral = JSON.stringify(model);
|
|
354
|
+
content = content.replace(
|
|
355
|
+
/shipyard\.CODEX_MODELS\.(?:routine|strong)/g,
|
|
356
|
+
modelLiteral
|
|
357
|
+
);
|
|
370
358
|
}
|
|
371
359
|
if (agent.name === "codex" && codexAuth === "chatgpt") {
|
|
372
360
|
content = content.replace(
|
|
@@ -434,13 +422,20 @@ var scaffold = (repoDir, options) => Effect.gen(function* () {
|
|
|
434
422
|
const {
|
|
435
423
|
agent,
|
|
436
424
|
model,
|
|
437
|
-
|
|
425
|
+
modelExplicit = false,
|
|
426
|
+
templateName = "simple-loop",
|
|
438
427
|
issueTracker = ISSUE_TRACKER_REGISTRY[0],
|
|
439
428
|
// default: github-issues
|
|
440
429
|
sandboxProvider = SANDBOX_PROVIDER_REGISTRY[0],
|
|
441
430
|
// default: docker
|
|
442
431
|
codexAuth = "api-key"
|
|
443
432
|
} = options;
|
|
433
|
+
const totalProgressSteps = 5;
|
|
434
|
+
const reportProgress2 = (current, message) => options.onProgress?.({
|
|
435
|
+
current,
|
|
436
|
+
total: totalProgressSteps,
|
|
437
|
+
message
|
|
438
|
+
});
|
|
444
439
|
if (codexAuth === "chatgpt" && agent.name !== "codex") {
|
|
445
440
|
return yield* Effect.fail(
|
|
446
441
|
new Error(
|
|
@@ -452,11 +447,14 @@ var scaffold = (repoDir, options) => Effect.gen(function* () {
|
|
|
452
447
|
yield* assertConfigDirAvailable(repoDir);
|
|
453
448
|
const configDir = join(repoDir, CONFIG_DIR);
|
|
454
449
|
const mainFilename = yield* detectMainFilename(repoDir);
|
|
450
|
+
reportProgress2(1, "Validated repository and configuration");
|
|
455
451
|
yield* fs.makeDirectory(configDir, { recursive: false }).pipe(Effect.mapError((e) => new Error(e.message)));
|
|
456
452
|
const templateDir = yield* getTemplateDir(templateName);
|
|
453
|
+
reportProgress2(2, `Prepared ${CONFIG_DIR}/ directory`);
|
|
457
454
|
const envExampleParts = [
|
|
458
455
|
agent.name === "codex" && codexAuth === "chatgpt" ? CODEX_CHATGPT_ENV_EXAMPLE : agent.envExample
|
|
459
456
|
];
|
|
457
|
+
envExampleParts.push(roleModelEnvExample(agent.name));
|
|
460
458
|
if (issueTracker.envExample) {
|
|
461
459
|
envExampleParts.push(issueTracker.envExample);
|
|
462
460
|
}
|
|
@@ -475,15 +473,19 @@ var scaffold = (repoDir, options) => Effect.gen(function* () {
|
|
|
475
473
|
],
|
|
476
474
|
{ concurrency: "unbounded" }
|
|
477
475
|
);
|
|
476
|
+
reportProgress2(3, "Wrote configuration and template files");
|
|
478
477
|
yield* rewriteMainTs(
|
|
479
478
|
configDir,
|
|
480
479
|
agent,
|
|
481
480
|
model,
|
|
481
|
+
modelExplicit,
|
|
482
482
|
sandboxProvider,
|
|
483
483
|
mainFilename,
|
|
484
484
|
codexAuth
|
|
485
485
|
);
|
|
486
|
+
reportProgress2(4, "Configured selected agent and sandbox");
|
|
486
487
|
yield* substituteTemplateArgs(configDir, issueTracker);
|
|
488
|
+
reportProgress2(5, "Applied issue tracker settings");
|
|
487
489
|
return { mainFilename };
|
|
488
490
|
});
|
|
489
491
|
var resolveCodexAuthMode = async (options) => {
|
|
@@ -562,6 +564,11 @@ var ensureCodexChatGptAuth = (options) => {
|
|
|
562
564
|
});
|
|
563
565
|
}
|
|
564
566
|
};
|
|
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
|
+
};
|
|
565
572
|
var REPOSITORY_RUNNER_WORKFLOW_PATH = ".github/workflows/shipyard-wake.yml";
|
|
566
573
|
var REPOSITORY_RUNNER_WAKE_EXECUTABLE = "shipyard-wake";
|
|
567
574
|
var REPOSITORY_RUNNER_WORKFLOW = `name: Shipyard wake-up
|
|
@@ -756,6 +763,22 @@ var RELEASES_API = "https://api.github.com/repos/actions/runner/releases/latest"
|
|
|
756
763
|
var RunnerInstallError = class extends Error {
|
|
757
764
|
name = "RunnerInstallError";
|
|
758
765
|
};
|
|
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
|
+
};
|
|
759
782
|
var defaultAdapters = {
|
|
760
783
|
platform: () => process.platform,
|
|
761
784
|
arch: () => process.arch,
|
|
@@ -879,9 +902,7 @@ var selectArm64Asset = (release) => {
|
|
|
879
902
|
var appendRunnerIgnores = async (gitignorePath, adapters) => {
|
|
880
903
|
const current = await adapters.exists(gitignorePath) ? await adapters.readText(gitignorePath) : "";
|
|
881
904
|
const lines = new Set(current.split(/\r?\n/));
|
|
882
|
-
const additions = [`${RUNNER_DIR}
|
|
883
|
-
(line) => !lines.has(line)
|
|
884
|
-
);
|
|
905
|
+
const additions = [`${RUNNER_DIR}/`].filter((line) => !lines.has(line));
|
|
885
906
|
if (additions.length === 0) return;
|
|
886
907
|
const prefix = current.length === 0 || current.endsWith("\n") ? current : `${current}
|
|
887
908
|
`;
|
|
@@ -892,6 +913,88 @@ var commandFailure = (purpose, error) => new RunnerInstallError(
|
|
|
892
913
|
`${purpose} failed: ${error instanceof Error ? error.message : String(error)}`
|
|
893
914
|
);
|
|
894
915
|
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
|
+
};
|
|
895
998
|
var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
896
999
|
if (adapters.platform() !== "darwin" || adapters.arch() !== "arm64") {
|
|
897
1000
|
throw new RunnerInstallError(
|
|
@@ -900,7 +1003,6 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
900
1003
|
}
|
|
901
1004
|
const configDir = join(options.repoDir, CONFIG_DIR);
|
|
902
1005
|
const runnerDir = join(configDir, RUNNER_DIR);
|
|
903
|
-
const maskDir = join(configDir, RUNNER_SANDBOX_MASK_DIR);
|
|
904
1006
|
if (!await adapters.exists(configDir)) {
|
|
905
1007
|
throw new RunnerInstallError(
|
|
906
1008
|
`No ${CONFIG_DIR}/ found. Run \`shipyard init\` in this repository first.`
|
|
@@ -956,7 +1058,6 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
956
1058
|
{
|
|
957
1059
|
repoDir: options.repoDir,
|
|
958
1060
|
runnerDir,
|
|
959
|
-
maskDir,
|
|
960
1061
|
repository,
|
|
961
1062
|
runnerName
|
|
962
1063
|
},
|
|
@@ -969,10 +1070,8 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
969
1070
|
}
|
|
970
1071
|
await appendRunnerIgnores(join(configDir, ".gitignore"), adapters);
|
|
971
1072
|
await adapters.chmod(runnerDir, 448);
|
|
972
|
-
await adapters.chmod(maskDir, 448);
|
|
973
1073
|
await assertProtectedDirectoryIdentities(
|
|
974
1074
|
runnerDir,
|
|
975
|
-
maskDir,
|
|
976
1075
|
adapters.inspectDirectory
|
|
977
1076
|
).catch((error) => {
|
|
978
1077
|
throw commandFailure("Validating protected runner directories", error);
|
|
@@ -1003,7 +1102,7 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
1003
1102
|
"--paginate",
|
|
1004
1103
|
`repos/${repository}/actions/runners`,
|
|
1005
1104
|
"--jq",
|
|
1006
|
-
`.runners[] | select(any(.labels[]; .name == "${ACTIVATION_LABEL}")) |
|
|
1105
|
+
`.runners[] | select(any(.labels[]; .name == "${ACTIVATION_LABEL}")) | {id, name, status, busy}`
|
|
1007
1106
|
],
|
|
1008
1107
|
{ cwd: options.repoDir, env: hostEnv }
|
|
1009
1108
|
).catch((error) => {
|
|
@@ -1012,10 +1111,11 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
1012
1111
|
error
|
|
1013
1112
|
);
|
|
1014
1113
|
});
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1114
|
+
const existingRegistrations = parseExistingRunnerRegistrations(
|
|
1115
|
+
existing.stdout
|
|
1116
|
+
);
|
|
1117
|
+
if (existingRegistrations.length > 0) {
|
|
1118
|
+
throw new RunnerInstallConflictError(repository, existingRegistrations);
|
|
1019
1119
|
}
|
|
1020
1120
|
reportProgress(
|
|
1021
1121
|
options,
|
|
@@ -1088,18 +1188,13 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
1088
1188
|
await adapters.makeDirectory(runnerDir).catch((error) => {
|
|
1089
1189
|
throw commandFailure("Creating the protected runner directory", error);
|
|
1090
1190
|
});
|
|
1091
|
-
if (!await adapters.exists(maskDir)) {
|
|
1092
|
-
await adapters.makeDirectory(maskDir);
|
|
1093
|
-
}
|
|
1094
1191
|
await assertProtectedDirectoryIdentities(
|
|
1095
1192
|
runnerDir,
|
|
1096
|
-
maskDir,
|
|
1097
1193
|
adapters.inspectDirectory
|
|
1098
1194
|
).catch((error) => {
|
|
1099
1195
|
throw commandFailure("Validating protected runner directories", error);
|
|
1100
1196
|
});
|
|
1101
1197
|
await adapters.chmod(runnerDir, 448);
|
|
1102
|
-
await adapters.chmod(maskDir, 448);
|
|
1103
1198
|
reportProgress(
|
|
1104
1199
|
options,
|
|
1105
1200
|
5,
|
|
@@ -1139,15 +1234,12 @@ var installRepositoryRunner = async (options, adapters = defaultAdapters) => {
|
|
|
1139
1234
|
{ cwd: runnerDir, env: repositoryRunnerEnvironment(hostEnv) }
|
|
1140
1235
|
);
|
|
1141
1236
|
} catch {
|
|
1142
|
-
const cleanup = await Promise.allSettled([
|
|
1143
|
-
adapters.remove(runnerDir),
|
|
1144
|
-
adapters.remove(maskDir)
|
|
1145
|
-
]);
|
|
1237
|
+
const cleanup = await Promise.allSettled([adapters.remove(runnerDir)]);
|
|
1146
1238
|
const cleanupSucceeded = cleanup.every(
|
|
1147
1239
|
(result) => result.status === "fulfilled"
|
|
1148
1240
|
);
|
|
1149
1241
|
throw new RunnerInstallError(
|
|
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}
|
|
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.`
|
|
1151
1243
|
);
|
|
1152
1244
|
}
|
|
1153
1245
|
reportProgress(options, 7, progressTotal, "Registered runner with GitHub");
|
|
@@ -1389,7 +1481,6 @@ var requireRunnerContext = async (repoDir, adapters) => {
|
|
|
1389
1481
|
}
|
|
1390
1482
|
const configDir = join(repoDir, CONFIG_DIR);
|
|
1391
1483
|
const runnerDir = join(configDir, RUNNER_DIR);
|
|
1392
|
-
const maskDir = join(configDir, RUNNER_SANDBOX_MASK_DIR);
|
|
1393
1484
|
if (!await adapters.exists(configDir)) {
|
|
1394
1485
|
throw new RunnerControlError(
|
|
1395
1486
|
`No ${CONFIG_DIR}/ found. Run \`shipyard init\` in this repository first.`
|
|
@@ -1539,7 +1630,6 @@ var requireRunnerContext = async (repoDir, adapters) => {
|
|
|
1539
1630
|
});
|
|
1540
1631
|
return {
|
|
1541
1632
|
runnerDir,
|
|
1542
|
-
maskDir,
|
|
1543
1633
|
repository,
|
|
1544
1634
|
metadata,
|
|
1545
1635
|
hostEnvironment,
|
|
@@ -1593,7 +1683,6 @@ var startRepositoryRunnerManaged = async (options, adapters = defaultAdapters2)
|
|
|
1593
1683
|
{
|
|
1594
1684
|
repoDir: options.repoDir,
|
|
1595
1685
|
runnerDir: context.runnerDir,
|
|
1596
|
-
maskDir: context.maskDir,
|
|
1597
1686
|
metadata: context.metadata,
|
|
1598
1687
|
runnerEnvironment: repositoryRunnerEnvironment(context.hostEnvironment),
|
|
1599
1688
|
dockerEnvironment: context.runtimeEnvironment
|
|
@@ -2123,19 +2212,38 @@ var installRunnerCommand = Command.make(
|
|
|
2123
2212
|
{ registrationToken: registrationTokenOption },
|
|
2124
2213
|
({ registrationToken }) => Effect.gen(function* () {
|
|
2125
2214
|
const display = yield* Display;
|
|
2126
|
-
const
|
|
2127
|
-
|
|
2128
|
-
(
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2215
|
+
const repoDir = process.cwd();
|
|
2216
|
+
const result = yield* Effect.tryPromise({
|
|
2217
|
+
try: () => installRepositoryRunnerWithReplacement({
|
|
2218
|
+
repoDir,
|
|
2219
|
+
interactive: process.stdin.isTTY === true,
|
|
2220
|
+
confirmReplacement: async (conflict) => {
|
|
2221
|
+
const confirmed = await clack2.confirm({
|
|
2222
|
+
message: conflict.confirmationMessage,
|
|
2223
|
+
initialValue: false
|
|
2224
|
+
});
|
|
2225
|
+
return !clack2.isCancel(confirmed) && confirmed === true;
|
|
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)}`
|
|
2137
2245
|
})
|
|
2138
|
-
);
|
|
2246
|
+
});
|
|
2139
2247
|
yield* display.status(
|
|
2140
2248
|
`Installed ${result.name} for ${result.repository}.`,
|
|
2141
2249
|
"success"
|
|
@@ -2259,7 +2367,7 @@ var runnerCommand = Command.make(
|
|
|
2259
2367
|
purgeRunnerCommand
|
|
2260
2368
|
])
|
|
2261
2369
|
);
|
|
2262
|
-
var VERSION = "0.
|
|
2370
|
+
var VERSION = "0.7.0-staging.36082887921" ;
|
|
2263
2371
|
|
|
2264
2372
|
// src/cli.ts
|
|
2265
2373
|
var imageNameOption = Options.text("image-name").pipe(
|
|
@@ -2427,7 +2535,7 @@ var runCommand2 = Command.make(
|
|
|
2427
2535
|
);
|
|
2428
2536
|
var templateOption = Options.text("template").pipe(
|
|
2429
2537
|
Options.withDescription(
|
|
2430
|
-
"Template to scaffold (e.g.
|
|
2538
|
+
"Template to scaffold (e.g. simple-loop, parallel-planner)"
|
|
2431
2539
|
),
|
|
2432
2540
|
Options.optional
|
|
2433
2541
|
);
|
|
@@ -2561,12 +2669,12 @@ var initCommand = Command.make(
|
|
|
2561
2669
|
yield* failIfNonInteractive(params.flag);
|
|
2562
2670
|
}
|
|
2563
2671
|
const confirmed = yield* Effect.promise(
|
|
2564
|
-
() =>
|
|
2672
|
+
() => clack2.confirm({
|
|
2565
2673
|
message: params.promptMessage,
|
|
2566
2674
|
initialValue: true
|
|
2567
2675
|
})
|
|
2568
2676
|
);
|
|
2569
|
-
if (
|
|
2677
|
+
if (clack2.isCancel(confirmed)) {
|
|
2570
2678
|
yield* Effect.fail(
|
|
2571
2679
|
new InitError({ message: params.cancelMessage })
|
|
2572
2680
|
);
|
|
@@ -2591,7 +2699,7 @@ var initCommand = Command.make(
|
|
|
2591
2699
|
yield* failIfNonInteractive("--agent");
|
|
2592
2700
|
}
|
|
2593
2701
|
const selected = yield* Effect.promise(
|
|
2594
|
-
() =>
|
|
2702
|
+
() => clack2.select({
|
|
2595
2703
|
message: "Select an agent:",
|
|
2596
2704
|
initialValue: DEFAULT_AGENT_NAME,
|
|
2597
2705
|
options: agents.map((a) => ({
|
|
@@ -2601,7 +2709,7 @@ var initCommand = Command.make(
|
|
|
2601
2709
|
}))
|
|
2602
2710
|
})
|
|
2603
2711
|
);
|
|
2604
|
-
if (
|
|
2712
|
+
if (clack2.isCancel(selected)) {
|
|
2605
2713
|
yield* Effect.fail(
|
|
2606
2714
|
new InitError({ message: "Agent selection cancelled." })
|
|
2607
2715
|
);
|
|
@@ -2614,7 +2722,7 @@ var initCommand = Command.make(
|
|
|
2614
2722
|
requested: codexAuthFlag._tag === "Some" ? codexAuthFlag.value : void 0,
|
|
2615
2723
|
interactive: isInteractive,
|
|
2616
2724
|
select: async () => {
|
|
2617
|
-
const selected = await
|
|
2725
|
+
const selected = await clack2.select({
|
|
2618
2726
|
message: "How will you authenticate Codex?",
|
|
2619
2727
|
initialValue: "chatgpt",
|
|
2620
2728
|
options: [
|
|
@@ -2630,7 +2738,7 @@ var initCommand = Command.make(
|
|
|
2630
2738
|
}
|
|
2631
2739
|
]
|
|
2632
2740
|
});
|
|
2633
|
-
return
|
|
2741
|
+
return clack2.isCancel(selected) ? void 0 : selected;
|
|
2634
2742
|
}
|
|
2635
2743
|
}),
|
|
2636
2744
|
catch: (error) => error instanceof InitError ? error : new InitError({
|
|
@@ -2649,32 +2757,7 @@ var initCommand = Command.make(
|
|
|
2649
2757
|
})
|
|
2650
2758
|
});
|
|
2651
2759
|
}
|
|
2652
|
-
const
|
|
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
|
-
}
|
|
2760
|
+
const selectedSandboxProvider = getSandboxProvider("docker");
|
|
2678
2761
|
const issueTrackers = listIssueTrackers();
|
|
2679
2762
|
let selectedIssueTracker;
|
|
2680
2763
|
if (issueTrackerFlag._tag === "Some") {
|
|
@@ -2684,7 +2767,7 @@ var initCommand = Command.make(
|
|
|
2684
2767
|
yield* failIfNonInteractive("--issue-tracker");
|
|
2685
2768
|
}
|
|
2686
2769
|
const selected = yield* Effect.promise(
|
|
2687
|
-
() =>
|
|
2770
|
+
() => clack2.select({
|
|
2688
2771
|
message: "Select an issue tracker:",
|
|
2689
2772
|
initialValue: "github-issues",
|
|
2690
2773
|
options: issueTrackers.map((b) => ({
|
|
@@ -2693,7 +2776,7 @@ var initCommand = Command.make(
|
|
|
2693
2776
|
}))
|
|
2694
2777
|
})
|
|
2695
2778
|
);
|
|
2696
|
-
if (
|
|
2779
|
+
if (clack2.isCancel(selected)) {
|
|
2697
2780
|
yield* Effect.fail(
|
|
2698
2781
|
new InitError({
|
|
2699
2782
|
message: "Issue tracker selection cancelled."
|
|
@@ -2710,9 +2793,9 @@ var initCommand = Command.make(
|
|
|
2710
2793
|
yield* failIfNonInteractive("--template");
|
|
2711
2794
|
}
|
|
2712
2795
|
const selected = yield* Effect.promise(
|
|
2713
|
-
() =>
|
|
2796
|
+
() => clack2.select({
|
|
2714
2797
|
message: "Select a template:",
|
|
2715
|
-
initialValue: "
|
|
2798
|
+
initialValue: "simple-loop",
|
|
2716
2799
|
options: templates.map((tmpl) => ({
|
|
2717
2800
|
value: tmpl.name,
|
|
2718
2801
|
label: tmpl.name,
|
|
@@ -2720,7 +2803,7 @@ var initCommand = Command.make(
|
|
|
2720
2803
|
}))
|
|
2721
2804
|
})
|
|
2722
2805
|
);
|
|
2723
|
-
if (
|
|
2806
|
+
if (clack2.isCancel(selected)) {
|
|
2724
2807
|
yield* Effect.fail(
|
|
2725
2808
|
new InitError({ message: "Template selection cancelled." })
|
|
2726
2809
|
);
|
|
@@ -2772,15 +2855,17 @@ var initCommand = Command.make(
|
|
|
2772
2855
|
console.warn(message);
|
|
2773
2856
|
}
|
|
2774
2857
|
}
|
|
2775
|
-
yield* d.
|
|
2858
|
+
yield* d.progress(
|
|
2776
2859
|
`Scaffolding ${CONFIG_DIR}/ config directory...`,
|
|
2777
|
-
scaffold(cwd, {
|
|
2860
|
+
(report) => scaffold(cwd, {
|
|
2778
2861
|
agent: selectedAgent,
|
|
2779
2862
|
model: selectedModel,
|
|
2863
|
+
modelExplicit: modelFlag._tag === "Some",
|
|
2780
2864
|
templateName: selectedTemplate,
|
|
2781
2865
|
issueTracker: selectedIssueTracker,
|
|
2782
2866
|
sandboxProvider: selectedSandboxProvider,
|
|
2783
|
-
codexAuth: selectedCodexAuth
|
|
2867
|
+
codexAuth: selectedCodexAuth,
|
|
2868
|
+
onProgress: report
|
|
2784
2869
|
}).pipe(
|
|
2785
2870
|
Effect.mapError(
|
|
2786
2871
|
(e) => new InitError({
|
|
@@ -2843,28 +2928,39 @@ var initCommand = Command.make(
|
|
|
2843
2928
|
interactive: isInteractive,
|
|
2844
2929
|
requested: installRunnerChoice._tag === "Some" ? installRunnerChoice.value : void 0,
|
|
2845
2930
|
confirm: async ({ message, initialValue }) => {
|
|
2846
|
-
const confirmed = await
|
|
2847
|
-
if (
|
|
2931
|
+
const confirmed = await clack2.confirm({ message, initialValue });
|
|
2932
|
+
if (clack2.isCancel(confirmed)) {
|
|
2848
2933
|
throw new InitError({
|
|
2849
2934
|
message: "Repository-runner installation selection cancelled."
|
|
2850
2935
|
});
|
|
2851
2936
|
}
|
|
2852
2937
|
return confirmed === true;
|
|
2853
2938
|
},
|
|
2854
|
-
install: () =>
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2939
|
+
install: () => installRepositoryRunnerWithReplacement({
|
|
2940
|
+
repoDir: cwd,
|
|
2941
|
+
interactive: isInteractive,
|
|
2942
|
+
confirmReplacement: async (conflict) => {
|
|
2943
|
+
const confirmed = await clack2.confirm({
|
|
2944
|
+
message: conflict.confirmationMessage,
|
|
2945
|
+
initialValue: false
|
|
2946
|
+
});
|
|
2947
|
+
return !clack2.isCancel(confirmed) && confirmed === true;
|
|
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
|
+
})
|
|
2864
2960
|
})
|
|
2865
|
-
|
|
2961
|
+
)
|
|
2866
2962
|
)
|
|
2867
|
-
)
|
|
2963
|
+
})
|
|
2868
2964
|
}),
|
|
2869
2965
|
catch: (error) => error instanceof InitError ? error : new InitError({
|
|
2870
2966
|
message: `Repository-runner setup failed: ${error instanceof Error ? error.message : String(error)}`
|