runcastle 0.2.0 → 1.0.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/README.md +47 -0
- package/bin/runcastle.js +5609 -0
- package/drizzle/0000_amused_bruce_banner.sql +74 -0
- package/drizzle/0001_mapped_feature_flag.sql +1 -0
- package/drizzle/0002_waypoints.sql +14 -0
- package/drizzle/0003_project_closed_at.sql +1 -0
- package/drizzle/0004_events_project_id.sql +28 -0
- package/drizzle/0005_project_setting_overrides.sql +2 -0
- package/drizzle/meta/0000_snapshot.json +466 -0
- package/drizzle/meta/0001_snapshot.json +474 -0
- package/drizzle/meta/0002_snapshot.json +568 -0
- package/drizzle/meta/0003_snapshot.json +575 -0
- package/drizzle/meta/0004_snapshot.json +582 -0
- package/drizzle/meta/0005_snapshot.json +596 -0
- package/drizzle/meta/_journal.json +48 -0
- package/hook-client.ts +67 -0
- package/index.js +5453 -0
- package/package.json +24 -30
- package/pty-host.cjs +186 -0
- package/sandcastle-template/Containerfile +33 -0
- package/sandcastle-template/Dockerfile +33 -0
- package/skills/burner/implement-ticket.md +42 -0
- package/skills/burner/research-waypoint.md +45 -0
- package/skills/packs/README.md +50 -0
- package/skills/packs/runcastle/.claude-plugin/plugin.json +6 -0
- package/skills/packs/runcastle/skills/converge/SKILL.md +51 -0
- package/skills/packs/runcastle/skills/ideate/SKILL.md +81 -0
- package/skills/packs/runcastle/skills/qa/SKILL.md +30 -0
- package/skills/packs/runcastle/skills/spec/SKILL.md +43 -0
- package/skills/packs/runcastle/skills/tickets/SKILL.md +52 -0
- package/skills/packs/runcastle/skills/waypoint/SKILL.md +54 -0
- package/web/assets/index-CKZ5Z7vc.css +1 -0
- package/web/assets/index-YBuT7tTz.js +108 -0
- package/web/index.html +13 -0
- package/dist/add-DD7G73HH.js +0 -315
- package/dist/add-DD7G73HH.js.map +0 -1
- package/dist/chunk-44KDPUYJ.js +0 -49
- package/dist/chunk-44KDPUYJ.js.map +0 -1
- package/dist/chunk-5VPKMXSV.js +0 -190
- package/dist/chunk-5VPKMXSV.js.map +0 -1
- package/dist/chunk-6CWCR5SO.js +0 -24
- package/dist/chunk-6CWCR5SO.js.map +0 -1
- package/dist/chunk-7CHGGMP7.js +0 -73
- package/dist/chunk-7CHGGMP7.js.map +0 -1
- package/dist/chunk-AHRTPV2C.js +0 -1082
- package/dist/chunk-AHRTPV2C.js.map +0 -1
- package/dist/chunk-GU4LRYYH.js +0 -41
- package/dist/chunk-GU4LRYYH.js.map +0 -1
- package/dist/chunk-LP5MV76P.js +0 -33
- package/dist/chunk-LP5MV76P.js.map +0 -1
- package/dist/chunk-OJ356IMJ.js +0 -136
- package/dist/chunk-OJ356IMJ.js.map +0 -1
- package/dist/chunk-PRNBOQHW.js +0 -32
- package/dist/chunk-PRNBOQHW.js.map +0 -1
- package/dist/chunk-QTC5Y3LU.js +0 -146
- package/dist/chunk-QTC5Y3LU.js.map +0 -1
- package/dist/chunk-SNUH2UT6.js +0 -95
- package/dist/chunk-SNUH2UT6.js.map +0 -1
- package/dist/chunk-UG25BZUN.js +0 -59
- package/dist/chunk-UG25BZUN.js.map +0 -1
- package/dist/chunk-VWAIXSB6.js +0 -78
- package/dist/chunk-VWAIXSB6.js.map +0 -1
- package/dist/index.js +0 -99
- package/dist/index.js.map +0 -1
- package/dist/info-3PW6A6JA.js +0 -80
- package/dist/info-3PW6A6JA.js.map +0 -1
- package/dist/init-MOJNLWXK.js +0 -212
- package/dist/init-MOJNLWXK.js.map +0 -1
- package/dist/logout-YPGZ6BB6.js +0 -27
- package/dist/logout-YPGZ6BB6.js.map +0 -1
- package/dist/publish-W37Y6TO6.js +0 -211
- package/dist/publish-W37Y6TO6.js.map +0 -1
- package/dist/run-M4XXNPGD.js +0 -291
- package/dist/run-M4XXNPGD.js.map +0 -1
- package/dist/search-2KNAXPM5.js +0 -55
- package/dist/search-2KNAXPM5.js.map +0 -1
- package/dist/update-UCT4K6IQ.js +0 -125
- package/dist/update-UCT4K6IQ.js.map +0 -1
- package/dist/validate-GFDSEVPV.js +0 -12
- package/dist/validate-GFDSEVPV.js.map +0 -1
package/dist/run-M4XXNPGD.js
DELETED
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
readLockfile
|
|
3
|
-
} from "./chunk-GU4LRYYH.js";
|
|
4
|
-
import {
|
|
5
|
-
detectSandcastle
|
|
6
|
-
} from "./chunk-UG25BZUN.js";
|
|
7
|
-
import "./chunk-LP5MV76P.js";
|
|
8
|
-
import {
|
|
9
|
-
PAYLOAD_DIR,
|
|
10
|
-
SANDCASTLE_PACKAGE_NAME
|
|
11
|
-
} from "./chunk-AHRTPV2C.js";
|
|
12
|
-
import {
|
|
13
|
-
CliError
|
|
14
|
-
} from "./chunk-6CWCR5SO.js";
|
|
15
|
-
import {
|
|
16
|
-
isJsonMode,
|
|
17
|
-
log,
|
|
18
|
-
pc
|
|
19
|
-
} from "./chunk-PRNBOQHW.js";
|
|
20
|
-
|
|
21
|
-
// src/commands/run.ts
|
|
22
|
-
import fs from "fs";
|
|
23
|
-
import fsp from "fs/promises";
|
|
24
|
-
import path from "path";
|
|
25
|
-
import { spawn } from "child_process";
|
|
26
|
-
function winQuote(token) {
|
|
27
|
-
return /[ \t"^&|<>()%!]/.test(token) ? `"${token.replace(/"/g, '""')}"` : token;
|
|
28
|
-
}
|
|
29
|
-
var defaultExec = (cmd, args, opts) => new Promise((resolve, reject) => {
|
|
30
|
-
const child = process.platform === "win32" ? spawn([cmd, ...args].map(winQuote).join(" "), {
|
|
31
|
-
cwd: opts.cwd,
|
|
32
|
-
stdio: opts.inherit ? "inherit" : "ignore",
|
|
33
|
-
shell: true
|
|
34
|
-
}) : spawn(cmd, args, {
|
|
35
|
-
cwd: opts.cwd,
|
|
36
|
-
stdio: opts.inherit ? "inherit" : "ignore"
|
|
37
|
-
});
|
|
38
|
-
const timer = opts.timeoutMs ? setTimeout(() => child.kill(), opts.timeoutMs) : void 0;
|
|
39
|
-
child.on("error", (e) => {
|
|
40
|
-
if (timer) clearTimeout(timer);
|
|
41
|
-
reject(e);
|
|
42
|
-
});
|
|
43
|
-
child.on("close", (code) => {
|
|
44
|
-
if (timer) clearTimeout(timer);
|
|
45
|
-
resolve({ code: code ?? 1 });
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
function defaultImageName(repoDir) {
|
|
49
|
-
const dirName = repoDir.replace(/[\\/]+$/, "").split(/[\\/]/).pop() ?? "local";
|
|
50
|
-
const sanitized = dirName.toLowerCase().replace(/[^a-z0-9_.-]/g, "-");
|
|
51
|
-
return `sandcastle:${sanitized || "local"}`;
|
|
52
|
-
}
|
|
53
|
-
function detectImageName(entrySource, repoDir) {
|
|
54
|
-
const m = /imageName\s*:\s*["'`]([^"'`]+)["'`]/.exec(entrySource);
|
|
55
|
-
if (m?.[1] && !m[1].includes("${")) return m[1];
|
|
56
|
-
return defaultImageName(repoDir);
|
|
57
|
-
}
|
|
58
|
-
function normalizeDirArg(dirArg) {
|
|
59
|
-
const clean = dirArg.replace(/\\/g, "/").replace(/\/+$/, "").replace(/^\.\//, "");
|
|
60
|
-
if (clean === PAYLOAD_DIR || clean.startsWith(`${PAYLOAD_DIR}/`)) return clean;
|
|
61
|
-
return `${PAYLOAD_DIR}/${clean}`;
|
|
62
|
-
}
|
|
63
|
-
async function findEntrypoint(cwd, dir, lock) {
|
|
64
|
-
const candidates = lock?.entrypoint ? [lock.entrypoint] : [`${dir}/main.ts`, `${dir}/main.mts`];
|
|
65
|
-
for (const rel of candidates) {
|
|
66
|
-
if (fs.existsSync(path.join(cwd, ...rel.split("/")))) return rel;
|
|
67
|
-
}
|
|
68
|
-
throw new CliError(`no entrypoint found in ${dir}/ (looked for ${candidates.join(", ")})`, {
|
|
69
|
-
code: "E_NO_ENTRYPOINT",
|
|
70
|
-
details: [`install a workflow first: npx runcastle add <author/slug>`]
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
async function resolveRunTarget(cwd, dirArg, interactive) {
|
|
74
|
-
const lockfile = await readLockfile(cwd);
|
|
75
|
-
if (dirArg) {
|
|
76
|
-
const dir2 = normalizeDirArg(dirArg);
|
|
77
|
-
const lock2 = lockfile.installs.find((i) => i.dir === dir2);
|
|
78
|
-
return { dir: dir2, entrypoint: await findEntrypoint(cwd, dir2, lock2), ...lock2 ? { lock: lock2 } : {} };
|
|
79
|
-
}
|
|
80
|
-
if (lockfile.installs.length > 1) {
|
|
81
|
-
if (!interactive) {
|
|
82
|
-
throw new CliError(`multiple workflows installed \u2014 pass one: runcastle run <dir>`, {
|
|
83
|
-
code: "E_AMBIGUOUS_TARGET",
|
|
84
|
-
details: lockfile.installs.map((i) => `runcastle run ${i.dir} ${pc.dim(`(${i.source})`)}`)
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
const clack = await import("@clack/prompts");
|
|
88
|
-
const choice = await clack.select({
|
|
89
|
-
message: "Which workflow?",
|
|
90
|
-
options: lockfile.installs.map((i) => ({ value: i.dir, label: `${i.source} ${pc.dim(i.dir)}` }))
|
|
91
|
-
});
|
|
92
|
-
if (clack.isCancel(choice)) throw new CliError("", { exitCode: 1, code: "E_CANCELLED" });
|
|
93
|
-
const lock2 = lockfile.installs.find((i) => i.dir === choice);
|
|
94
|
-
return { dir: lock2.dir, entrypoint: await findEntrypoint(cwd, lock2.dir, lock2), lock: lock2 };
|
|
95
|
-
}
|
|
96
|
-
const lock = lockfile.installs[0];
|
|
97
|
-
const dir = lock?.dir ?? PAYLOAD_DIR;
|
|
98
|
-
return { dir, entrypoint: await findEntrypoint(cwd, dir, lock), ...lock ? { lock } : {} };
|
|
99
|
-
}
|
|
100
|
-
function detectPackageManager(cwd) {
|
|
101
|
-
try {
|
|
102
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8"));
|
|
103
|
-
const pm = pkg.packageManager?.split("@")[0];
|
|
104
|
-
if (pm === "pnpm" || pm === "yarn" || pm === "bun" || pm === "npm") return pm;
|
|
105
|
-
} catch {
|
|
106
|
-
}
|
|
107
|
-
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
108
|
-
if (fs.existsSync(path.join(cwd, "yarn.lock"))) return "yarn";
|
|
109
|
-
if (fs.existsSync(path.join(cwd, "bun.lock")) || fs.existsSync(path.join(cwd, "bun.lockb"))) return "bun";
|
|
110
|
-
return "npm";
|
|
111
|
-
}
|
|
112
|
-
function installDevCommand(pm, pkgs) {
|
|
113
|
-
switch (pm) {
|
|
114
|
-
case "pnpm":
|
|
115
|
-
return { cmd: "pnpm", args: ["add", "-D", ...pkgs] };
|
|
116
|
-
case "yarn":
|
|
117
|
-
return { cmd: "yarn", args: ["add", "-D", ...pkgs] };
|
|
118
|
-
case "bun":
|
|
119
|
-
return { cmd: "bun", args: ["add", "-d", ...pkgs] };
|
|
120
|
-
default:
|
|
121
|
-
return { cmd: "npm", args: ["install", "-D", ...pkgs] };
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
async function ensureSandcastle(cwd, interactive, yes, exec) {
|
|
125
|
-
const detected = await detectSandcastle(cwd);
|
|
126
|
-
if (detected.version) {
|
|
127
|
-
log(`${pc.green("\u2713")} ${SANDCASTLE_PACKAGE_NAME} ${detected.version}`);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const missing = [SANDCASTLE_PACKAGE_NAME];
|
|
131
|
-
if (!fs.existsSync(path.join(cwd, "node_modules", "tsx"))) missing.push("tsx");
|
|
132
|
-
const pm = detectPackageManager(cwd);
|
|
133
|
-
const { cmd, args } = installDevCommand(pm, missing);
|
|
134
|
-
const cmdline = `${cmd} ${args.join(" ")}`;
|
|
135
|
-
let proceed = yes;
|
|
136
|
-
if (!proceed && interactive) {
|
|
137
|
-
const clack = await import("@clack/prompts");
|
|
138
|
-
const ok = await clack.confirm({
|
|
139
|
-
message: `${SANDCASTLE_PACKAGE_NAME} is not installed. Install it now? (${cmdline})`,
|
|
140
|
-
initialValue: true
|
|
141
|
-
});
|
|
142
|
-
proceed = !clack.isCancel(ok) && ok === true;
|
|
143
|
-
}
|
|
144
|
-
if (!proceed) {
|
|
145
|
-
throw new CliError(`${SANDCASTLE_PACKAGE_NAME} is not installed in this project`, {
|
|
146
|
-
code: "E_MISSING_RUNTIME",
|
|
147
|
-
details: [`install it, then re-run: ${cmdline}`]
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
log(`${pc.cyan("\u2192")} ${cmdline}`);
|
|
151
|
-
const res = await exec(cmd, args, { cwd, inherit: true });
|
|
152
|
-
if (res.code !== 0) {
|
|
153
|
-
throw new CliError(`"${cmdline}" failed (exit ${res.code})`, { code: "E_MISSING_RUNTIME" });
|
|
154
|
-
}
|
|
155
|
-
log(`${pc.green("\u2713")} ${SANDCASTLE_PACKAGE_NAME} installed`);
|
|
156
|
-
}
|
|
157
|
-
function detectProvider(cwd, dir, lock) {
|
|
158
|
-
const fromLock = lock?.sandboxProviders?.find((p) => p === "docker" || p === "podman");
|
|
159
|
-
if (fromLock) return fromLock;
|
|
160
|
-
if (fs.existsSync(path.join(cwd, ...dir.split("/"), "Containerfile"))) return "podman";
|
|
161
|
-
return "docker";
|
|
162
|
-
}
|
|
163
|
-
function localSandcastleBin(cwd) {
|
|
164
|
-
const bin = path.join(
|
|
165
|
-
cwd,
|
|
166
|
-
"node_modules",
|
|
167
|
-
".bin",
|
|
168
|
-
process.platform === "win32" ? "sandcastle.cmd" : "sandcastle"
|
|
169
|
-
);
|
|
170
|
-
return fs.existsSync(bin) ? bin : void 0;
|
|
171
|
-
}
|
|
172
|
-
async function ensureImage(cwd, entrypoint, provider, exec) {
|
|
173
|
-
try {
|
|
174
|
-
const info = await exec(provider, ["info"], { cwd, inherit: false, timeoutMs: 15e3 });
|
|
175
|
-
if (info.code !== 0) {
|
|
176
|
-
throw new CliError(`the ${provider} daemon is not responding`, {
|
|
177
|
-
code: "E_SANDBOX_DOWN",
|
|
178
|
-
details: [provider === "docker" ? "start Docker Desktop (or the docker service) and re-run" : "start the podman machine and re-run"]
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
} catch (e) {
|
|
182
|
-
if (e instanceof CliError) throw e;
|
|
183
|
-
throw new CliError(`${provider} is not installed or not on PATH`, {
|
|
184
|
-
code: "E_SANDBOX_MISSING",
|
|
185
|
-
details: [provider === "docker" ? "install Docker Desktop: https://www.docker.com/" : "install Podman: https://podman.io/"]
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
log(`${pc.green("\u2713")} ${provider} daemon`);
|
|
189
|
-
const entrySource = await fsp.readFile(path.join(cwd, ...entrypoint.split("/")), "utf8");
|
|
190
|
-
const image = detectImageName(entrySource, cwd);
|
|
191
|
-
const inspect = await exec(provider, ["image", "inspect", image], { cwd, inherit: false, timeoutMs: 15e3 });
|
|
192
|
-
if (inspect.code === 0) {
|
|
193
|
-
log(`${pc.green("\u2713")} image ${image}`);
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
log(`${pc.cyan("\u2192")} building image ${image} (first run)\u2026`);
|
|
197
|
-
const bin = localSandcastleBin(cwd);
|
|
198
|
-
const buildArgs = [provider, "build-image", "--image-name", image];
|
|
199
|
-
const res = bin ? await exec(bin, buildArgs, { cwd, inherit: true }) : await exec("npx", ["-y", SANDCASTLE_PACKAGE_NAME, ...buildArgs], { cwd, inherit: true });
|
|
200
|
-
if (res.code !== 0) {
|
|
201
|
-
throw new CliError(`image build failed (exit ${res.code})`, {
|
|
202
|
-
code: "E_IMAGE_BUILD",
|
|
203
|
-
details: [`retry manually: npx ${SANDCASTLE_PACKAGE_NAME} ${buildArgs.join(" ")}`]
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
log(`${pc.green("\u2713")} image ${image}`);
|
|
207
|
-
}
|
|
208
|
-
function parseEnvFile(text) {
|
|
209
|
-
const out = /* @__PURE__ */ new Map();
|
|
210
|
-
for (const raw of text.split(/\r?\n/)) {
|
|
211
|
-
const line = raw.trim();
|
|
212
|
-
if (!line || line.startsWith("#")) continue;
|
|
213
|
-
const eq = line.indexOf("=");
|
|
214
|
-
if (eq <= 0) continue;
|
|
215
|
-
const key = line.slice(0, eq).trim();
|
|
216
|
-
let val = line.slice(eq + 1).trim();
|
|
217
|
-
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
218
|
-
val = val.slice(1, -1);
|
|
219
|
-
}
|
|
220
|
-
out.set(key, val);
|
|
221
|
-
}
|
|
222
|
-
return out;
|
|
223
|
-
}
|
|
224
|
-
async function ensureEnv(cwd, dir, lock) {
|
|
225
|
-
const envPath = path.join(cwd, ...dir.split("/"), ".env");
|
|
226
|
-
const examplePath = path.join(cwd, ...dir.split("/"), ".env.example");
|
|
227
|
-
const exampleText = fs.existsSync(examplePath) ? await fsp.readFile(examplePath, "utf8") : "";
|
|
228
|
-
if (!fs.existsSync(envPath) && exampleText) {
|
|
229
|
-
await fsp.writeFile(envPath, exampleText, "utf8");
|
|
230
|
-
log(`${pc.cyan("\u2192")} created ${dir}/.env from .env.example`);
|
|
231
|
-
}
|
|
232
|
-
const required = lock?.requiredEnv ?? [...parseEnvFile(exampleText).keys()];
|
|
233
|
-
if (required.length === 0) return;
|
|
234
|
-
const envText = fs.existsSync(envPath) ? await fsp.readFile(envPath, "utf8") : "";
|
|
235
|
-
const envMap = parseEnvFile(envText);
|
|
236
|
-
const missing = required.filter((k) => !envMap.get(k) && !process.env[k]);
|
|
237
|
-
if (missing.length > 0) {
|
|
238
|
-
throw new CliError(`missing required env var(s): ${missing.join(", ")}`, {
|
|
239
|
-
code: "E_MISSING_ENV",
|
|
240
|
-
details: [
|
|
241
|
-
`edit ${dir}/.env and fill in the value(s)`,
|
|
242
|
-
...missing.includes("CLAUDE_CODE_OAUTH_TOKEN") ? ["generate a token with: claude setup-token"] : []
|
|
243
|
-
]
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
log(`${pc.green("\u2713")} env: ${required.join(", ")}`);
|
|
247
|
-
}
|
|
248
|
-
async function runCommand(dirArg, opts) {
|
|
249
|
-
if (isJsonMode()) {
|
|
250
|
-
throw new CliError("run executes a workflow interactively \u2014 --json is not supported", {
|
|
251
|
-
code: "E_UNSUPPORTED"
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
255
|
-
const exec = opts.execImpl ?? defaultExec;
|
|
256
|
-
const interactive = !opts.yes && process.stdin.isTTY === true;
|
|
257
|
-
const target = await resolveRunTarget(cwd, dirArg, interactive);
|
|
258
|
-
log(`${pc.bold("runcastle run")} ${pc.dim(target.lock ? target.lock.source : target.dir)}`);
|
|
259
|
-
await ensureSandcastle(cwd, interactive, Boolean(opts.yes), exec);
|
|
260
|
-
const provider = detectProvider(cwd, target.dir, target.lock);
|
|
261
|
-
await ensureImage(cwd, target.entrypoint, provider, exec);
|
|
262
|
-
await ensureEnv(cwd, target.dir, target.lock);
|
|
263
|
-
const tsxBin = path.join(
|
|
264
|
-
cwd,
|
|
265
|
-
"node_modules",
|
|
266
|
-
".bin",
|
|
267
|
-
process.platform === "win32" ? "tsx.cmd" : "tsx"
|
|
268
|
-
);
|
|
269
|
-
log("");
|
|
270
|
-
log(`${pc.cyan("\u25B6")} tsx ${target.entrypoint}`);
|
|
271
|
-
log("");
|
|
272
|
-
const res = fs.existsSync(tsxBin) ? await exec(tsxBin, [target.entrypoint], { cwd, inherit: true }) : await exec("npx", ["-y", "tsx", target.entrypoint], { cwd, inherit: true });
|
|
273
|
-
if (res.code !== 0) {
|
|
274
|
-
throw new CliError(`workflow exited with code ${res.code}`, {
|
|
275
|
-
exitCode: res.code,
|
|
276
|
-
code: "E_RUN_FAILED",
|
|
277
|
-
details: [`logs are under ${target.dir}/logs/`]
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
export {
|
|
282
|
-
defaultImageName,
|
|
283
|
-
detectImageName,
|
|
284
|
-
detectPackageManager,
|
|
285
|
-
detectProvider,
|
|
286
|
-
installDevCommand,
|
|
287
|
-
parseEnvFile,
|
|
288
|
-
resolveRunTarget,
|
|
289
|
-
runCommand
|
|
290
|
-
};
|
|
291
|
-
//# sourceMappingURL=run-M4XXNPGD.js.map
|
package/dist/run-M4XXNPGD.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/run.ts"],"sourcesContent":["// `runcastle run [dir]` — the one-command path from an installed workflow to a\n// running one. Runs an ordered preflight (sandcastle dep → sandbox daemon →\n// image → env), fixes what it safely can (build the image, copy .env.example),\n// prints ONE actionable line for what it can't, then execs the entrypoint via\n// tsx. Unlike `add` (which never executes anything, spec §8.1), `run` is the\n// user's explicit consent to execute the workflow they installed.\n\nimport fs from \"node:fs\";\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { spawn } from \"node:child_process\";\nimport { SANDCASTLE_PACKAGE_NAME } from \"@castellan/schema\";\nimport { PAYLOAD_DIR } from \"../lib/config.js\";\nimport { CliError } from \"../lib/errors.js\";\nimport { isJsonMode, log, pc } from \"../lib/output.js\";\nimport { detectSandcastle } from \"../lib/sandcastle-version.js\";\nimport { readLockfile, type LockEntry } from \"../lib/lockfile.js\";\n\nexport interface RunCommandOptions {\n dir?: string;\n yes?: boolean;\n /** Test seams. */\n cwd?: string;\n execImpl?: ExecImpl;\n}\n\n/** Spawn seam: returns the exit code (rejects only on spawn failure). */\nexport type ExecImpl = (\n cmd: string,\n args: string[],\n opts: { cwd: string; inherit: boolean; timeoutMs?: number },\n) => Promise<{ code: number }>;\n\n/** Quote a token for the Windows shell only when it needs it. */\nfunction winQuote(token: string): string {\n return /[ \\t\"^&|<>()%!]/.test(token) ? `\"${token.replace(/\"/g, '\"\"')}\"` : token;\n}\n\nconst defaultExec: ExecImpl = (cmd, args, opts) =>\n new Promise((resolve, reject) => {\n // .cmd shims (npx/npm/docker on some setups) need a shell on Windows, and\n // shell:true with an args ARRAY is deprecated (DEP0190) — so pass one\n // pre-quoted command string there, and a plain argv everywhere else.\n const child =\n process.platform === \"win32\"\n ? spawn([cmd, ...args].map(winQuote).join(\" \"), {\n cwd: opts.cwd,\n stdio: opts.inherit ? \"inherit\" : \"ignore\",\n shell: true,\n })\n : spawn(cmd, args, {\n cwd: opts.cwd,\n stdio: opts.inherit ? \"inherit\" : \"ignore\",\n });\n const timer = opts.timeoutMs\n ? setTimeout(() => child.kill(), opts.timeoutMs)\n : undefined;\n child.on(\"error\", (e) => {\n if (timer) clearTimeout(timer);\n reject(e);\n });\n child.on(\"close\", (code) => {\n if (timer) clearTimeout(timer);\n resolve({ code: code ?? 1 });\n });\n });\n\n// --- Image name (mirrors @ai-hero/sandcastle's defaultImageName exactly) ------\n\nexport function defaultImageName(repoDir: string): string {\n const dirName = repoDir.replace(/[\\\\/]+$/, \"\").split(/[\\\\/]/).pop() ?? \"local\";\n const sanitized = dirName.toLowerCase().replace(/[^a-z0-9_.-]/g, \"-\");\n return `sandcastle:${sanitized || \"local\"}`;\n}\n\n/** A hardcoded `imageName: \"…\"` in the entrypoint wins over the default. */\nexport function detectImageName(entrySource: string, repoDir: string): string {\n const m = /imageName\\s*:\\s*[\"'`]([^\"'`]+)[\"'`]/.exec(entrySource);\n if (m?.[1] && !m[1].includes(\"${\")) return m[1];\n return defaultImageName(repoDir);\n}\n\n// --- Target resolution ---------------------------------------------------------\n\ninterface RunTarget {\n /** Install dir relative to project root (e.g. \".sandcastle\"). */\n dir: string;\n /** Entrypoint path relative to project root. */\n entrypoint: string;\n lock?: LockEntry;\n}\n\nfunction normalizeDirArg(dirArg: string): string {\n const clean = dirArg.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\").replace(/^\\.\\//, \"\");\n if (clean === PAYLOAD_DIR || clean.startsWith(`${PAYLOAD_DIR}/`)) return clean;\n return `${PAYLOAD_DIR}/${clean}`;\n}\n\nasync function findEntrypoint(cwd: string, dir: string, lock?: LockEntry): Promise<string> {\n const candidates = lock?.entrypoint\n ? [lock.entrypoint]\n : [`${dir}/main.ts`, `${dir}/main.mts`];\n for (const rel of candidates) {\n if (fs.existsSync(path.join(cwd, ...rel.split(\"/\")))) return rel;\n }\n throw new CliError(`no entrypoint found in ${dir}/ (looked for ${candidates.join(\", \")})`, {\n code: \"E_NO_ENTRYPOINT\",\n details: [`install a workflow first: npx runcastle add <author/slug>`],\n });\n}\n\nexport async function resolveRunTarget(\n cwd: string,\n dirArg: string | undefined,\n interactive: boolean,\n): Promise<RunTarget> {\n const lockfile = await readLockfile(cwd);\n\n if (dirArg) {\n const dir = normalizeDirArg(dirArg);\n const lock = lockfile.installs.find((i) => i.dir === dir);\n return { dir, entrypoint: await findEntrypoint(cwd, dir, lock), ...(lock ? { lock } : {}) };\n }\n\n if (lockfile.installs.length > 1) {\n if (!interactive) {\n throw new CliError(`multiple workflows installed — pass one: runcastle run <dir>`, {\n code: \"E_AMBIGUOUS_TARGET\",\n details: lockfile.installs.map((i) => `runcastle run ${i.dir} ${pc.dim(`(${i.source})`)}`),\n });\n }\n const clack = await import(\"@clack/prompts\");\n const choice = await clack.select({\n message: \"Which workflow?\",\n options: lockfile.installs.map((i) => ({ value: i.dir, label: `${i.source} ${pc.dim(i.dir)}` })),\n });\n if (clack.isCancel(choice)) throw new CliError(\"\", { exitCode: 1, code: \"E_CANCELLED\" });\n const lock = lockfile.installs.find((i) => i.dir === choice)!;\n return { dir: lock.dir, entrypoint: await findEntrypoint(cwd, lock.dir, lock), lock };\n }\n\n const lock = lockfile.installs[0];\n const dir = lock?.dir ?? PAYLOAD_DIR;\n return { dir, entrypoint: await findEntrypoint(cwd, dir, lock), ...(lock ? { lock } : {}) };\n}\n\n// --- Preflight: sandcastle dep ---------------------------------------------------\n\ntype PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nexport function detectPackageManager(cwd: string): PackageManager {\n try {\n const pkg = JSON.parse(fs.readFileSync(path.join(cwd, \"package.json\"), \"utf8\")) as {\n packageManager?: string;\n };\n const pm = pkg.packageManager?.split(\"@\")[0];\n if (pm === \"pnpm\" || pm === \"yarn\" || pm === \"bun\" || pm === \"npm\") return pm;\n } catch {\n /* fall through to lockfile sniffing */\n }\n if (fs.existsSync(path.join(cwd, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (fs.existsSync(path.join(cwd, \"yarn.lock\"))) return \"yarn\";\n if (fs.existsSync(path.join(cwd, \"bun.lock\")) || fs.existsSync(path.join(cwd, \"bun.lockb\"))) return \"bun\";\n return \"npm\";\n}\n\nexport function installDevCommand(pm: PackageManager, pkgs: string[]): { cmd: string; args: string[] } {\n switch (pm) {\n case \"pnpm\":\n return { cmd: \"pnpm\", args: [\"add\", \"-D\", ...pkgs] };\n case \"yarn\":\n return { cmd: \"yarn\", args: [\"add\", \"-D\", ...pkgs] };\n case \"bun\":\n return { cmd: \"bun\", args: [\"add\", \"-d\", ...pkgs] };\n default:\n return { cmd: \"npm\", args: [\"install\", \"-D\", ...pkgs] };\n }\n}\n\nasync function ensureSandcastle(\n cwd: string,\n interactive: boolean,\n yes: boolean,\n exec: ExecImpl,\n): Promise<void> {\n const detected = await detectSandcastle(cwd);\n if (detected.version) {\n log(`${pc.green(\"✓\")} ${SANDCASTLE_PACKAGE_NAME} ${detected.version}`);\n return;\n }\n\n const missing = [SANDCASTLE_PACKAGE_NAME];\n if (!fs.existsSync(path.join(cwd, \"node_modules\", \"tsx\"))) missing.push(\"tsx\");\n const pm = detectPackageManager(cwd);\n const { cmd, args } = installDevCommand(pm, missing);\n const cmdline = `${cmd} ${args.join(\" \")}`;\n\n let proceed = yes;\n if (!proceed && interactive) {\n const clack = await import(\"@clack/prompts\");\n const ok = await clack.confirm({\n message: `${SANDCASTLE_PACKAGE_NAME} is not installed. Install it now? (${cmdline})`,\n initialValue: true,\n });\n proceed = !clack.isCancel(ok) && ok === true;\n }\n if (!proceed) {\n throw new CliError(`${SANDCASTLE_PACKAGE_NAME} is not installed in this project`, {\n code: \"E_MISSING_RUNTIME\",\n details: [`install it, then re-run: ${cmdline}`],\n });\n }\n\n log(`${pc.cyan(\"→\")} ${cmdline}`);\n const res = await exec(cmd, args, { cwd, inherit: true });\n if (res.code !== 0) {\n throw new CliError(`\"${cmdline}\" failed (exit ${res.code})`, { code: \"E_MISSING_RUNTIME\" });\n }\n log(`${pc.green(\"✓\")} ${SANDCASTLE_PACKAGE_NAME} installed`);\n}\n\n// --- Preflight: sandbox provider + image ---------------------------------------\n\ntype Provider = \"docker\" | \"podman\";\n\nexport function detectProvider(cwd: string, dir: string, lock?: LockEntry): Provider {\n const fromLock = lock?.sandboxProviders?.find((p) => p === \"docker\" || p === \"podman\");\n if (fromLock) return fromLock as Provider;\n if (fs.existsSync(path.join(cwd, ...dir.split(\"/\"), \"Containerfile\"))) return \"podman\";\n return \"docker\";\n}\n\n/** Path to the locally installed sandcastle bin, or undefined. */\nfunction localSandcastleBin(cwd: string): string | undefined {\n const bin = path.join(\n cwd,\n \"node_modules\",\n \".bin\",\n process.platform === \"win32\" ? \"sandcastle.cmd\" : \"sandcastle\",\n );\n return fs.existsSync(bin) ? bin : undefined;\n}\n\nasync function ensureImage(\n cwd: string,\n entrypoint: string,\n provider: Provider,\n exec: ExecImpl,\n): Promise<void> {\n // Daemon reachable?\n try {\n const info = await exec(provider, [\"info\"], { cwd, inherit: false, timeoutMs: 15_000 });\n if (info.code !== 0) {\n throw new CliError(`the ${provider} daemon is not responding`, {\n code: \"E_SANDBOX_DOWN\",\n details: [provider === \"docker\" ? \"start Docker Desktop (or the docker service) and re-run\" : \"start the podman machine and re-run\"],\n });\n }\n } catch (e) {\n if (e instanceof CliError) throw e;\n throw new CliError(`${provider} is not installed or not on PATH`, {\n code: \"E_SANDBOX_MISSING\",\n details: [provider === \"docker\" ? \"install Docker Desktop: https://www.docker.com/\" : \"install Podman: https://podman.io/\"],\n });\n }\n log(`${pc.green(\"✓\")} ${provider} daemon`);\n\n const entrySource = await fsp.readFile(path.join(cwd, ...entrypoint.split(\"/\")), \"utf8\");\n const image = detectImageName(entrySource, cwd);\n const inspect = await exec(provider, [\"image\", \"inspect\", image], { cwd, inherit: false, timeoutMs: 15_000 });\n if (inspect.code === 0) {\n log(`${pc.green(\"✓\")} image ${image}`);\n return;\n }\n\n log(`${pc.cyan(\"→\")} building image ${image} (first run)…`);\n const bin = localSandcastleBin(cwd);\n const buildArgs = [provider, \"build-image\", \"--image-name\", image];\n const res = bin\n ? await exec(bin, buildArgs, { cwd, inherit: true })\n : await exec(\"npx\", [\"-y\", SANDCASTLE_PACKAGE_NAME, ...buildArgs], { cwd, inherit: true });\n if (res.code !== 0) {\n throw new CliError(`image build failed (exit ${res.code})`, {\n code: \"E_IMAGE_BUILD\",\n details: [`retry manually: npx ${SANDCASTLE_PACKAGE_NAME} ${buildArgs.join(\" \")}`],\n });\n }\n log(`${pc.green(\"✓\")} image ${image}`);\n}\n\n// --- Preflight: env --------------------------------------------------------------\n\n/** Parse KEY=VALUE lines (dotenv subset — enough for our .env.example format). */\nexport function parseEnvFile(text: string): Map<string, string> {\n const out = new Map<string, string>();\n for (const raw of text.split(/\\r?\\n/)) {\n const line = raw.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const eq = line.indexOf(\"=\");\n if (eq <= 0) continue;\n const key = line.slice(0, eq).trim();\n let val = line.slice(eq + 1).trim();\n if ((val.startsWith('\"') && val.endsWith('\"')) || (val.startsWith(\"'\") && val.endsWith(\"'\"))) {\n val = val.slice(1, -1);\n }\n out.set(key, val);\n }\n return out;\n}\n\nasync function ensureEnv(cwd: string, dir: string, lock?: LockEntry): Promise<void> {\n const envPath = path.join(cwd, ...dir.split(\"/\"), \".env\");\n const examplePath = path.join(cwd, ...dir.split(\"/\"), \".env.example\");\n\n const exampleText = fs.existsSync(examplePath) ? await fsp.readFile(examplePath, \"utf8\") : \"\";\n if (!fs.existsSync(envPath) && exampleText) {\n await fsp.writeFile(envPath, exampleText, \"utf8\");\n log(`${pc.cyan(\"→\")} created ${dir}/.env from .env.example`);\n }\n\n // Required keys: what the author declared at publish time, else every key in\n // .env.example. A key is satisfied by a non-empty .env value or process.env.\n const required = lock?.requiredEnv ?? [...parseEnvFile(exampleText).keys()];\n if (required.length === 0) return;\n\n const envText = fs.existsSync(envPath) ? await fsp.readFile(envPath, \"utf8\") : \"\";\n const envMap = parseEnvFile(envText);\n const missing = required.filter((k) => !envMap.get(k) && !process.env[k]);\n if (missing.length > 0) {\n throw new CliError(`missing required env var(s): ${missing.join(\", \")}`, {\n code: \"E_MISSING_ENV\",\n details: [\n `edit ${dir}/.env and fill in the value(s)`,\n ...(missing.includes(\"CLAUDE_CODE_OAUTH_TOKEN\")\n ? [\"generate a token with: claude setup-token\"]\n : []),\n ],\n });\n }\n log(`${pc.green(\"✓\")} env: ${required.join(\", \")}`);\n}\n\n// --- Command ---------------------------------------------------------------------\n\nexport async function runCommand(dirArg: string | undefined, opts: RunCommandOptions): Promise<void> {\n if (isJsonMode()) {\n throw new CliError(\"run executes a workflow interactively — --json is not supported\", {\n code: \"E_UNSUPPORTED\",\n });\n }\n const cwd = opts.cwd ?? process.cwd();\n const exec = opts.execImpl ?? defaultExec;\n const interactive = !opts.yes && process.stdin.isTTY === true;\n\n const target = await resolveRunTarget(cwd, dirArg, interactive);\n log(`${pc.bold(\"runcastle run\")} ${pc.dim(target.lock ? target.lock.source : target.dir)}`);\n\n await ensureSandcastle(cwd, interactive, Boolean(opts.yes), exec);\n const provider = detectProvider(cwd, target.dir, target.lock);\n await ensureImage(cwd, target.entrypoint, provider, exec);\n await ensureEnv(cwd, target.dir, target.lock);\n\n const tsxBin = path.join(\n cwd,\n \"node_modules\",\n \".bin\",\n process.platform === \"win32\" ? \"tsx.cmd\" : \"tsx\",\n );\n log(\"\");\n log(`${pc.cyan(\"▶\")} tsx ${target.entrypoint}`);\n log(\"\");\n const res = fs.existsSync(tsxBin)\n ? await exec(tsxBin, [target.entrypoint], { cwd, inherit: true })\n : await exec(\"npx\", [\"-y\", \"tsx\", target.entrypoint], { cwd, inherit: true });\n if (res.code !== 0) {\n throw new CliError(`workflow exited with code ${res.code}`, {\n exitCode: res.code,\n code: \"E_RUN_FAILED\",\n details: [`logs are under ${target.dir}/logs/`],\n });\n }\n}\n"],"mappings":"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","names":["dir","lock"]}
|
package/dist/search-2KNAXPM5.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
RegistryClient
|
|
3
|
-
} from "./chunk-5VPKMXSV.js";
|
|
4
|
-
import {
|
|
5
|
-
resolveRegistry
|
|
6
|
-
} from "./chunk-LP5MV76P.js";
|
|
7
|
-
import "./chunk-AHRTPV2C.js";
|
|
8
|
-
import "./chunk-6CWCR5SO.js";
|
|
9
|
-
import {
|
|
10
|
-
emitJson,
|
|
11
|
-
isJsonMode,
|
|
12
|
-
log,
|
|
13
|
-
pc
|
|
14
|
-
} from "./chunk-PRNBOQHW.js";
|
|
15
|
-
|
|
16
|
-
// src/commands/search.ts
|
|
17
|
-
function truncate(s, n) {
|
|
18
|
-
return s.length <= n ? s : `${s.slice(0, n - 1)}\u2026`;
|
|
19
|
-
}
|
|
20
|
-
async function searchCommand(query, opts) {
|
|
21
|
-
const registry = new RegistryClient({
|
|
22
|
-
baseUrl: resolveRegistry(opts.registry),
|
|
23
|
-
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
24
|
-
});
|
|
25
|
-
const res = await registry.search(query ?? "", opts.sort ? { sort: opts.sort } : {});
|
|
26
|
-
if (isJsonMode()) {
|
|
27
|
-
emitJson(res);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
if (res.items.length === 0) {
|
|
31
|
-
log(pc.dim(`No workflows found${query ? ` for "${query}"` : ""}.`));
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
const rows = res.items.map((w) => ({
|
|
35
|
-
ref: `${w.author}/${w.slug}`,
|
|
36
|
-
desc: truncate(w.description, 50),
|
|
37
|
-
agents: w.agents.map((a) => a.provider).join(",") || "-",
|
|
38
|
-
downloads: String(w.downloads)
|
|
39
|
-
}));
|
|
40
|
-
const refW = Math.max(9, ...rows.map((r) => r.ref.length));
|
|
41
|
-
const agW = Math.max(6, ...rows.map((r) => r.agents.length));
|
|
42
|
-
log(`${pc.bold(pad("WORKFLOW", refW))} ${pc.bold(pad("AGENTS", agW))} ${pc.bold("DL")} DESCRIPTION`);
|
|
43
|
-
for (const r of rows) {
|
|
44
|
-
log(`${pad(r.ref, refW)} ${pc.dim(pad(r.agents, agW))} ${pad(r.downloads, 4)} ${pc.dim(r.desc)}`);
|
|
45
|
-
}
|
|
46
|
-
log("");
|
|
47
|
-
log(pc.dim(`${res.total} result(s) \xB7 page ${res.page}`));
|
|
48
|
-
}
|
|
49
|
-
function pad(s, w) {
|
|
50
|
-
return s.length >= w ? s : s + " ".repeat(w - s.length);
|
|
51
|
-
}
|
|
52
|
-
export {
|
|
53
|
-
searchCommand
|
|
54
|
-
};
|
|
55
|
-
//# sourceMappingURL=search-2KNAXPM5.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/search.ts"],"sourcesContent":["// `runcastle search <query>` (spec §5.5). Thin wrapper over GET /api/workflows.\n// Prints a table (slug, description, agents, downloads); `--json` emits the raw\n// BrowseResponse.\n\nimport { emitJson, isJsonMode, log, pc } from \"../lib/output.js\";\nimport { resolveRegistry } from \"../lib/config.js\";\nimport { RegistryClient } from \"../lib/registry.js\";\nimport type { FetchLike } from \"../lib/http.js\";\n\nexport interface SearchOptions {\n registry?: string;\n json?: boolean;\n sort?: string;\n fetchImpl?: FetchLike;\n}\n\nfunction truncate(s: string, n: number): string {\n return s.length <= n ? s : `${s.slice(0, n - 1)}…`;\n}\n\nexport async function searchCommand(query: string, opts: SearchOptions): Promise<void> {\n const registry = new RegistryClient({\n baseUrl: resolveRegistry(opts.registry),\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n const res = await registry.search(query ?? \"\", opts.sort ? { sort: opts.sort } : {});\n\n if (isJsonMode()) {\n emitJson(res);\n return;\n }\n\n if (res.items.length === 0) {\n log(pc.dim(`No workflows found${query ? ` for \"${query}\"` : \"\"}.`));\n return;\n }\n\n const rows = res.items.map((w) => ({\n ref: `${w.author}/${w.slug}`,\n desc: truncate(w.description, 50),\n agents: w.agents.map((a) => a.provider).join(\",\") || \"-\",\n downloads: String(w.downloads),\n }));\n const refW = Math.max(9, ...rows.map((r) => r.ref.length));\n const agW = Math.max(6, ...rows.map((r) => r.agents.length));\n\n log(`${pc.bold(pad(\"WORKFLOW\", refW))} ${pc.bold(pad(\"AGENTS\", agW))} ${pc.bold(\"DL\")} DESCRIPTION`);\n for (const r of rows) {\n log(`${pad(r.ref, refW)} ${pc.dim(pad(r.agents, agW))} ${pad(r.downloads, 4)} ${pc.dim(r.desc)}`);\n }\n log(\"\");\n log(pc.dim(`${res.total} result(s) · page ${res.page}`));\n}\n\nfunction pad(s: string, w: number): string {\n return s.length >= w ? s : s + \" \".repeat(w - s.length);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,SAAS,SAAS,GAAW,GAAmB;AAC9C,SAAO,EAAE,UAAU,IAAI,IAAI,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;AACjD;AAEA,eAAsB,cAAc,OAAe,MAAoC;AACrF,QAAM,WAAW,IAAI,eAAe;AAAA,IAClC,SAAS,gBAAgB,KAAK,QAAQ;AAAA,IACtC,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,EACxD,CAAC;AACD,QAAM,MAAM,MAAM,SAAS,OAAO,SAAS,IAAI,KAAK,OAAO,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC,CAAC;AAEnF,MAAI,WAAW,GAAG;AAChB,aAAS,GAAG;AACZ;AAAA,EACF;AAEA,MAAI,IAAI,MAAM,WAAW,GAAG;AAC1B,QAAI,GAAG,IAAI,qBAAqB,QAAQ,SAAS,KAAK,MAAM,EAAE,GAAG,CAAC;AAClE;AAAA,EACF;AAEA,QAAM,OAAO,IAAI,MAAM,IAAI,CAAC,OAAO;AAAA,IACjC,KAAK,GAAG,EAAE,MAAM,IAAI,EAAE,IAAI;AAAA,IAC1B,MAAM,SAAS,EAAE,aAAa,EAAE;AAAA,IAChC,QAAQ,EAAE,OAAO,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,GAAG,KAAK;AAAA,IACrD,WAAW,OAAO,EAAE,SAAS;AAAA,EAC/B,EAAE;AACF,QAAM,OAAO,KAAK,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,MAAM,CAAC;AACzD,QAAM,MAAM,KAAK,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,OAAO,MAAM,CAAC;AAE3D,MAAI,GAAG,GAAG,KAAK,IAAI,YAAY,IAAI,CAAC,CAAC,KAAK,GAAG,KAAK,IAAI,UAAU,GAAG,CAAC,CAAC,KAAK,GAAG,KAAK,IAAI,CAAC,gBAAgB;AACvG,aAAW,KAAK,MAAM;AACpB,QAAI,GAAG,IAAI,EAAE,KAAK,IAAI,CAAC,KAAK,GAAG,IAAI,IAAI,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,WAAW,CAAC,CAAC,IAAI,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE;AAAA,EACpG;AACA,MAAI,EAAE;AACN,MAAI,GAAG,IAAI,GAAG,IAAI,KAAK,wBAAqB,IAAI,IAAI,EAAE,CAAC;AACzD;AAEA,SAAS,IAAI,GAAW,GAAmB;AACzC,SAAO,EAAE,UAAU,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,EAAE,MAAM;AACxD;","names":[]}
|
package/dist/update-UCT4K6IQ.js
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
downloadFromRegistry
|
|
3
|
-
} from "./chunk-7CHGGMP7.js";
|
|
4
|
-
import {
|
|
5
|
-
readLockfile,
|
|
6
|
-
writeLockEntry
|
|
7
|
-
} from "./chunk-GU4LRYYH.js";
|
|
8
|
-
import {
|
|
9
|
-
mapTarget,
|
|
10
|
-
resolveInsideRoot,
|
|
11
|
-
sha256Hex
|
|
12
|
-
} from "./chunk-QTC5Y3LU.js";
|
|
13
|
-
import {
|
|
14
|
-
RealGitHubClient
|
|
15
|
-
} from "./chunk-OJ356IMJ.js";
|
|
16
|
-
import {
|
|
17
|
-
RegistryClient,
|
|
18
|
-
getFetch
|
|
19
|
-
} from "./chunk-5VPKMXSV.js";
|
|
20
|
-
import {
|
|
21
|
-
resolveRegistry
|
|
22
|
-
} from "./chunk-LP5MV76P.js";
|
|
23
|
-
import "./chunk-AHRTPV2C.js";
|
|
24
|
-
import "./chunk-6CWCR5SO.js";
|
|
25
|
-
import {
|
|
26
|
-
emitJson,
|
|
27
|
-
isJsonMode,
|
|
28
|
-
log,
|
|
29
|
-
pc
|
|
30
|
-
} from "./chunk-PRNBOQHW.js";
|
|
31
|
-
|
|
32
|
-
// src/commands/update.ts
|
|
33
|
-
import fs from "fs";
|
|
34
|
-
import path from "path";
|
|
35
|
-
function onDiskSha(projectRoot, target) {
|
|
36
|
-
const abs = path.join(projectRoot, ...target.split("/"));
|
|
37
|
-
if (!fs.existsSync(abs)) return null;
|
|
38
|
-
return sha256Hex(new Uint8Array(fs.readFileSync(abs)));
|
|
39
|
-
}
|
|
40
|
-
async function updateCommand(opts) {
|
|
41
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
42
|
-
const fetchImpl = getFetch(opts.fetchImpl);
|
|
43
|
-
const github = new RealGitHubClient({ fetchImpl });
|
|
44
|
-
const lock = await readLockfile(cwd);
|
|
45
|
-
if (lock.installs.length === 0) {
|
|
46
|
-
if (isJsonMode()) emitJson({ ok: true, reports: [] });
|
|
47
|
-
else log(pc.dim("No installs recorded in .sandcastle/runcastle.lock.json \u2014 nothing to update."));
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const reports = [];
|
|
51
|
-
for (const entry of lock.installs) {
|
|
52
|
-
if (entry.source.startsWith("github:") || !entry.author) {
|
|
53
|
-
reports.push({ slug: entry.slug, from: entry.version, to: entry.version, updated: [], skippedModified: [], status: "skipped-github" });
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
const registry = new RegistryClient({ baseUrl: resolveRegistry(opts.registry ?? entry.registry), fetchImpl });
|
|
57
|
-
const record = await registry.getWorkflow(entry.author, entry.slug);
|
|
58
|
-
if (record.version === entry.version) {
|
|
59
|
-
reports.push({ slug: entry.slug, from: entry.version, to: record.version, updated: [], skippedModified: [], status: "up-to-date" });
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
const downloaded = await downloadFromRegistry(record, registry, github, fetchImpl);
|
|
63
|
-
const updated = [];
|
|
64
|
-
const skippedModified = [];
|
|
65
|
-
const newFiles = { ...entry.files };
|
|
66
|
-
for (const d of downloaded) {
|
|
67
|
-
const target = mapTarget(d.path, entry.dir);
|
|
68
|
-
const recorded = entry.files[target];
|
|
69
|
-
const disk = onDiskSha(cwd, target);
|
|
70
|
-
if (disk !== null && recorded !== void 0 && disk !== recorded) {
|
|
71
|
-
skippedModified.push(target);
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
if (disk === d.sha256) {
|
|
75
|
-
newFiles[target] = d.sha256;
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
const abs = resolveInsideRoot(cwd, target);
|
|
79
|
-
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
80
|
-
fs.writeFileSync(abs, d.bytes);
|
|
81
|
-
updated.push(target);
|
|
82
|
-
newFiles[target] = d.sha256;
|
|
83
|
-
}
|
|
84
|
-
const next = {
|
|
85
|
-
...entry,
|
|
86
|
-
version: record.version,
|
|
87
|
-
sha: record.commitSha,
|
|
88
|
-
files: newFiles,
|
|
89
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
90
|
-
};
|
|
91
|
-
await writeLockEntry(cwd, next);
|
|
92
|
-
reports.push({
|
|
93
|
-
slug: entry.slug,
|
|
94
|
-
from: entry.version,
|
|
95
|
-
to: record.version,
|
|
96
|
-
updated,
|
|
97
|
-
skippedModified,
|
|
98
|
-
status: "updated"
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
if (isJsonMode()) {
|
|
102
|
-
emitJson({ ok: true, reports });
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
for (const r of reports) {
|
|
106
|
-
if (r.status === "up-to-date") {
|
|
107
|
-
log(`${pc.dim("\xB7")} ${r.slug} ${pc.dim(`is up to date (${r.from})`)}`);
|
|
108
|
-
} else if (r.status === "skipped-github") {
|
|
109
|
-
log(`${pc.dim("\xB7")} ${r.slug} ${pc.dim("installed from github: \u2014 re-run `runcastle add github:\u2026` to update")}`);
|
|
110
|
-
} else {
|
|
111
|
-
log(`${pc.green("\u2191")} ${r.slug} ${pc.dim(`${r.from} \u2192 ${r.to}`)}`);
|
|
112
|
-
for (const f of r.updated) log(` ${pc.green("updated")} ${f}`);
|
|
113
|
-
for (const f of r.skippedModified) log(` ${pc.yellow("skipped")} ${f} ${pc.dim("(locally modified \u2014 resolve manually)")}`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
const anyModified = reports.some((r) => r.skippedModified.length > 0);
|
|
117
|
-
if (anyModified) {
|
|
118
|
-
log("");
|
|
119
|
-
log(pc.yellow("Some files were locally modified and left untouched. Diff them against the new version and merge by hand."));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
export {
|
|
123
|
-
updateCommand
|
|
124
|
-
};
|
|
125
|
-
//# sourceMappingURL=update-UCT4K6IQ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/update.ts"],"sourcesContent":["// `runcastle update` (spec §5.6, minimal v1). Read the lockfile, check for newer\n// versions, auto-update files that are UNMODIFIED since install, and REFUSE to\n// touch locally-modified files (telling the user to resolve them manually).\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { emitJson, isJsonMode, log, pc } from \"../lib/output.js\";\nimport { resolveRegistry } from \"../lib/config.js\";\nimport { RegistryClient } from \"../lib/registry.js\";\nimport { RealGitHubClient } from \"../lib/github.js\";\nimport { downloadFromRegistry } from \"../lib/download.js\";\nimport { sha256Hex } from \"../lib/checksum.js\";\nimport { getFetch, type FetchLike } from \"../lib/http.js\";\nimport { readLockfile, writeLockEntry, type LockEntry } from \"../lib/lockfile.js\";\nimport { mapTarget, resolveInsideRoot } from \"../lib/installer.js\";\n\nexport interface UpdateOptions {\n registry?: string;\n json?: boolean;\n fetchImpl?: FetchLike;\n cwd?: string;\n}\n\ninterface UpdateReport {\n slug: string;\n from: string;\n to: string;\n updated: string[];\n skippedModified: string[];\n status: \"updated\" | \"up-to-date\" | \"skipped-github\" | \"no-op\";\n}\n\nfunction onDiskSha(projectRoot: string, target: string): string | null {\n const abs = path.join(projectRoot, ...target.split(\"/\"));\n if (!fs.existsSync(abs)) return null;\n return sha256Hex(new Uint8Array(fs.readFileSync(abs)));\n}\n\nexport async function updateCommand(opts: UpdateOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const fetchImpl = getFetch(opts.fetchImpl);\n const github = new RealGitHubClient({ fetchImpl });\n const lock = await readLockfile(cwd);\n\n if (lock.installs.length === 0) {\n if (isJsonMode()) emitJson({ ok: true, reports: [] });\n else log(pc.dim(\"No installs recorded in .sandcastle/runcastle.lock.json — nothing to update.\"));\n return;\n }\n\n const reports: UpdateReport[] = [];\n\n for (const entry of lock.installs) {\n if (entry.source.startsWith(\"github:\") || !entry.author) {\n reports.push({ slug: entry.slug, from: entry.version, to: entry.version, updated: [], skippedModified: [], status: \"skipped-github\" });\n continue;\n }\n const registry = new RegistryClient({ baseUrl: resolveRegistry(opts.registry ?? entry.registry), fetchImpl });\n const record = await registry.getWorkflow(entry.author, entry.slug);\n if (record.version === entry.version) {\n reports.push({ slug: entry.slug, from: entry.version, to: record.version, updated: [], skippedModified: [], status: \"up-to-date\" });\n continue;\n }\n\n const downloaded = await downloadFromRegistry(record, registry, github, fetchImpl);\n const updated: string[] = [];\n const skippedModified: string[] = [];\n const newFiles: Record<string, string> = { ...entry.files };\n\n for (const d of downloaded) {\n const target = mapTarget(d.path, entry.dir);\n const recorded = entry.files[target];\n const disk = onDiskSha(cwd, target);\n // Locally modified: on-disk differs from what we installed last time.\n if (disk !== null && recorded !== undefined && disk !== recorded) {\n skippedModified.push(target);\n continue;\n }\n if (disk === d.sha256) {\n // already identical to the new bytes\n newFiles[target] = d.sha256;\n continue;\n }\n // Defense-in-depth: never write outside the project (§8 / C1).\n const abs = resolveInsideRoot(cwd, target);\n fs.mkdirSync(path.dirname(abs), { recursive: true });\n fs.writeFileSync(abs, d.bytes);\n updated.push(target);\n newFiles[target] = d.sha256;\n }\n\n // Persist the new version/sha; skipped files retain their recorded checksum.\n const next: LockEntry = {\n ...entry,\n version: record.version,\n sha: record.commitSha,\n files: newFiles,\n installedAt: new Date().toISOString(),\n };\n await writeLockEntry(cwd, next);\n\n reports.push({\n slug: entry.slug,\n from: entry.version,\n to: record.version,\n updated,\n skippedModified,\n status: \"updated\",\n });\n }\n\n if (isJsonMode()) {\n emitJson({ ok: true, reports });\n return;\n }\n\n for (const r of reports) {\n if (r.status === \"up-to-date\") {\n log(`${pc.dim(\"·\")} ${r.slug} ${pc.dim(`is up to date (${r.from})`)}`);\n } else if (r.status === \"skipped-github\") {\n log(`${pc.dim(\"·\")} ${r.slug} ${pc.dim(\"installed from github: — re-run `runcastle add github:…` to update\")}`);\n } else {\n log(`${pc.green(\"↑\")} ${r.slug} ${pc.dim(`${r.from} → ${r.to}`)}`);\n for (const f of r.updated) log(` ${pc.green(\"updated\")} ${f}`);\n for (const f of r.skippedModified) log(` ${pc.yellow(\"skipped\")} ${f} ${pc.dim(\"(locally modified — resolve manually)\")}`);\n }\n }\n const anyModified = reports.some((r) => r.skippedModified.length > 0);\n if (anyModified) {\n log(\"\");\n log(pc.yellow(\"Some files were locally modified and left untouched. Diff them against the new version and merge by hand.\"));\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
runValidation,
|
|
3
|
-
validateCommand
|
|
4
|
-
} from "./chunk-SNUH2UT6.js";
|
|
5
|
-
import "./chunk-AHRTPV2C.js";
|
|
6
|
-
import "./chunk-6CWCR5SO.js";
|
|
7
|
-
import "./chunk-PRNBOQHW.js";
|
|
8
|
-
export {
|
|
9
|
-
runValidation,
|
|
10
|
-
validateCommand
|
|
11
|
-
};
|
|
12
|
-
//# sourceMappingURL=validate-GFDSEVPV.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|