runcastle 0.2.0 → 1.0.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 +47 -0
- package/bin/runcastle.js +5580 -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 +5424 -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/init-MOJNLWXK.js
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DEFAULT_ENTRYPOINT,
|
|
3
|
-
DEFAULT_REGISTRY_URL,
|
|
4
|
-
PAYLOAD_DIR,
|
|
5
|
-
SANDBOX_PROVIDERS,
|
|
6
|
-
SANDCASTLE_PACKAGE_NAME,
|
|
7
|
-
SCHEMA_ID,
|
|
8
|
-
SCHEMA_VERSION,
|
|
9
|
-
detectAgents,
|
|
10
|
-
detectShellExpansions,
|
|
11
|
-
extractHooks,
|
|
12
|
-
normalizePath
|
|
13
|
-
} from "./chunk-AHRTPV2C.js";
|
|
14
|
-
import {
|
|
15
|
-
CliError
|
|
16
|
-
} from "./chunk-6CWCR5SO.js";
|
|
17
|
-
import {
|
|
18
|
-
emitJson,
|
|
19
|
-
isJsonMode,
|
|
20
|
-
log,
|
|
21
|
-
pc
|
|
22
|
-
} from "./chunk-PRNBOQHW.js";
|
|
23
|
-
|
|
24
|
-
// src/commands/init.ts
|
|
25
|
-
import fs from "fs";
|
|
26
|
-
import fsp from "fs/promises";
|
|
27
|
-
import path from "path";
|
|
28
|
-
function kebab(input) {
|
|
29
|
-
return input.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "my-workflow";
|
|
30
|
-
}
|
|
31
|
-
async function walkPayload(root) {
|
|
32
|
-
const out = [];
|
|
33
|
-
async function rec(dir) {
|
|
34
|
-
let entries;
|
|
35
|
-
try {
|
|
36
|
-
entries = await fsp.readdir(dir, { withFileTypes: true });
|
|
37
|
-
} catch {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
for (const e of entries) {
|
|
41
|
-
const abs = path.join(dir, e.name);
|
|
42
|
-
if (e.isDirectory()) await rec(abs);
|
|
43
|
-
else if (e.isFile()) out.push(normalizePath(path.relative(root, abs)));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
await rec(path.join(root, PAYLOAD_DIR));
|
|
47
|
-
return out.sort();
|
|
48
|
-
}
|
|
49
|
-
function parseEnvExample(content) {
|
|
50
|
-
const keys = [];
|
|
51
|
-
for (const raw of content.split(/\r?\n/)) {
|
|
52
|
-
const line = raw.trim();
|
|
53
|
-
if (!line || line.startsWith("#")) continue;
|
|
54
|
-
const m = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(line);
|
|
55
|
-
if (m?.[1] && !keys.includes(m[1])) keys.push(m[1]);
|
|
56
|
-
}
|
|
57
|
-
return keys;
|
|
58
|
-
}
|
|
59
|
-
var TOPOLOGY_GUIDE = `// Topology drives the site diagram and cards. Add a "topology" key like:
|
|
60
|
-
// "topology": {
|
|
61
|
-
// "nodes": [
|
|
62
|
-
// { "id": "plan", "role": "planner", "label": "Plan" },
|
|
63
|
-
// { "id": "impl", "role": "implementer", "label": "Implement", "fanOut": true }
|
|
64
|
-
// ],
|
|
65
|
-
// "edges": [ { "from": "plan", "to": "impl" } ]
|
|
66
|
-
// }
|
|
67
|
-
// Node "role" must match an agents[].role (or null for non-agent steps like git).
|
|
68
|
-
// Docs: ${DEFAULT_REGISTRY_URL}/docs/topology`;
|
|
69
|
-
async function initCommand(opts) {
|
|
70
|
-
const cwd = process.cwd();
|
|
71
|
-
const manifestPath = path.join(cwd, "workflow.json");
|
|
72
|
-
if (fs.existsSync(manifestPath)) {
|
|
73
|
-
throw new CliError("workflow.json already exists \u2014 edit it directly instead of re-initializing", {
|
|
74
|
-
code: "E_EXISTS"
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
if (!fs.existsSync(path.join(cwd, PAYLOAD_DIR))) {
|
|
78
|
-
throw new CliError(`no ${PAYLOAD_DIR}/ directory found in the current directory`, {
|
|
79
|
-
code: "E_NO_PAYLOAD",
|
|
80
|
-
details: ["`runcastle init` scaffolds from an existing Sandcastle payload."]
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
const files = await walkPayload(cwd);
|
|
84
|
-
const entrypointRel = files.find((f) => f === DEFAULT_ENTRYPOINT) ?? files.find((f) => f.endsWith("/main.ts")) ?? DEFAULT_ENTRYPOINT;
|
|
85
|
-
let entrypointSource = "";
|
|
86
|
-
try {
|
|
87
|
-
entrypointSource = await fsp.readFile(path.join(cwd, ...entrypointRel.split("/")), "utf8");
|
|
88
|
-
} catch {
|
|
89
|
-
}
|
|
90
|
-
const detectedAgents = entrypointSource ? detectAgents(entrypointSource) : [];
|
|
91
|
-
const agents = detectedAgents.map((a, i) => ({
|
|
92
|
-
role: a.factory === "claudeCode" ? `agent-${i + 1}` : a.factory,
|
|
93
|
-
provider: a.provider,
|
|
94
|
-
model: a.model ?? "unknown"
|
|
95
|
-
}));
|
|
96
|
-
const hooks = entrypointSource ? extractHooks(entrypointSource) : void 0;
|
|
97
|
-
let usesShellExpansion = false;
|
|
98
|
-
for (const f of files) {
|
|
99
|
-
if (!f.endsWith(".md")) continue;
|
|
100
|
-
try {
|
|
101
|
-
const text = await fsp.readFile(path.join(cwd, ...f.split("/")), "utf8");
|
|
102
|
-
if (detectShellExpansions(text).length > 0) usesShellExpansion = true;
|
|
103
|
-
} catch {
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
const envExamplePath = path.join(cwd, PAYLOAD_DIR, ".env.example");
|
|
107
|
-
let envVars = [];
|
|
108
|
-
try {
|
|
109
|
-
envVars = parseEnvExample(await fsp.readFile(envExamplePath, "utf8"));
|
|
110
|
-
} catch {
|
|
111
|
-
}
|
|
112
|
-
const dirName = path.basename(cwd);
|
|
113
|
-
const interactive = !opts.yes && !isJsonMode() && process.stdin.isTTY;
|
|
114
|
-
let identity = {
|
|
115
|
-
name: dirName,
|
|
116
|
-
slug: kebab(dirName),
|
|
117
|
-
description: "TODO: describe what this workflow does.",
|
|
118
|
-
license: "MIT",
|
|
119
|
-
tags: [],
|
|
120
|
-
sandboxProviders: ["docker"]
|
|
121
|
-
};
|
|
122
|
-
if (interactive) {
|
|
123
|
-
const clack = await import("@clack/prompts");
|
|
124
|
-
clack.intro(pc.cyan("runcastle init"));
|
|
125
|
-
const answers = await clack.group(
|
|
126
|
-
{
|
|
127
|
-
name: () => clack.text({ message: "Workflow name", initialValue: dirName }),
|
|
128
|
-
slug: () => clack.text({
|
|
129
|
-
message: "Slug",
|
|
130
|
-
initialValue: kebab(dirName),
|
|
131
|
-
validate: (v) => /^[a-z0-9-]{3,64}$/.test(v ?? "") ? void 0 : "must match [a-z0-9-]{3,64}"
|
|
132
|
-
}),
|
|
133
|
-
description: () => clack.text({
|
|
134
|
-
message: "Description",
|
|
135
|
-
validate: (v) => (v ?? "").trim().length > 0 ? void 0 : "required"
|
|
136
|
-
}),
|
|
137
|
-
license: () => clack.text({ message: "License (SPDX id)", initialValue: "MIT" }),
|
|
138
|
-
tags: () => clack.text({ message: "Tags (comma-separated)", initialValue: "" }),
|
|
139
|
-
sandboxProviders: () => clack.multiselect({
|
|
140
|
-
message: "Sandbox providers",
|
|
141
|
-
options: SANDBOX_PROVIDERS.map((p) => ({ value: p, label: p })),
|
|
142
|
-
initialValues: ["docker"],
|
|
143
|
-
required: true
|
|
144
|
-
})
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
onCancel: () => {
|
|
148
|
-
clack.cancel("init cancelled");
|
|
149
|
-
throw new CliError("", { exitCode: 1, code: "E_CANCELLED" });
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
);
|
|
153
|
-
identity = {
|
|
154
|
-
name: String(answers.name),
|
|
155
|
-
slug: String(answers.slug),
|
|
156
|
-
description: String(answers.description),
|
|
157
|
-
license: String(answers.license) || "MIT",
|
|
158
|
-
tags: String(answers.tags).split(",").map((t) => t.trim()).filter(Boolean).slice(0, 8),
|
|
159
|
-
sandboxProviders: answers.sandboxProviders
|
|
160
|
-
};
|
|
161
|
-
clack.outro(pc.green("scaffolding workflow.json"));
|
|
162
|
-
}
|
|
163
|
-
const manifest = {
|
|
164
|
-
$schema: SCHEMA_ID,
|
|
165
|
-
schemaVersion: SCHEMA_VERSION,
|
|
166
|
-
name: identity.name,
|
|
167
|
-
slug: identity.slug,
|
|
168
|
-
version: "0.1.0",
|
|
169
|
-
description: identity.description,
|
|
170
|
-
license: identity.license,
|
|
171
|
-
tags: identity.tags,
|
|
172
|
-
sandcastle: {
|
|
173
|
-
packageName: SANDCASTLE_PACKAGE_NAME,
|
|
174
|
-
versionRange: ">=0.12.0 <0.13.0",
|
|
175
|
-
entrypoint: entrypointRel
|
|
176
|
-
},
|
|
177
|
-
agents,
|
|
178
|
-
sandboxProviders: identity.sandboxProviders,
|
|
179
|
-
env: envVars.map((name) => ({ name, required: true, description: "" })),
|
|
180
|
-
disclosures: {
|
|
181
|
-
hostHooks: hooks?.static ? hooks.hostCommands : [],
|
|
182
|
-
sandboxHooks: hooks?.static ? hooks.sandboxCommands : [],
|
|
183
|
-
usesShellExpansion
|
|
184
|
-
},
|
|
185
|
-
files
|
|
186
|
-
};
|
|
187
|
-
const json = JSON.stringify(manifest, null, 2);
|
|
188
|
-
await fsp.writeFile(manifestPath, `${json}
|
|
189
|
-
`, "utf8");
|
|
190
|
-
if (isJsonMode()) {
|
|
191
|
-
emitJson({ ok: true, path: "workflow.json", files: files.length, agents: agents.length });
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
log(pc.green("\u2713 wrote workflow.json"));
|
|
195
|
-
log("");
|
|
196
|
-
log(pc.dim(` ${files.length} payload file(s), ${agents.length} agent(s) detected`));
|
|
197
|
-
if (hooks && !hooks.static) {
|
|
198
|
-
log(pc.yellow(" \u26A0 hooks in your entrypoint are not statically analyzable \u2014 disclosures left empty; fix before publishing"));
|
|
199
|
-
}
|
|
200
|
-
log("");
|
|
201
|
-
log(pc.bold("Add a topology (optional but recommended):"));
|
|
202
|
-
log(pc.dim(TOPOLOGY_GUIDE));
|
|
203
|
-
log("");
|
|
204
|
-
log(pc.bold("Next steps:"));
|
|
205
|
-
log(` 1. Fill in the description, env descriptions, and tags in workflow.json`);
|
|
206
|
-
log(` 2. ${pc.cyan("runcastle validate")}`);
|
|
207
|
-
log(` 3. ${pc.cyan("runcastle publish")}`);
|
|
208
|
-
}
|
|
209
|
-
export {
|
|
210
|
-
initCommand
|
|
211
|
-
};
|
|
212
|
-
//# sourceMappingURL=init-MOJNLWXK.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/init.ts"],"sourcesContent":["// `runcastle init` (spec §5.1). Scaffold a workflow.json from an existing\n// .sandcastle/ directory: pre-populate `files` (dir walk), `env` (.env.example),\n// `disclosures` + `agents` (schema static analyzers). Prompt (clack) for the\n// identity fields. Topology is NOT auto-generated — we print a commented-out\n// example and a docs link. Errors if workflow.json already exists.\n\nimport fs from \"node:fs\";\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n DEFAULT_ENTRYPOINT,\n DEFAULT_REGISTRY_URL,\n PAYLOAD_DIR,\n SANDBOX_PROVIDERS,\n SANDCASTLE_PACKAGE_NAME,\n SCHEMA_ID,\n SCHEMA_VERSION,\n detectAgents,\n extractHooks,\n detectShellExpansions,\n normalizePath,\n type ManifestInput,\n} from \"@castellan/schema\";\nimport { CliError } from \"../lib/errors.js\";\nimport { isJsonMode, log, emitJson, pc } from \"../lib/output.js\";\n\nexport interface InitOptions {\n yes?: boolean;\n}\n\nfunction kebab(input: string): string {\n return (\n input\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 64) || \"my-workflow\"\n );\n}\n\nasync function walkPayload(root: string): Promise<string[]> {\n const out: string[] = [];\n async function rec(dir: string): Promise<void> {\n let entries: fs.Dirent[];\n try {\n entries = await fsp.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n const abs = path.join(dir, e.name);\n if (e.isDirectory()) await rec(abs);\n else if (e.isFile()) out.push(normalizePath(path.relative(root, abs)));\n }\n }\n await rec(path.join(root, PAYLOAD_DIR));\n return out.sort();\n}\n\nfunction parseEnvExample(content: string): string[] {\n const keys: string[] = [];\n for (const raw of content.split(/\\r?\\n/)) {\n const line = raw.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const m = /^(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=/.exec(line);\n if (m?.[1] && !keys.includes(m[1])) keys.push(m[1]);\n }\n return keys;\n}\n\nconst TOPOLOGY_GUIDE = `// Topology drives the site diagram and cards. Add a \"topology\" key like:\n// \"topology\": {\n// \"nodes\": [\n// { \"id\": \"plan\", \"role\": \"planner\", \"label\": \"Plan\" },\n// { \"id\": \"impl\", \"role\": \"implementer\", \"label\": \"Implement\", \"fanOut\": true }\n// ],\n// \"edges\": [ { \"from\": \"plan\", \"to\": \"impl\" } ]\n// }\n// Node \"role\" must match an agents[].role (or null for non-agent steps like git).\n// Docs: ${DEFAULT_REGISTRY_URL}/docs/topology`;\n\nexport async function initCommand(opts: InitOptions): Promise<void> {\n const cwd = process.cwd();\n const manifestPath = path.join(cwd, \"workflow.json\");\n if (fs.existsSync(manifestPath)) {\n throw new CliError(\"workflow.json already exists — edit it directly instead of re-initializing\", {\n code: \"E_EXISTS\",\n });\n }\n if (!fs.existsSync(path.join(cwd, PAYLOAD_DIR))) {\n throw new CliError(`no ${PAYLOAD_DIR}/ directory found in the current directory`, {\n code: \"E_NO_PAYLOAD\",\n details: [\"`runcastle init` scaffolds from an existing Sandcastle payload.\"],\n });\n }\n\n // --- Pre-populate from the payload ---------------------------------------\n const files = await walkPayload(cwd);\n const entrypointRel =\n files.find((f) => f === DEFAULT_ENTRYPOINT) ??\n files.find((f) => f.endsWith(\"/main.ts\")) ??\n DEFAULT_ENTRYPOINT;\n\n let entrypointSource = \"\";\n try {\n entrypointSource = await fsp.readFile(path.join(cwd, ...entrypointRel.split(\"/\")), \"utf8\");\n } catch {\n /* entrypoint missing — leave analyzers empty */\n }\n\n const detectedAgents = entrypointSource ? detectAgents(entrypointSource) : [];\n const agents = detectedAgents.map((a, i) => ({\n role: a.factory === \"claudeCode\" ? `agent-${i + 1}` : a.factory,\n provider: a.provider,\n model: a.model ?? \"unknown\",\n }));\n\n const hooks = entrypointSource ? extractHooks(entrypointSource) : undefined;\n\n let usesShellExpansion = false;\n for (const f of files) {\n if (!f.endsWith(\".md\")) continue;\n try {\n const text = await fsp.readFile(path.join(cwd, ...f.split(\"/\")), \"utf8\");\n if (detectShellExpansions(text).length > 0) usesShellExpansion = true;\n } catch {\n /* ignore */\n }\n }\n\n const envExamplePath = path.join(cwd, PAYLOAD_DIR, \".env.example\");\n let envVars: string[] = [];\n try {\n envVars = parseEnvExample(await fsp.readFile(envExamplePath, \"utf8\"));\n } catch {\n /* no .env.example */\n }\n\n // --- Identity (prompts or defaults) --------------------------------------\n const dirName = path.basename(cwd);\n const interactive = !opts.yes && !isJsonMode() && process.stdin.isTTY;\n\n let identity = {\n name: dirName,\n slug: kebab(dirName),\n description: \"TODO: describe what this workflow does.\",\n license: \"MIT\",\n tags: [] as string[],\n sandboxProviders: [\"docker\"] as string[],\n };\n\n if (interactive) {\n const clack = await import(\"@clack/prompts\");\n clack.intro(pc.cyan(\"runcastle init\"));\n const answers = await clack.group(\n {\n name: () => clack.text({ message: \"Workflow name\", initialValue: dirName }),\n slug: () =>\n clack.text({\n message: \"Slug\",\n initialValue: kebab(dirName),\n validate: (v) => (/^[a-z0-9-]{3,64}$/.test(v ?? \"\") ? undefined : \"must match [a-z0-9-]{3,64}\"),\n }),\n description: () =>\n clack.text({\n message: \"Description\",\n validate: (v) => ((v ?? \"\").trim().length > 0 ? undefined : \"required\"),\n }),\n license: () => clack.text({ message: \"License (SPDX id)\", initialValue: \"MIT\" }),\n tags: () => clack.text({ message: \"Tags (comma-separated)\", initialValue: \"\" }),\n sandboxProviders: () =>\n clack.multiselect({\n message: \"Sandbox providers\",\n options: SANDBOX_PROVIDERS.map((p) => ({ value: p, label: p })),\n initialValues: [\"docker\"],\n required: true,\n }),\n },\n {\n onCancel: () => {\n clack.cancel(\"init cancelled\");\n throw new CliError(\"\", { exitCode: 1, code: \"E_CANCELLED\" });\n },\n },\n );\n identity = {\n name: String(answers.name),\n slug: String(answers.slug),\n description: String(answers.description),\n license: String(answers.license) || \"MIT\",\n tags: String(answers.tags)\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean)\n .slice(0, 8),\n sandboxProviders: answers.sandboxProviders as string[],\n };\n clack.outro(pc.green(\"scaffolding workflow.json\"));\n }\n\n // --- Assemble the manifest ------------------------------------------------\n const manifest: ManifestInput = {\n $schema: SCHEMA_ID,\n schemaVersion: SCHEMA_VERSION,\n name: identity.name,\n slug: identity.slug,\n version: \"0.1.0\",\n description: identity.description,\n license: identity.license,\n tags: identity.tags,\n sandcastle: {\n packageName: SANDCASTLE_PACKAGE_NAME,\n versionRange: \">=0.12.0 <0.13.0\",\n entrypoint: entrypointRel,\n },\n agents,\n sandboxProviders: identity.sandboxProviders as ManifestInput[\"sandboxProviders\"],\n env: envVars.map((name) => ({ name, required: true, description: \"\" })),\n disclosures: {\n hostHooks: hooks?.static ? hooks.hostCommands : [],\n sandboxHooks: hooks?.static ? hooks.sandboxCommands : [],\n usesShellExpansion,\n },\n files,\n };\n\n const json = JSON.stringify(manifest, null, 2);\n await fsp.writeFile(manifestPath, `${json}\\n`, \"utf8\");\n\n if (isJsonMode()) {\n emitJson({ ok: true, path: \"workflow.json\", files: files.length, agents: agents.length });\n return;\n }\n\n log(pc.green(\"✓ wrote workflow.json\"));\n log(\"\");\n log(pc.dim(` ${files.length} payload file(s), ${agents.length} agent(s) detected`));\n if (hooks && !hooks.static) {\n log(pc.yellow(\" ⚠ hooks in your entrypoint are not statically analyzable — disclosures left empty; fix before publishing\"));\n }\n log(\"\");\n log(pc.bold(\"Add a topology (optional but recommended):\"));\n log(pc.dim(TOPOLOGY_GUIDE));\n log(\"\");\n log(pc.bold(\"Next steps:\"));\n log(` 1. Fill in the description, env descriptions, and tags in workflow.json`);\n log(` 2. ${pc.cyan(\"runcastle validate\")}`);\n log(` 3. ${pc.cyan(\"runcastle publish\")}`);\n}\n"],"mappings":"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","names":[]}
|
package/dist/logout-YPGZ6BB6.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
authPathForDisplay,
|
|
3
|
-
clearAuth
|
|
4
|
-
} from "./chunk-44KDPUYJ.js";
|
|
5
|
-
import "./chunk-LP5MV76P.js";
|
|
6
|
-
import "./chunk-AHRTPV2C.js";
|
|
7
|
-
import {
|
|
8
|
-
emitJson,
|
|
9
|
-
isJsonMode,
|
|
10
|
-
log,
|
|
11
|
-
pc
|
|
12
|
-
} from "./chunk-PRNBOQHW.js";
|
|
13
|
-
|
|
14
|
-
// src/commands/logout.ts
|
|
15
|
-
async function logoutCommand() {
|
|
16
|
-
const cleared = await clearAuth();
|
|
17
|
-
if (isJsonMode()) {
|
|
18
|
-
emitJson({ ok: true, cleared });
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
if (cleared) log(pc.green(`\u2713 logged out (removed ${authPathForDisplay()})`));
|
|
22
|
-
else log(pc.dim("Not logged in \u2014 no cached token to remove."));
|
|
23
|
-
}
|
|
24
|
-
export {
|
|
25
|
-
logoutCommand
|
|
26
|
-
};
|
|
27
|
-
//# sourceMappingURL=logout-YPGZ6BB6.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/logout.ts"],"sourcesContent":["// `runcastle logout` (spec §5.3). Clears the cached GitHub OAuth token.\n\nimport { clearAuth, authPathForDisplay } from \"../lib/auth.js\";\nimport { emitJson, isJsonMode, log, pc } from \"../lib/output.js\";\n\nexport async function logoutCommand(): Promise<void> {\n const cleared = await clearAuth();\n if (isJsonMode()) {\n emitJson({ ok: true, cleared });\n return;\n }\n if (cleared) log(pc.green(`✓ logged out (removed ${authPathForDisplay()})`));\n else log(pc.dim(\"Not logged in — no cached token to remove.\"));\n}\n"],"mappings":";;;;;;;;;;;;;;AAKA,eAAsB,gBAA+B;AACnD,QAAM,UAAU,MAAM,UAAU;AAChC,MAAI,WAAW,GAAG;AAChB,aAAS,EAAE,IAAI,MAAM,QAAQ,CAAC;AAC9B;AAAA,EACF;AACA,MAAI,QAAS,KAAI,GAAG,MAAM,8BAAyB,mBAAmB,CAAC,GAAG,CAAC;AAAA,MACtE,KAAI,GAAG,IAAI,iDAA4C,CAAC;AAC/D;","names":[]}
|
package/dist/publish-W37Y6TO6.js
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
loadAuth,
|
|
3
|
-
saveAuth
|
|
4
|
-
} from "./chunk-44KDPUYJ.js";
|
|
5
|
-
import {
|
|
6
|
-
runValidation
|
|
7
|
-
} from "./chunk-SNUH2UT6.js";
|
|
8
|
-
import {
|
|
9
|
-
RealGitHubClient
|
|
10
|
-
} from "./chunk-OJ356IMJ.js";
|
|
11
|
-
import {
|
|
12
|
-
RegistryClient,
|
|
13
|
-
getFetch
|
|
14
|
-
} from "./chunk-5VPKMXSV.js";
|
|
15
|
-
import {
|
|
16
|
-
GITHUB_CLIENT_ID,
|
|
17
|
-
resolveRegistry
|
|
18
|
-
} from "./chunk-LP5MV76P.js";
|
|
19
|
-
import {
|
|
20
|
-
PublishRequestSchema,
|
|
21
|
-
VersionRecordSchema,
|
|
22
|
-
parseManifest
|
|
23
|
-
} from "./chunk-AHRTPV2C.js";
|
|
24
|
-
import {
|
|
25
|
-
CliError
|
|
26
|
-
} from "./chunk-6CWCR5SO.js";
|
|
27
|
-
import {
|
|
28
|
-
emitJson,
|
|
29
|
-
isJsonMode,
|
|
30
|
-
log,
|
|
31
|
-
pc
|
|
32
|
-
} from "./chunk-PRNBOQHW.js";
|
|
33
|
-
|
|
34
|
-
// src/commands/publish.ts
|
|
35
|
-
import fsp from "fs/promises";
|
|
36
|
-
import path from "path";
|
|
37
|
-
|
|
38
|
-
// src/lib/git.ts
|
|
39
|
-
import { execFile } from "child_process";
|
|
40
|
-
import { promisify } from "util";
|
|
41
|
-
var pExecFile = promisify(execFile);
|
|
42
|
-
async function git(args, cwd) {
|
|
43
|
-
try {
|
|
44
|
-
const { stdout } = await pExecFile("git", args, { cwd, windowsHide: true });
|
|
45
|
-
return stdout.trim();
|
|
46
|
-
} catch (e) {
|
|
47
|
-
const err = e;
|
|
48
|
-
if (err.code === "ENOENT") {
|
|
49
|
-
throw new CliError("`git` was not found on your PATH \u2014 install Git and try again", {
|
|
50
|
-
code: "E_NO_GIT"
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
throw new CliError(`git ${args.join(" ")} failed`, {
|
|
54
|
-
code: "E_GIT",
|
|
55
|
-
details: [err.stderr?.trim() || err.message]
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
async function assertGitAvailable(cwd) {
|
|
60
|
-
await git(["--version"], cwd);
|
|
61
|
-
}
|
|
62
|
-
async function isGitRepo(cwd) {
|
|
63
|
-
try {
|
|
64
|
-
const out = await git(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
65
|
-
return out === "true";
|
|
66
|
-
} catch {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
async function headSha(cwd) {
|
|
71
|
-
return git(["rev-parse", "HEAD"], cwd);
|
|
72
|
-
}
|
|
73
|
-
async function currentBranch(cwd) {
|
|
74
|
-
return git(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
75
|
-
}
|
|
76
|
-
async function isClean(cwd) {
|
|
77
|
-
const out = await git(["status", "--porcelain"], cwd);
|
|
78
|
-
return out.length === 0;
|
|
79
|
-
}
|
|
80
|
-
async function originUrl(cwd) {
|
|
81
|
-
try {
|
|
82
|
-
return await git(["remote", "get-url", "origin"], cwd);
|
|
83
|
-
} catch {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
async function headOnOrigin(cwd, branch, sha) {
|
|
88
|
-
try {
|
|
89
|
-
const out = await git(["branch", "-r", "--contains", sha], cwd);
|
|
90
|
-
return out.split(/\r?\n/).some((l) => l.trim().startsWith(`origin/${branch}`) || l.trim().startsWith("origin/"));
|
|
91
|
-
} catch {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
async function repoPrefix(cwd) {
|
|
96
|
-
const out = await git(["rev-parse", "--show-prefix"], cwd);
|
|
97
|
-
return out.replace(/\/+$/, "");
|
|
98
|
-
}
|
|
99
|
-
function parseGitHubRemote(url) {
|
|
100
|
-
const cleaned = url.trim().replace(/\.git$/, "");
|
|
101
|
-
const m = /^https?:\/\/github\.com\/([^/]+)\/([^/]+)/.exec(cleaned) || /^git@github\.com:([^/]+)\/([^/]+)/.exec(cleaned) || /^ssh:\/\/git@github\.com\/([^/]+)\/([^/]+)/.exec(cleaned);
|
|
102
|
-
if (!m || !m[1] || !m[2]) return null;
|
|
103
|
-
return { owner: m[1], repo: m[2] };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// src/commands/publish.ts
|
|
107
|
-
async function preconditions(cwd) {
|
|
108
|
-
await assertGitAvailable(cwd);
|
|
109
|
-
if (!await isGitRepo(cwd)) {
|
|
110
|
-
throw new CliError("not inside a git repository \u2014 publish must run in your workflow's repo", { code: "E_NOT_REPO" });
|
|
111
|
-
}
|
|
112
|
-
if (!await isClean(cwd)) {
|
|
113
|
-
throw new CliError("working tree has uncommitted changes \u2014 commit or stash them, then publish", {
|
|
114
|
-
code: "E_DIRTY_TREE",
|
|
115
|
-
details: ["Publishing pins a commit SHA; only committed files are published."]
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
const origin = await originUrl(cwd);
|
|
119
|
-
if (!origin) {
|
|
120
|
-
throw new CliError("no `origin` remote \u2014 add a github.com remote and push your branch", { code: "E_NO_ORIGIN" });
|
|
121
|
-
}
|
|
122
|
-
const parsed = parseGitHubRemote(origin);
|
|
123
|
-
if (!parsed) {
|
|
124
|
-
throw new CliError(`origin is not a github.com remote (${origin})`, {
|
|
125
|
-
code: "E_NOT_GITHUB",
|
|
126
|
-
details: ["Only GitHub repositories can be published in v1."]
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
const branch = await currentBranch(cwd);
|
|
130
|
-
const sha = await headSha(cwd);
|
|
131
|
-
if (!await headOnOrigin(cwd, branch, sha)) {
|
|
132
|
-
throw new CliError(`HEAD (${sha.slice(0, 7)}) is not on origin \u2014 push branch "${branch}" first`, {
|
|
133
|
-
code: "E_NOT_PUSHED",
|
|
134
|
-
details: [`git push -u origin ${branch}`]
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
const directory = await repoPrefix(cwd);
|
|
138
|
-
return { repo: `${parsed.owner}/${parsed.repo}`, directory, sha, branch };
|
|
139
|
-
}
|
|
140
|
-
async function loadManifest(cwd) {
|
|
141
|
-
const raw = await fsp.readFile(path.join(cwd, "workflow.json"), "utf8").catch(() => {
|
|
142
|
-
throw new CliError("no workflow.json in the current directory", { code: "E_NO_MANIFEST" });
|
|
143
|
-
});
|
|
144
|
-
const parsed = parseManifest(JSON.parse(raw));
|
|
145
|
-
if (!parsed.ok) {
|
|
146
|
-
throw new CliError("workflow.json is invalid \u2014 run `runcastle validate` for details", { code: "E_SCHEMA_INVALID" });
|
|
147
|
-
}
|
|
148
|
-
return parsed.manifest;
|
|
149
|
-
}
|
|
150
|
-
async function authenticate(github) {
|
|
151
|
-
const cached = await loadAuth();
|
|
152
|
-
if (cached) {
|
|
153
|
-
return { token: cached.token, login: cached.login ?? "(cached)" };
|
|
154
|
-
}
|
|
155
|
-
const grant = await github.requestDeviceCode(GITHUB_CLIENT_ID);
|
|
156
|
-
log("");
|
|
157
|
-
log(pc.bold("Authorize with GitHub:"));
|
|
158
|
-
log(` 1. Open ${pc.cyan(grant.verificationUri)}`);
|
|
159
|
-
log(` 2. Enter the code: ${pc.bold(grant.userCode)}`);
|
|
160
|
-
log(pc.dim(" Waiting for authorization\u2026"));
|
|
161
|
-
const token = await github.pollForToken(GITHUB_CLIENT_ID, grant);
|
|
162
|
-
const login = await github.getViewerLogin(token);
|
|
163
|
-
await saveAuth({ token, login, savedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
164
|
-
log(pc.green(`\u2713 authenticated as ${login}`));
|
|
165
|
-
return { token, login };
|
|
166
|
-
}
|
|
167
|
-
async function publishCommand(opts) {
|
|
168
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
169
|
-
const fetchImpl = getFetch(opts.fetchImpl);
|
|
170
|
-
const github = opts.github ?? new RealGitHubClient({ fetchImpl });
|
|
171
|
-
const { repo, directory, sha, branch } = await preconditions(cwd);
|
|
172
|
-
const [owner, repoName] = repo.split("/");
|
|
173
|
-
const validation = await runValidation(cwd);
|
|
174
|
-
if (!validation.ok) {
|
|
175
|
-
throw new CliError(`validation failed (${validation.errors.length} error(s)) \u2014 fix with \`runcastle validate\``, {
|
|
176
|
-
code: "E_VALIDATION",
|
|
177
|
-
details: validation.errors.map((e) => `[${e.code}] ${e.message}${e.file ? ` (${e.file})` : ""}`)
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
const manifest = await loadManifest(cwd);
|
|
181
|
-
if (!isJsonMode()) {
|
|
182
|
-
log(pc.green("\u2713 validation passed"));
|
|
183
|
-
log(pc.dim(` ${repo}${directory ? `/${directory}` : ""} @ ${sha.slice(0, 7)} (branch ${branch})`));
|
|
184
|
-
}
|
|
185
|
-
const { token } = await authenticate(github);
|
|
186
|
-
if (!await github.commitExists(owner, repoName, sha, token)) {
|
|
187
|
-
throw new CliError(`commit ${sha.slice(0, 7)} was not found on ${repo} \u2014 push it first`, { code: "E_SHA_MISSING" });
|
|
188
|
-
}
|
|
189
|
-
const body = PublishRequestSchema.parse({
|
|
190
|
-
manifest,
|
|
191
|
-
repo,
|
|
192
|
-
...directory ? { directory } : {},
|
|
193
|
-
commitSha: sha
|
|
194
|
-
});
|
|
195
|
-
const registry = new RegistryClient({ baseUrl: resolveRegistry(opts.registry), fetchImpl });
|
|
196
|
-
const result = await registry.publish(body, token);
|
|
197
|
-
const parsedVersion = VersionRecordSchema.safeParse(result.body);
|
|
198
|
-
const publishedVersion = parsedVersion.success ? parsedVersion.data.version : manifest.version;
|
|
199
|
-
const url = `${resolveRegistry(opts.registry)}/w/${owner}/${manifest.slug}`;
|
|
200
|
-
if (isJsonMode()) {
|
|
201
|
-
emitJson({ ok: true, status: result.status, url, slug: manifest.slug, version: publishedVersion, commitSha: sha });
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
log("");
|
|
205
|
-
log(pc.green(`\u2713 published ${manifest.slug}@${publishedVersion}`));
|
|
206
|
-
log(` ${pc.cyan(url)}`);
|
|
207
|
-
}
|
|
208
|
-
export {
|
|
209
|
-
publishCommand
|
|
210
|
-
};
|
|
211
|
-
//# sourceMappingURL=publish-W37Y6TO6.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/publish.ts","../src/lib/git.ts"],"sourcesContent":["// `runcastle publish` (spec §5.3). Preconditions → validate → device-flow OAuth\n// → resolve HEAD SHA → confirm SHA on remote → POST /api/workflows → print URL.\n\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n parseManifest,\n PublishRequestSchema,\n VersionRecordSchema,\n type Manifest,\n} from \"@castellan/schema\";\nimport { CliError } from \"../lib/errors.js\";\nimport { emitJson, isJsonMode, log, pc } from \"../lib/output.js\";\nimport { resolveRegistry, GITHUB_CLIENT_ID } from \"../lib/config.js\";\nimport { RegistryClient } from \"../lib/registry.js\";\nimport { RealGitHubClient, type GitHubClient } from \"../lib/github.js\";\nimport { getFetch, type FetchLike } from \"../lib/http.js\";\nimport { loadAuth, saveAuth } from \"../lib/auth.js\";\nimport { runValidation } from \"./validate.js\";\nimport * as git from \"../lib/git.js\";\n\nexport interface PublishOptions {\n registry?: string;\n json?: boolean;\n yes?: boolean;\n fetchImpl?: FetchLike;\n github?: GitHubClient;\n cwd?: string;\n}\n\nasync function preconditions(cwd: string): Promise<{ repo: string; directory: string; sha: string; branch: string }> {\n await git.assertGitAvailable(cwd);\n if (!(await git.isGitRepo(cwd))) {\n throw new CliError(\"not inside a git repository — publish must run in your workflow's repo\", { code: \"E_NOT_REPO\" });\n }\n if (!(await git.isClean(cwd))) {\n throw new CliError(\"working tree has uncommitted changes — commit or stash them, then publish\", {\n code: \"E_DIRTY_TREE\",\n details: [\"Publishing pins a commit SHA; only committed files are published.\"],\n });\n }\n const origin = await git.originUrl(cwd);\n if (!origin) {\n throw new CliError(\"no `origin` remote — add a github.com remote and push your branch\", { code: \"E_NO_ORIGIN\" });\n }\n const parsed = git.parseGitHubRemote(origin);\n if (!parsed) {\n throw new CliError(`origin is not a github.com remote (${origin})`, {\n code: \"E_NOT_GITHUB\",\n details: [\"Only GitHub repositories can be published in v1.\"],\n });\n }\n const branch = await git.currentBranch(cwd);\n const sha = await git.headSha(cwd);\n if (!(await git.headOnOrigin(cwd, branch, sha))) {\n throw new CliError(`HEAD (${sha.slice(0, 7)}) is not on origin — push branch \"${branch}\" first`, {\n code: \"E_NOT_PUSHED\",\n details: [`git push -u origin ${branch}`],\n });\n }\n const directory = await git.repoPrefix(cwd);\n return { repo: `${parsed.owner}/${parsed.repo}`, directory, sha, branch };\n}\n\nasync function loadManifest(cwd: string): Promise<Manifest> {\n const raw = await fsp.readFile(path.join(cwd, \"workflow.json\"), \"utf8\").catch(() => {\n throw new CliError(\"no workflow.json in the current directory\", { code: \"E_NO_MANIFEST\" });\n });\n const parsed = parseManifest(JSON.parse(raw));\n if (!parsed.ok) {\n throw new CliError(\"workflow.json is invalid — run `runcastle validate` for details\", { code: \"E_SCHEMA_INVALID\" });\n }\n return parsed.manifest;\n}\n\nasync function authenticate(github: GitHubClient): Promise<{ token: string; login: string }> {\n const cached = await loadAuth();\n if (cached) {\n return { token: cached.token, login: cached.login ?? \"(cached)\" };\n }\n const grant = await github.requestDeviceCode(GITHUB_CLIENT_ID);\n log(\"\");\n log(pc.bold(\"Authorize with GitHub:\"));\n log(` 1. Open ${pc.cyan(grant.verificationUri)}`);\n log(` 2. Enter the code: ${pc.bold(grant.userCode)}`);\n log(pc.dim(\" Waiting for authorization…\"));\n const token = await github.pollForToken(GITHUB_CLIENT_ID, grant);\n const login = await github.getViewerLogin(token);\n await saveAuth({ token, login, savedAt: new Date().toISOString() });\n log(pc.green(`✓ authenticated as ${login}`));\n return { token, login };\n}\n\nexport async function publishCommand(opts: PublishOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const fetchImpl = getFetch(opts.fetchImpl);\n const github = opts.github ?? new RealGitHubClient({ fetchImpl });\n\n // 1. Preconditions.\n const { repo, directory, sha, branch } = await preconditions(cwd);\n const [owner, repoName] = repo.split(\"/\") as [string, string];\n\n // 2. Validate.\n const validation = await runValidation(cwd);\n if (!validation.ok) {\n throw new CliError(`validation failed (${validation.errors.length} error(s)) — fix with \\`runcastle validate\\``, {\n code: \"E_VALIDATION\",\n details: validation.errors.map((e) => `[${e.code}] ${e.message}${e.file ? ` (${e.file})` : \"\"}`),\n });\n }\n const manifest = await loadManifest(cwd);\n\n if (!isJsonMode()) {\n log(pc.green(\"✓ validation passed\"));\n log(pc.dim(` ${repo}${directory ? `/${directory}` : \"\"} @ ${sha.slice(0, 7)} (branch ${branch})`));\n }\n\n // 3. Authenticate.\n const { token } = await authenticate(github);\n\n // 4. Confirm the SHA exists on the remote.\n if (!(await github.commitExists(owner, repoName, sha, token))) {\n throw new CliError(`commit ${sha.slice(0, 7)} was not found on ${repo} — push it first`, { code: \"E_SHA_MISSING\" });\n }\n\n // 5. POST /api/workflows.\n const body = PublishRequestSchema.parse({\n manifest,\n repo,\n ...(directory ? { directory } : {}),\n commitSha: sha,\n });\n const registry = new RegistryClient({ baseUrl: resolveRegistry(opts.registry), fetchImpl });\n const result = await registry.publish(body, token);\n\n // 7. Print the workflow URL.\n const parsedVersion = VersionRecordSchema.safeParse(result.body);\n const publishedVersion = parsedVersion.success ? parsedVersion.data.version : manifest.version;\n const url = `${resolveRegistry(opts.registry)}/w/${owner}/${manifest.slug}`;\n\n if (isJsonMode()) {\n emitJson({ ok: true, status: result.status, url, slug: manifest.slug, version: publishedVersion, commitSha: sha });\n return;\n }\n log(\"\");\n log(pc.green(`✓ published ${manifest.slug}@${publishedVersion}`));\n log(` ${pc.cyan(url)}`);\n}\n","// Git helpers for `publish` preconditions (spec §5.3.1). The only permitted\n// shell-out (§5.7); the binary is probed with a clear error if missing.\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { CliError } from \"./errors.js\";\n\nconst pExecFile = promisify(execFile);\n\nasync function git(args: string[], cwd: string): Promise<string> {\n try {\n const { stdout } = await pExecFile(\"git\", args, { cwd, windowsHide: true });\n return stdout.trim();\n } catch (e) {\n const err = e as NodeJS.ErrnoException & { stderr?: string };\n if (err.code === \"ENOENT\") {\n throw new CliError(\"`git` was not found on your PATH — install Git and try again\", {\n code: \"E_NO_GIT\",\n });\n }\n throw new CliError(`git ${args.join(\" \")} failed`, {\n code: \"E_GIT\",\n details: [err.stderr?.trim() || err.message],\n });\n }\n}\n\nexport async function assertGitAvailable(cwd: string): Promise<void> {\n await git([\"--version\"], cwd);\n}\n\nexport async function isGitRepo(cwd: string): Promise<boolean> {\n try {\n const out = await git([\"rev-parse\", \"--is-inside-work-tree\"], cwd);\n return out === \"true\";\n } catch {\n return false;\n }\n}\n\nexport async function headSha(cwd: string): Promise<string> {\n return git([\"rev-parse\", \"HEAD\"], cwd);\n}\n\nexport async function currentBranch(cwd: string): Promise<string> {\n return git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], cwd);\n}\n\n/** True when the working tree has no uncommitted changes. */\nexport async function isClean(cwd: string): Promise<boolean> {\n const out = await git([\"status\", \"--porcelain\"], cwd);\n return out.length === 0;\n}\n\n/** The `origin` remote URL, or null if there is no origin. */\nexport async function originUrl(cwd: string): Promise<string | null> {\n try {\n return await git([\"remote\", \"get-url\", \"origin\"], cwd);\n } catch {\n return null;\n }\n}\n\n/** True when the current HEAD SHA is present on origin (branch pushed). */\nexport async function headOnOrigin(cwd: string, branch: string, sha: string): Promise<boolean> {\n try {\n const out = await git([\"branch\", \"-r\", \"--contains\", sha], cwd);\n // Look for origin/<branch> or any origin/* ref containing the commit.\n return out.split(/\\r?\\n/).some((l) => l.trim().startsWith(`origin/${branch}`) || l.trim().startsWith(\"origin/\"));\n } catch {\n return false;\n }\n}\n\n/** Path of `cwd` relative to the repo root (POSIX, no trailing slash), or \"\". */\nexport async function repoPrefix(cwd: string): Promise<string> {\n const out = await git([\"rev-parse\", \"--show-prefix\"], cwd);\n return out.replace(/\\/+$/, \"\");\n}\n\n/** Parse \"owner/name\" from a github.com remote URL (ssh or https). Null otherwise. */\nexport function parseGitHubRemote(url: string): { owner: string; repo: string } | null {\n const cleaned = url.trim().replace(/\\.git$/, \"\");\n // https://github.com/owner/repo | git@github.com:owner/repo | ssh://git@github.com/owner/repo\n const m =\n /^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+)/.exec(cleaned) ||\n /^git@github\\.com:([^/]+)\\/([^/]+)/.exec(cleaned) ||\n /^ssh:\\/\\/git@github\\.com\\/([^/]+)\\/([^/]+)/.exec(cleaned);\n if (!m || !m[1] || !m[2]) return null;\n return { owner: m[1], repo: m[2] };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,OAAO,SAAS;AAChB,OAAO,UAAU;;;ACDjB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAG1B,IAAM,YAAY,UAAU,QAAQ;AAEpC,eAAe,IAAI,MAAgB,KAA8B;AAC/D,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,UAAU,OAAO,MAAM,EAAE,KAAK,aAAa,KAAK,CAAC;AAC1E,WAAO,OAAO,KAAK;AAAA,EACrB,SAAS,GAAG;AACV,UAAM,MAAM;AACZ,QAAI,IAAI,SAAS,UAAU;AACzB,YAAM,IAAI,SAAS,qEAAgE;AAAA,QACjF,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,UAAM,IAAI,SAAS,OAAO,KAAK,KAAK,GAAG,CAAC,WAAW;AAAA,MACjD,MAAM;AAAA,MACN,SAAS,CAAC,IAAI,QAAQ,KAAK,KAAK,IAAI,OAAO;AAAA,IAC7C,CAAC;AAAA,EACH;AACF;AAEA,eAAsB,mBAAmB,KAA4B;AACnE,QAAM,IAAI,CAAC,WAAW,GAAG,GAAG;AAC9B;AAEA,eAAsB,UAAU,KAA+B;AAC7D,MAAI;AACF,UAAM,MAAM,MAAM,IAAI,CAAC,aAAa,uBAAuB,GAAG,GAAG;AACjE,WAAO,QAAQ;AAAA,EACjB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,QAAQ,KAA8B;AAC1D,SAAO,IAAI,CAAC,aAAa,MAAM,GAAG,GAAG;AACvC;AAEA,eAAsB,cAAc,KAA8B;AAChE,SAAO,IAAI,CAAC,aAAa,gBAAgB,MAAM,GAAG,GAAG;AACvD;AAGA,eAAsB,QAAQ,KAA+B;AAC3D,QAAM,MAAM,MAAM,IAAI,CAAC,UAAU,aAAa,GAAG,GAAG;AACpD,SAAO,IAAI,WAAW;AACxB;AAGA,eAAsB,UAAU,KAAqC;AACnE,MAAI;AACF,WAAO,MAAM,IAAI,CAAC,UAAU,WAAW,QAAQ,GAAG,GAAG;AAAA,EACvD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAsB,aAAa,KAAa,QAAgB,KAA+B;AAC7F,MAAI;AACF,UAAM,MAAM,MAAM,IAAI,CAAC,UAAU,MAAM,cAAc,GAAG,GAAG,GAAG;AAE9D,WAAO,IAAI,MAAM,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,WAAW,UAAU,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,SAAS,CAAC;AAAA,EACjH,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAsB,WAAW,KAA8B;AAC7D,QAAM,MAAM,MAAM,IAAI,CAAC,aAAa,eAAe,GAAG,GAAG;AACzD,SAAO,IAAI,QAAQ,QAAQ,EAAE;AAC/B;AAGO,SAAS,kBAAkB,KAAqD;AACrF,QAAM,UAAU,IAAI,KAAK,EAAE,QAAQ,UAAU,EAAE;AAE/C,QAAM,IACJ,4CAA4C,KAAK,OAAO,KACxD,oCAAoC,KAAK,OAAO,KAChD,6CAA6C,KAAK,OAAO;AAC3D,MAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,EAAG,QAAO;AACjC,SAAO,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE;AACnC;;;AD5DA,eAAe,cAAc,KAAwF;AACnH,QAAU,mBAAmB,GAAG;AAChC,MAAI,CAAE,MAAU,UAAU,GAAG,GAAI;AAC/B,UAAM,IAAI,SAAS,+EAA0E,EAAE,MAAM,aAAa,CAAC;AAAA,EACrH;AACA,MAAI,CAAE,MAAU,QAAQ,GAAG,GAAI;AAC7B,UAAM,IAAI,SAAS,kFAA6E;AAAA,MAC9F,MAAM;AAAA,MACN,SAAS,CAAC,mEAAmE;AAAA,IAC/E,CAAC;AAAA,EACH;AACA,QAAM,SAAS,MAAU,UAAU,GAAG;AACtC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,SAAS,0EAAqE,EAAE,MAAM,cAAc,CAAC;AAAA,EACjH;AACA,QAAM,SAAa,kBAAkB,MAAM;AAC3C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,SAAS,sCAAsC,MAAM,KAAK;AAAA,MAClE,MAAM;AAAA,MACN,SAAS,CAAC,kDAAkD;AAAA,IAC9D,CAAC;AAAA,EACH;AACA,QAAM,SAAS,MAAU,cAAc,GAAG;AAC1C,QAAM,MAAM,MAAU,QAAQ,GAAG;AACjC,MAAI,CAAE,MAAU,aAAa,KAAK,QAAQ,GAAG,GAAI;AAC/C,UAAM,IAAI,SAAS,SAAS,IAAI,MAAM,GAAG,CAAC,CAAC,0CAAqC,MAAM,WAAW;AAAA,MAC/F,MAAM;AAAA,MACN,SAAS,CAAC,sBAAsB,MAAM,EAAE;AAAA,IAC1C,CAAC;AAAA,EACH;AACA,QAAM,YAAY,MAAU,WAAW,GAAG;AAC1C,SAAO,EAAE,MAAM,GAAG,OAAO,KAAK,IAAI,OAAO,IAAI,IAAI,WAAW,KAAK,OAAO;AAC1E;AAEA,eAAe,aAAa,KAAgC;AAC1D,QAAM,MAAM,MAAM,IAAI,SAAS,KAAK,KAAK,KAAK,eAAe,GAAG,MAAM,EAAE,MAAM,MAAM;AAClF,UAAM,IAAI,SAAS,6CAA6C,EAAE,MAAM,gBAAgB,CAAC;AAAA,EAC3F,CAAC;AACD,QAAM,SAAS,cAAc,KAAK,MAAM,GAAG,CAAC;AAC5C,MAAI,CAAC,OAAO,IAAI;AACd,UAAM,IAAI,SAAS,wEAAmE,EAAE,MAAM,mBAAmB,CAAC;AAAA,EACpH;AACA,SAAO,OAAO;AAChB;AAEA,eAAe,aAAa,QAAiE;AAC3F,QAAM,SAAS,MAAM,SAAS;AAC9B,MAAI,QAAQ;AACV,WAAO,EAAE,OAAO,OAAO,OAAO,OAAO,OAAO,SAAS,WAAW;AAAA,EAClE;AACA,QAAM,QAAQ,MAAM,OAAO,kBAAkB,gBAAgB;AAC7D,MAAI,EAAE;AACN,MAAI,GAAG,KAAK,wBAAwB,CAAC;AACrC,MAAI,aAAa,GAAG,KAAK,MAAM,eAAe,CAAC,EAAE;AACjD,MAAI,wBAAwB,GAAG,KAAK,MAAM,QAAQ,CAAC,EAAE;AACrD,MAAI,GAAG,IAAI,mCAA8B,CAAC;AAC1C,QAAM,QAAQ,MAAM,OAAO,aAAa,kBAAkB,KAAK;AAC/D,QAAM,QAAQ,MAAM,OAAO,eAAe,KAAK;AAC/C,QAAM,SAAS,EAAE,OAAO,OAAO,UAAS,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC;AAClE,MAAI,GAAG,MAAM,2BAAsB,KAAK,EAAE,CAAC;AAC3C,SAAO,EAAE,OAAO,MAAM;AACxB;AAEA,eAAsB,eAAe,MAAqC;AACxE,QAAM,MAAM,KAAK,OAAO,QAAQ,IAAI;AACpC,QAAM,YAAY,SAAS,KAAK,SAAS;AACzC,QAAM,SAAS,KAAK,UAAU,IAAI,iBAAiB,EAAE,UAAU,CAAC;AAGhE,QAAM,EAAE,MAAM,WAAW,KAAK,OAAO,IAAI,MAAM,cAAc,GAAG;AAChE,QAAM,CAAC,OAAO,QAAQ,IAAI,KAAK,MAAM,GAAG;AAGxC,QAAM,aAAa,MAAM,cAAc,GAAG;AAC1C,MAAI,CAAC,WAAW,IAAI;AAClB,UAAM,IAAI,SAAS,sBAAsB,WAAW,OAAO,MAAM,qDAAgD;AAAA,MAC/G,MAAM;AAAA,MACN,SAAS,WAAW,OAAO,IAAI,CAAC,MAAM,IAAI,EAAE,IAAI,KAAK,EAAE,OAAO,GAAG,EAAE,OAAO,KAAK,EAAE,IAAI,MAAM,EAAE,EAAE;AAAA,IACjG,CAAC;AAAA,EACH;AACA,QAAM,WAAW,MAAM,aAAa,GAAG;AAEvC,MAAI,CAAC,WAAW,GAAG;AACjB,QAAI,GAAG,MAAM,0BAAqB,CAAC;AACnC,QAAI,GAAG,IAAI,KAAK,IAAI,GAAG,YAAY,IAAI,SAAS,KAAK,EAAE,MAAM,IAAI,MAAM,GAAG,CAAC,CAAC,YAAY,MAAM,GAAG,CAAC;AAAA,EACpG;AAGA,QAAM,EAAE,MAAM,IAAI,MAAM,aAAa,MAAM;AAG3C,MAAI,CAAE,MAAM,OAAO,aAAa,OAAO,UAAU,KAAK,KAAK,GAAI;AAC7D,UAAM,IAAI,SAAS,UAAU,IAAI,MAAM,GAAG,CAAC,CAAC,qBAAqB,IAAI,yBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAAA,EACpH;AAGA,QAAM,OAAO,qBAAqB,MAAM;AAAA,IACtC;AAAA,IACA;AAAA,IACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,IACjC,WAAW;AAAA,EACb,CAAC;AACD,QAAM,WAAW,IAAI,eAAe,EAAE,SAAS,gBAAgB,KAAK,QAAQ,GAAG,UAAU,CAAC;AAC1F,QAAM,SAAS,MAAM,SAAS,QAAQ,MAAM,KAAK;AAGjD,QAAM,gBAAgB,oBAAoB,UAAU,OAAO,IAAI;AAC/D,QAAM,mBAAmB,cAAc,UAAU,cAAc,KAAK,UAAU,SAAS;AACvF,QAAM,MAAM,GAAG,gBAAgB,KAAK,QAAQ,CAAC,MAAM,KAAK,IAAI,SAAS,IAAI;AAEzE,MAAI,WAAW,GAAG;AAChB,aAAS,EAAE,IAAI,MAAM,QAAQ,OAAO,QAAQ,KAAK,MAAM,SAAS,MAAM,SAAS,kBAAkB,WAAW,IAAI,CAAC;AACjH;AAAA,EACF;AACA,MAAI,EAAE;AACN,MAAI,GAAG,MAAM,oBAAe,SAAS,IAAI,IAAI,gBAAgB,EAAE,CAAC;AAChE,MAAI,KAAK,GAAG,KAAK,GAAG,CAAC,EAAE;AACzB;","names":[]}
|