spawnfile 0.1.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/LICENSE +21 -0
- package/README.md +464 -0
- package/dist/.env.example +5 -0
- package/dist/Dockerfile +21 -0
- package/dist/auth/importers.d.ts +8 -0
- package/dist/auth/importers.js +93 -0
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.js +5 -0
- package/dist/auth/paths.d.ts +6 -0
- package/dist/auth/paths.js +18 -0
- package/dist/auth/profileStore.d.ts +10 -0
- package/dist/auth/profileStore.js +125 -0
- package/dist/auth/runtimeCredentials.d.ts +14 -0
- package/dist/auth/runtimeCredentials.js +76 -0
- package/dist/auth/types.d.ts +22 -0
- package/dist/auth/types.js +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/runCli.d.ts +27 -0
- package/dist/cli/runCli.js +314 -0
- package/dist/compiler/addProjectNode.d.ts +21 -0
- package/dist/compiler/addProjectNode.js +126 -0
- package/dist/compiler/agentSurfaces.d.ts +4 -0
- package/dist/compiler/agentSurfaces.js +70 -0
- package/dist/compiler/buildCompilePlan.d.ts +2 -0
- package/dist/compiler/buildCompilePlan.js +258 -0
- package/dist/compiler/buildProject.d.ts +20 -0
- package/dist/compiler/buildProject.js +52 -0
- package/dist/compiler/compilePlanHelpers.d.ts +7 -0
- package/dist/compiler/compilePlanHelpers.js +39 -0
- package/dist/compiler/compileProject.d.ts +11 -0
- package/dist/compiler/compileProject.js +182 -0
- package/dist/compiler/containerArtifacts.d.ts +4 -0
- package/dist/compiler/containerArtifacts.js +64 -0
- package/dist/compiler/containerArtifactsPlans.d.ts +4 -0
- package/dist/compiler/containerArtifactsPlans.js +154 -0
- package/dist/compiler/containerArtifactsRender.d.ts +6 -0
- package/dist/compiler/containerArtifactsRender.js +237 -0
- package/dist/compiler/containerArtifactsTypes.d.ts +42 -0
- package/dist/compiler/containerArtifactsTypes.js +1 -0
- package/dist/compiler/discordSurface.d.ts +4 -0
- package/dist/compiler/discordSurface.js +28 -0
- package/dist/compiler/executionDefaults.d.ts +2 -0
- package/dist/compiler/executionDefaults.js +9 -0
- package/dist/compiler/helpers.d.ts +7 -0
- package/dist/compiler/helpers.js +35 -0
- package/dist/compiler/index.d.ts +9 -0
- package/dist/compiler/index.js +9 -0
- package/dist/compiler/initProject.d.ts +9 -0
- package/dist/compiler/initProject.js +46 -0
- package/dist/compiler/modelAuth.d.ts +2 -0
- package/dist/compiler/modelAuth.js +17 -0
- package/dist/compiler/modelEnv.d.ts +10 -0
- package/dist/compiler/modelEnv.js +97 -0
- package/dist/compiler/runProject.d.ts +34 -0
- package/dist/compiler/runProject.js +197 -0
- package/dist/compiler/runProjectAuth.d.ts +9 -0
- package/dist/compiler/runProjectAuth.js +59 -0
- package/dist/compiler/surfaceSupport.d.ts +2 -0
- package/dist/compiler/surfaceSupport.js +13 -0
- package/dist/compiler/surfaces.d.ts +21 -0
- package/dist/compiler/surfaces.js +59 -0
- package/dist/compiler/syncProjectAuth.d.ts +7 -0
- package/dist/compiler/syncProjectAuth.js +65 -0
- package/dist/compiler/types.d.ts +134 -0
- package/dist/compiler/types.js +1 -0
- package/dist/compiler/updateProjectModels.d.ts +20 -0
- package/dist/compiler/updateProjectModels.js +181 -0
- package/dist/container/rootfs/var/lib/spawnfile/instances/picoclaw/agent-assistant/picoclaw/config.json +16 -0
- package/dist/container/rootfs/var/lib/spawnfile/instances/picoclaw/agent-assistant/picoclaw/workspace/AGENTS.md +1 -0
- package/dist/e2e/cli.d.ts +1 -0
- package/dist/e2e/cli.js +40 -0
- package/dist/e2e/dockerAuth.d.ts +18 -0
- package/dist/e2e/dockerAuth.js +212 -0
- package/dist/e2e/fixtures.d.ts +2 -0
- package/dist/e2e/fixtures.js +49 -0
- package/dist/e2e/index.d.ts +4 -0
- package/dist/e2e/index.js +4 -0
- package/dist/e2e/runtimePrompts.d.ts +13 -0
- package/dist/e2e/runtimePrompts.js +132 -0
- package/dist/e2e/scenarios.d.ts +3 -0
- package/dist/e2e/scenarios.js +84 -0
- package/dist/e2e/types.d.ts +35 -0
- package/dist/e2e/types.js +1 -0
- package/dist/entrypoint.sh +71 -0
- package/dist/filesystem/index.d.ts +2 -0
- package/dist/filesystem/index.js +2 -0
- package/dist/filesystem/io.d.ts +11 -0
- package/dist/filesystem/io.js +57 -0
- package/dist/filesystem/paths.d.ts +6 -0
- package/dist/filesystem/paths.js +30 -0
- package/dist/manifest/index.d.ts +5 -0
- package/dist/manifest/index.js +5 -0
- package/dist/manifest/loadManifest.d.ts +12 -0
- package/dist/manifest/loadManifest.js +208 -0
- package/dist/manifest/renderSpawnfile.d.ts +2 -0
- package/dist/manifest/renderSpawnfile.js +211 -0
- package/dist/manifest/scaffold.d.ts +16 -0
- package/dist/manifest/scaffold.js +41 -0
- package/dist/manifest/schemas.d.ts +989 -0
- package/dist/manifest/schemas.js +314 -0
- package/dist/manifest/skillFrontmatter.d.ts +5 -0
- package/dist/manifest/skillFrontmatter.js +32 -0
- package/dist/manifest/surfaceSchemas.d.ts +148 -0
- package/dist/manifest/surfaceSchemas.js +162 -0
- package/dist/report/createDiagnostic.d.ts +2 -0
- package/dist/report/createDiagnostic.js +4 -0
- package/dist/report/createReport.d.ts +2 -0
- package/dist/report/createReport.js +7 -0
- package/dist/report/index.d.ts +4 -0
- package/dist/report/index.js +4 -0
- package/dist/report/types.d.ts +50 -0
- package/dist/report/types.js +1 -0
- package/dist/report/writeReport.d.ts +2 -0
- package/dist/report/writeReport.js +9 -0
- package/dist/runtime/common.d.ts +13 -0
- package/dist/runtime/common.js +63 -0
- package/dist/runtime/container.d.ts +8 -0
- package/dist/runtime/container.js +67 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/install.d.ts +51 -0
- package/dist/runtime/install.js +167 -0
- package/dist/runtime/openclaw/adapter.d.ts +2 -0
- package/dist/runtime/openclaw/adapter.js +194 -0
- package/dist/runtime/openclaw/runAuth.d.ts +2 -0
- package/dist/runtime/openclaw/runAuth.js +125 -0
- package/dist/runtime/openclaw/scaffold-assets/AGENTS.md +120 -0
- package/dist/runtime/openclaw/scaffold-assets/CLAUDE.md +5 -0
- package/dist/runtime/openclaw/scaffold-assets/IDENTITY.md +23 -0
- package/dist/runtime/openclaw/scaffold-assets/SOUL.md +36 -0
- package/dist/runtime/openclaw/scaffold.d.ts +2 -0
- package/dist/runtime/openclaw/scaffold.js +28 -0
- package/dist/runtime/openclaw/surfaces.d.ts +5 -0
- package/dist/runtime/openclaw/surfaces.js +253 -0
- package/dist/runtime/picoclaw/adapter.d.ts +2 -0
- package/dist/runtime/picoclaw/adapter.js +204 -0
- package/dist/runtime/picoclaw/runAuth.d.ts +2 -0
- package/dist/runtime/picoclaw/runAuth.js +117 -0
- package/dist/runtime/picoclaw/scaffold-assets/AGENTS.md +3 -0
- package/dist/runtime/picoclaw/scaffold-assets/CLAUDE.md +5 -0
- package/dist/runtime/picoclaw/scaffold-assets/IDENTITY.md +3 -0
- package/dist/runtime/picoclaw/scaffold-assets/SOUL.md +3 -0
- package/dist/runtime/picoclaw/scaffold.d.ts +2 -0
- package/dist/runtime/picoclaw/scaffold.js +28 -0
- package/dist/runtime/picoclaw/surfaces.d.ts +5 -0
- package/dist/runtime/picoclaw/surfaces.js +111 -0
- package/dist/runtime/registry.d.ts +41 -0
- package/dist/runtime/registry.js +134 -0
- package/dist/runtime/scaffoldAssets.d.ts +1 -0
- package/dist/runtime/scaffoldAssets.js +2 -0
- package/dist/runtime/tinyclaw/adapter.d.ts +2 -0
- package/dist/runtime/tinyclaw/adapter.js +263 -0
- package/dist/runtime/tinyclaw/runAuth.d.ts +2 -0
- package/dist/runtime/tinyclaw/runAuth.js +22 -0
- package/dist/runtime/tinyclaw/scaffold-assets/AGENTS.md +160 -0
- package/dist/runtime/tinyclaw/scaffold-assets/CLAUDE.md +5 -0
- package/dist/runtime/tinyclaw/scaffold-assets/SOUL.md +177 -0
- package/dist/runtime/tinyclaw/scaffold.d.ts +2 -0
- package/dist/runtime/tinyclaw/scaffold.js +24 -0
- package/dist/runtime/tinyclaw/surfaces.d.ts +8 -0
- package/dist/runtime/tinyclaw/surfaces.js +86 -0
- package/dist/runtime/types.d.ts +87 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtimes/picoclaw/agents/assistant/config.json +16 -0
- package/dist/runtimes/picoclaw/agents/assistant/workspace/AGENTS.md +1 -0
- package/dist/shared/constants.d.ts +7 -0
- package/dist/shared/constants.js +7 -0
- package/dist/shared/errors.d.ts +6 -0
- package/dist/shared/errors.js +9 -0
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.js +3 -0
- package/dist/shared/types.d.ts +9 -0
- package/dist/shared/types.js +1 -0
- package/dist/spawnfile-report.json +71 -0
- package/package.json +41 -0
- package/runtimes.yaml +62 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { RuntimeLifecycleStatus } from "../shared/index.js";
|
|
2
|
+
import type { ModelAuthMethod } from "../shared/index.js";
|
|
3
|
+
export type CapabilityOutcome = "degraded" | "supported" | "unsupported";
|
|
4
|
+
export interface CapabilityReport {
|
|
5
|
+
key: string;
|
|
6
|
+
message: string;
|
|
7
|
+
outcome: CapabilityOutcome;
|
|
8
|
+
}
|
|
9
|
+
export interface DiagnosticReport {
|
|
10
|
+
level: "error" | "info" | "warn";
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ContainerRuntimeInstanceReport {
|
|
14
|
+
config_path: string;
|
|
15
|
+
home_path: string | null;
|
|
16
|
+
id: string;
|
|
17
|
+
model_auth_methods: Record<string, ModelAuthMethod>;
|
|
18
|
+
model_secrets_required: string[];
|
|
19
|
+
runtime: string;
|
|
20
|
+
}
|
|
21
|
+
export interface NodeReport {
|
|
22
|
+
capabilities: CapabilityReport[];
|
|
23
|
+
diagnostics: DiagnosticReport[];
|
|
24
|
+
id: string;
|
|
25
|
+
kind: "agent" | "team";
|
|
26
|
+
output_dir: string | null;
|
|
27
|
+
runtime: string | null;
|
|
28
|
+
runtime_ref: string | null;
|
|
29
|
+
runtime_status: RuntimeLifecycleStatus | null;
|
|
30
|
+
source: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ContainerReport {
|
|
33
|
+
dockerfile: string;
|
|
34
|
+
entrypoint: string;
|
|
35
|
+
env_example: string;
|
|
36
|
+
model_secrets_required: string[];
|
|
37
|
+
ports: number[];
|
|
38
|
+
runtime_instances: ContainerRuntimeInstanceReport[];
|
|
39
|
+
runtime_homes: string[];
|
|
40
|
+
runtime_secrets_required: string[];
|
|
41
|
+
runtimes_installed: string[];
|
|
42
|
+
secrets_required: string[];
|
|
43
|
+
}
|
|
44
|
+
export interface CompileReport {
|
|
45
|
+
container?: ContainerReport;
|
|
46
|
+
diagnostics: DiagnosticReport[];
|
|
47
|
+
nodes: NodeReport[];
|
|
48
|
+
root: string;
|
|
49
|
+
spawnfile_version: "0.1";
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { ensureDirectory, writeUtf8File } from "../filesystem/index.js";
|
|
3
|
+
import { REPORT_FILENAME } from "../shared/index.js";
|
|
4
|
+
export const writeCompileReport = async (outputDirectory, report) => {
|
|
5
|
+
await ensureDirectory(outputDirectory);
|
|
6
|
+
const reportPath = path.join(outputDirectory, REPORT_FILENAME);
|
|
7
|
+
await writeUtf8File(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
8
|
+
return reportPath;
|
|
9
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ResolvedAgentNode, ResolvedDocument, ResolvedSkill } from "../compiler/types.js";
|
|
2
|
+
import type { CapabilityReport, DiagnosticReport } from "../report/index.js";
|
|
3
|
+
import { EmittedFile } from "./types.js";
|
|
4
|
+
export declare const createCapability: (key: string, outcome: CapabilityReport["outcome"], message?: string) => CapabilityReport;
|
|
5
|
+
export declare const createDiagnostic: (level: DiagnosticReport["level"], message: string) => DiagnosticReport;
|
|
6
|
+
export declare const createDocumentFiles: (baseDirectory: string, documents: ResolvedDocument[]) => EmittedFile[];
|
|
7
|
+
export declare const createSkillFiles: (baseDirectory: string, skills: ResolvedSkill[]) => EmittedFile[];
|
|
8
|
+
export declare const createAgentCapabilities: (node: ResolvedAgentNode, options?: {
|
|
9
|
+
mcpOutcome?: CapabilityReport["outcome"];
|
|
10
|
+
sandboxOutcome?: CapabilityReport["outcome"];
|
|
11
|
+
subagentOutcome?: CapabilityReport["outcome"];
|
|
12
|
+
workspaceOutcome?: CapabilityReport["outcome"];
|
|
13
|
+
}) => CapabilityReport[];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const ROLE_FILE_NAMES = {
|
|
2
|
+
heartbeat: "HEARTBEAT.md",
|
|
3
|
+
identity: "IDENTITY.md",
|
|
4
|
+
memory: "MEMORY.md",
|
|
5
|
+
soul: "SOUL.md",
|
|
6
|
+
system: "AGENTS.md"
|
|
7
|
+
};
|
|
8
|
+
export const createCapability = (key, outcome, message = "") => ({
|
|
9
|
+
key,
|
|
10
|
+
message,
|
|
11
|
+
outcome
|
|
12
|
+
});
|
|
13
|
+
export const createDiagnostic = (level, message) => ({
|
|
14
|
+
level,
|
|
15
|
+
message
|
|
16
|
+
});
|
|
17
|
+
export const createDocumentFiles = (baseDirectory, documents) => documents.map((document) => ({
|
|
18
|
+
content: document.content,
|
|
19
|
+
path: document.role in ROLE_FILE_NAMES
|
|
20
|
+
? `${baseDirectory}/${ROLE_FILE_NAMES[document.role]}`
|
|
21
|
+
: `${baseDirectory}/extras/${document.role.replace(/^extras\./, "")}.md`
|
|
22
|
+
}));
|
|
23
|
+
export const createSkillFiles = (baseDirectory, skills) => skills.map((skill) => ({
|
|
24
|
+
content: skill.content,
|
|
25
|
+
path: `${baseDirectory}/${skill.name}/SKILL.md`
|
|
26
|
+
}));
|
|
27
|
+
export const createAgentCapabilities = (node, options = {}) => {
|
|
28
|
+
const capabilities = [];
|
|
29
|
+
for (const document of node.docs) {
|
|
30
|
+
capabilities.push(createCapability(`docs.${document.role}`, "supported"));
|
|
31
|
+
}
|
|
32
|
+
for (const skill of node.skills) {
|
|
33
|
+
capabilities.push(createCapability(`skills.${skill.name}`, "supported"));
|
|
34
|
+
}
|
|
35
|
+
for (const server of node.mcpServers) {
|
|
36
|
+
capabilities.push(createCapability(`mcp.${server.name}`, options.mcpOutcome ?? "supported"));
|
|
37
|
+
}
|
|
38
|
+
if (node.execution?.model) {
|
|
39
|
+
capabilities.push(createCapability("execution.model", "supported"));
|
|
40
|
+
}
|
|
41
|
+
if (node.execution?.workspace) {
|
|
42
|
+
capabilities.push(createCapability("execution.workspace", options.workspaceOutcome ?? "supported"));
|
|
43
|
+
}
|
|
44
|
+
if (node.execution?.sandbox) {
|
|
45
|
+
capabilities.push(createCapability("execution.sandbox", options.sandboxOutcome ?? "supported"));
|
|
46
|
+
}
|
|
47
|
+
if (node.surfaces?.discord) {
|
|
48
|
+
capabilities.push(createCapability("surfaces.discord", "supported"));
|
|
49
|
+
}
|
|
50
|
+
if (node.surfaces?.telegram) {
|
|
51
|
+
capabilities.push(createCapability("surfaces.telegram", "supported"));
|
|
52
|
+
}
|
|
53
|
+
if (node.surfaces?.whatsapp) {
|
|
54
|
+
capabilities.push(createCapability("surfaces.whatsapp", "supported"));
|
|
55
|
+
}
|
|
56
|
+
if (node.surfaces?.slack) {
|
|
57
|
+
capabilities.push(createCapability("surfaces.slack", "supported"));
|
|
58
|
+
}
|
|
59
|
+
if (node.subagents.length > 0) {
|
|
60
|
+
capabilities.push(createCapability("agent.subagents", options.subagentOutcome ?? "supported"));
|
|
61
|
+
}
|
|
62
|
+
return capabilities;
|
|
63
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const RUNTIME_INSTALL_ROOT = "/opt/spawnfile/runtime-installs";
|
|
2
|
+
export interface RuntimeInstallRecipe {
|
|
3
|
+
commands: string[];
|
|
4
|
+
copyCommands: string[];
|
|
5
|
+
runtimeName: string;
|
|
6
|
+
runtimeRoot: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const createRuntimeInstallRecipe: (runtimeName: string) => Promise<RuntimeInstallRecipe>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
2
|
+
import { resolveRuntimeInstallSelection } from "./install.js";
|
|
3
|
+
export const RUNTIME_INSTALL_ROOT = "/opt/spawnfile/runtime-installs";
|
|
4
|
+
const createGitHubReleaseUrl = (repository, tag, asset) => `https://github.com/${repository}/releases/download/${tag}/${asset}`;
|
|
5
|
+
const assertArtifactInstallSelection = (runtimeName, selection) => {
|
|
6
|
+
if (selection.kind !== "source_repo") {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
throw new SpawnfileError("runtime_error", `Runtime ${runtimeName} must use a compiled artifact install for generated containers`);
|
|
10
|
+
};
|
|
11
|
+
const createPicoClawArchiveInstallCommands = (runtimeRoot, selection) => [
|
|
12
|
+
`mkdir -p ${runtimeRoot}/bin`,
|
|
13
|
+
`arch="$(dpkg --print-architecture)" && case "$arch" in amd64) asset=${JSON.stringify(selection.versionedAssets.linux_amd64)} ;; arm64) asset=${JSON.stringify(selection.versionedAssets.linux_arm64)} ;; *) echo "Unsupported PicoClaw release architecture: $arch" >&2; exit 1 ;; esac && url="https://github.com/${selection.repository}/releases/download/${selection.tag}/$asset" && curl -fsSL -o /tmp/picoclaw.tar.gz "$url" && rm -rf /tmp/picoclaw-extract && mkdir -p /tmp/picoclaw-extract && tar -xzf /tmp/picoclaw.tar.gz -C /tmp/picoclaw-extract && binary_path="$(find /tmp/picoclaw-extract -type f -name ${JSON.stringify(selection.binaryName)} | head -n 1)" && [ -n "$binary_path" ] && install -m 0755 "$binary_path" ${runtimeRoot}/bin/${selection.binaryName} && ln -sf ${runtimeRoot}/bin/${selection.binaryName} /usr/local/bin/${selection.binaryName} && rm -rf /tmp/picoclaw.tar.gz /tmp/picoclaw-extract`
|
|
14
|
+
];
|
|
15
|
+
export const createRuntimeInstallRecipe = async (runtimeName) => {
|
|
16
|
+
const selection = await resolveRuntimeInstallSelection(runtimeName);
|
|
17
|
+
assertArtifactInstallSelection(runtimeName, selection);
|
|
18
|
+
const installRoot = `${RUNTIME_INSTALL_ROOT}/${runtimeName}`;
|
|
19
|
+
switch (runtimeName) {
|
|
20
|
+
case "openclaw": {
|
|
21
|
+
if (selection.kind !== "container_image" && selection.kind !== "npm") {
|
|
22
|
+
throw new SpawnfileError("runtime_error", `Runtime ${runtimeName} has no compiled artifact recipe for ${selection.kind}`);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
commands: selection.kind === "container_image"
|
|
26
|
+
? []
|
|
27
|
+
: [`npm install -g --omit=dev --no-fund --no-audit ${selection.packageName}@${selection.version}`],
|
|
28
|
+
copyCommands: selection.kind === "container_image"
|
|
29
|
+
? [`COPY --from=${selection.image}:${selection.tag} /app ${installRoot}`]
|
|
30
|
+
: [],
|
|
31
|
+
runtimeName,
|
|
32
|
+
runtimeRoot: selection.kind === "container_image"
|
|
33
|
+
? installRoot
|
|
34
|
+
: `/usr/local/lib/node_modules/${selection.packageName}`
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
case "picoclaw": {
|
|
38
|
+
if (selection.kind !== "github_release_archive") {
|
|
39
|
+
throw new SpawnfileError("runtime_error", `Runtime ${runtimeName} has no compiled artifact recipe for ${selection.kind}`);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
commands: createPicoClawArchiveInstallCommands(installRoot, selection),
|
|
43
|
+
copyCommands: [],
|
|
44
|
+
runtimeName,
|
|
45
|
+
runtimeRoot: installRoot
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
case "tinyclaw": {
|
|
49
|
+
if (selection.kind !== "github_release_bundle") {
|
|
50
|
+
throw new SpawnfileError("runtime_error", `Runtime ${runtimeName} has no compiled artifact recipe for ${selection.kind}`);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
commands: [
|
|
54
|
+
`mkdir -p ${installRoot}`,
|
|
55
|
+
`curl -fsSL ${JSON.stringify(createGitHubReleaseUrl(selection.repository, selection.tag, selection.asset))} | tar -xz --strip-components=1 -C ${installRoot}`,
|
|
56
|
+
`cd ${installRoot} && npm rebuild better-sqlite3 --silent`
|
|
57
|
+
],
|
|
58
|
+
copyCommands: [],
|
|
59
|
+
runtimeName,
|
|
60
|
+
runtimeRoot: installRoot
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/* c8 ignore next 5 -- compileable runtimes are exhaustively covered above */
|
|
64
|
+
default:
|
|
65
|
+
throw new SpawnfileError("runtime_error", `Runtime ${runtimeName} has no container install recipe`);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type RuntimeInstallSelection = {
|
|
2
|
+
ecosystem: "node";
|
|
3
|
+
image: string;
|
|
4
|
+
installHint: string;
|
|
5
|
+
kind: "container_image";
|
|
6
|
+
runtimeName: string;
|
|
7
|
+
runtimeRef: string;
|
|
8
|
+
selectionSource: "runtime_registry_install";
|
|
9
|
+
tag: string;
|
|
10
|
+
} | {
|
|
11
|
+
binaryName: string;
|
|
12
|
+
ecosystem: "go";
|
|
13
|
+
installHint: string;
|
|
14
|
+
kind: "github_release_archive";
|
|
15
|
+
repository: string;
|
|
16
|
+
runtimeName: string;
|
|
17
|
+
runtimeRef: string;
|
|
18
|
+
selectionSource: "runtime_registry_install";
|
|
19
|
+
tag: string;
|
|
20
|
+
versionedAssets: Record<string, string>;
|
|
21
|
+
} | {
|
|
22
|
+
asset: string;
|
|
23
|
+
ecosystem: "node";
|
|
24
|
+
installHint: string;
|
|
25
|
+
kind: "github_release_bundle";
|
|
26
|
+
repository: string;
|
|
27
|
+
runtimeName: string;
|
|
28
|
+
runtimeRef: string;
|
|
29
|
+
selectionSource: "runtime_registry_install";
|
|
30
|
+
tag: string;
|
|
31
|
+
} | {
|
|
32
|
+
ecosystem: "node";
|
|
33
|
+
installHint: string;
|
|
34
|
+
kind: "npm";
|
|
35
|
+
packageName: string;
|
|
36
|
+
runtimeName: string;
|
|
37
|
+
runtimeRef: string;
|
|
38
|
+
selectionSource: "runtime_registry_install";
|
|
39
|
+
version: string;
|
|
40
|
+
} | {
|
|
41
|
+
ecosystem: "go" | "node";
|
|
42
|
+
installHint: string;
|
|
43
|
+
kind: "source_repo";
|
|
44
|
+
remote: string;
|
|
45
|
+
runtimeName: string;
|
|
46
|
+
runtimeRef: string;
|
|
47
|
+
selectionSource: "runtime_registry_ref";
|
|
48
|
+
};
|
|
49
|
+
export declare const resolveRuntimeInstallSelection: (runtimeName: string) => Promise<RuntimeInstallSelection>;
|
|
50
|
+
export declare const listInstallSelectionRuntimes: () => Promise<string[]>;
|
|
51
|
+
export declare const assertInstallSelectionsCoverCompileableRuntimes: () => Promise<void>;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
2
|
+
import { assertRuntimeCanCompile, loadRuntimeRegistry } from "./registry.js";
|
|
3
|
+
const GITHUB_SSH_REMOTE_PATTERN = /^git@github\.com:(.+)$/;
|
|
4
|
+
const convertRemoteToHttps = (remote) => {
|
|
5
|
+
const githubSshMatch = remote.match(GITHUB_SSH_REMOTE_PATTERN);
|
|
6
|
+
if (githubSshMatch) {
|
|
7
|
+
return `https://github.com/${githubSshMatch[1]}`;
|
|
8
|
+
}
|
|
9
|
+
return remote.replace(/^git\+/, "");
|
|
10
|
+
};
|
|
11
|
+
const installHints = new Map([
|
|
12
|
+
[
|
|
13
|
+
"openclaw",
|
|
14
|
+
{
|
|
15
|
+
container_image: {
|
|
16
|
+
ecosystem: "node",
|
|
17
|
+
installHint: "Copy the pinned OpenClaw runtime files from the official container image."
|
|
18
|
+
},
|
|
19
|
+
github_release_archive: {
|
|
20
|
+
ecosystem: "node",
|
|
21
|
+
installHint: "Download the pinned OpenClaw artifact archive from the release."
|
|
22
|
+
},
|
|
23
|
+
github_release_bundle: {
|
|
24
|
+
ecosystem: "node",
|
|
25
|
+
installHint: "Download the pinned OpenClaw bundle artifact from the release."
|
|
26
|
+
},
|
|
27
|
+
npm: {
|
|
28
|
+
ecosystem: "node",
|
|
29
|
+
installHint: "Install the pinned OpenClaw package version from npm."
|
|
30
|
+
},
|
|
31
|
+
source_repo: {
|
|
32
|
+
ecosystem: "node",
|
|
33
|
+
installHint: "Checkout the pinned repo ref and install from the repository root."
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
"picoclaw",
|
|
39
|
+
{
|
|
40
|
+
container_image: {
|
|
41
|
+
ecosystem: "go",
|
|
42
|
+
installHint: "Copy the pinned PicoClaw runtime files from the official container image."
|
|
43
|
+
},
|
|
44
|
+
github_release_archive: {
|
|
45
|
+
ecosystem: "go",
|
|
46
|
+
installHint: "Download the pinned PicoClaw release archive for the target platform."
|
|
47
|
+
},
|
|
48
|
+
github_release_bundle: {
|
|
49
|
+
ecosystem: "node",
|
|
50
|
+
installHint: "Download the pinned PicoClaw bundle artifact from the release."
|
|
51
|
+
},
|
|
52
|
+
npm: {
|
|
53
|
+
ecosystem: "node",
|
|
54
|
+
installHint: "Install the pinned PicoClaw package version from npm."
|
|
55
|
+
},
|
|
56
|
+
source_repo: {
|
|
57
|
+
ecosystem: "go",
|
|
58
|
+
installHint: "Checkout the pinned repo ref and build/install from the repository root."
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
[
|
|
63
|
+
"tinyclaw",
|
|
64
|
+
{
|
|
65
|
+
container_image: {
|
|
66
|
+
ecosystem: "node",
|
|
67
|
+
installHint: "Copy the pinned TinyClaw runtime files from the official container image."
|
|
68
|
+
},
|
|
69
|
+
github_release_archive: {
|
|
70
|
+
ecosystem: "go",
|
|
71
|
+
installHint: "Download the pinned TinyClaw release archive for the target platform."
|
|
72
|
+
},
|
|
73
|
+
github_release_bundle: {
|
|
74
|
+
ecosystem: "node",
|
|
75
|
+
installHint: "Download the pinned TinyClaw bundle artifact from the release."
|
|
76
|
+
},
|
|
77
|
+
npm: {
|
|
78
|
+
ecosystem: "node",
|
|
79
|
+
installHint: "Install the pinned TinyClaw package version from npm."
|
|
80
|
+
},
|
|
81
|
+
source_repo: {
|
|
82
|
+
ecosystem: "node",
|
|
83
|
+
installHint: "Checkout the pinned repo ref and run the TinyAGI install flow from the repository root."
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
]);
|
|
88
|
+
export const resolveRuntimeInstallSelection = async (runtimeName) => {
|
|
89
|
+
const runtime = await assertRuntimeCanCompile(runtimeName);
|
|
90
|
+
const installProfile = installHints.get(runtime.name);
|
|
91
|
+
if (!installProfile) {
|
|
92
|
+
throw new SpawnfileError("runtime_error", `Runtime ${runtime.name} has no install selection profile`);
|
|
93
|
+
}
|
|
94
|
+
switch (runtime.install?.kind) {
|
|
95
|
+
case "container_image":
|
|
96
|
+
return {
|
|
97
|
+
ecosystem: "node",
|
|
98
|
+
image: runtime.install.image,
|
|
99
|
+
installHint: installProfile.container_image.installHint,
|
|
100
|
+
kind: "container_image",
|
|
101
|
+
runtimeName: runtime.name,
|
|
102
|
+
runtimeRef: runtime.ref,
|
|
103
|
+
selectionSource: "runtime_registry_install",
|
|
104
|
+
tag: runtime.install.tag
|
|
105
|
+
};
|
|
106
|
+
case "npm":
|
|
107
|
+
return {
|
|
108
|
+
ecosystem: "node",
|
|
109
|
+
installHint: installProfile.npm.installHint,
|
|
110
|
+
kind: "npm",
|
|
111
|
+
packageName: runtime.install.package,
|
|
112
|
+
runtimeName: runtime.name,
|
|
113
|
+
runtimeRef: runtime.ref,
|
|
114
|
+
selectionSource: "runtime_registry_install",
|
|
115
|
+
version: runtime.install.version
|
|
116
|
+
};
|
|
117
|
+
case "github_release_archive":
|
|
118
|
+
return {
|
|
119
|
+
ecosystem: "go",
|
|
120
|
+
installHint: installProfile.github_release_archive.installHint,
|
|
121
|
+
binaryName: runtime.install.binary,
|
|
122
|
+
kind: "github_release_archive",
|
|
123
|
+
repository: runtime.install.repository,
|
|
124
|
+
runtimeName: runtime.name,
|
|
125
|
+
runtimeRef: runtime.ref,
|
|
126
|
+
selectionSource: "runtime_registry_install",
|
|
127
|
+
tag: runtime.install.tag,
|
|
128
|
+
versionedAssets: runtime.install.assets
|
|
129
|
+
};
|
|
130
|
+
case "github_release_bundle":
|
|
131
|
+
return {
|
|
132
|
+
ecosystem: "node",
|
|
133
|
+
installHint: installProfile.github_release_bundle.installHint,
|
|
134
|
+
asset: runtime.install.asset,
|
|
135
|
+
kind: "github_release_bundle",
|
|
136
|
+
repository: runtime.install.repository,
|
|
137
|
+
runtimeName: runtime.name,
|
|
138
|
+
runtimeRef: runtime.ref,
|
|
139
|
+
selectionSource: "runtime_registry_install",
|
|
140
|
+
tag: runtime.install.tag
|
|
141
|
+
};
|
|
142
|
+
case "source_repo":
|
|
143
|
+
case undefined:
|
|
144
|
+
return {
|
|
145
|
+
...installProfile.source_repo,
|
|
146
|
+
kind: "source_repo",
|
|
147
|
+
remote: convertRemoteToHttps(runtime.remote),
|
|
148
|
+
runtimeName: runtime.name,
|
|
149
|
+
runtimeRef: runtime.ref,
|
|
150
|
+
selectionSource: "runtime_registry_ref"
|
|
151
|
+
};
|
|
152
|
+
default:
|
|
153
|
+
throw new SpawnfileError("runtime_error", `Unsupported install selection kind for ${runtime.name}`);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
export const listInstallSelectionRuntimes = async () => [...installHints.keys()].sort();
|
|
157
|
+
export const assertInstallSelectionsCoverCompileableRuntimes = async () => {
|
|
158
|
+
const compileableRuntimeNames = (await loadRuntimeRegistry())
|
|
159
|
+
.filter((entry) => entry.status === "active" || entry.status === "deprecated")
|
|
160
|
+
.map((entry) => entry.name)
|
|
161
|
+
.sort();
|
|
162
|
+
const coveredRuntimeNames = await listInstallSelectionRuntimes();
|
|
163
|
+
if (compileableRuntimeNames.length !== coveredRuntimeNames.length ||
|
|
164
|
+
compileableRuntimeNames.some((runtimeName, index) => runtimeName !== coveredRuntimeNames[index])) {
|
|
165
|
+
throw new SpawnfileError("runtime_error", "Install selection profiles do not cover all compileable runtimes");
|
|
166
|
+
}
|
|
167
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { listEffectiveExecutionModelTargets } from "../../compiler/modelEnv.js";
|
|
2
|
+
import { createAgentCapabilities, createDiagnostic, createDocumentFiles, createSkillFiles } from "../common.js";
|
|
3
|
+
import { SpawnfileError } from "../../shared/index.js";
|
|
4
|
+
import { prepareOpenClawRuntimeAuth } from "./runAuth.js";
|
|
5
|
+
import { createOpenClawAgentScaffold } from "./scaffold.js";
|
|
6
|
+
import { assertSupportedOpenClawSurfaces, buildOpenClawChannelConfig, buildOpenClawSurfaceEnvBindings } from "./surfaces.js";
|
|
7
|
+
const buildEnvSecretRef = (envName) => ({
|
|
8
|
+
id: envName,
|
|
9
|
+
provider: "default",
|
|
10
|
+
source: "env"
|
|
11
|
+
});
|
|
12
|
+
const createCustomProviderId = (provider) => `spawnfile-${provider}`;
|
|
13
|
+
const createOpenClawModelConfig = (node) => {
|
|
14
|
+
const [primary] = listEffectiveExecutionModelTargets(node.execution);
|
|
15
|
+
if (!primary) {
|
|
16
|
+
return { model: null };
|
|
17
|
+
}
|
|
18
|
+
if (primary.provider !== "custom" && primary.provider !== "local") {
|
|
19
|
+
return { model: `${primary.provider}/${primary.name}` };
|
|
20
|
+
}
|
|
21
|
+
const providerId = createCustomProviderId(primary.provider);
|
|
22
|
+
return {
|
|
23
|
+
model: `${providerId}/${primary.name}`,
|
|
24
|
+
providers: {
|
|
25
|
+
[providerId]: {
|
|
26
|
+
api: primary.endpoint?.compatibility === "anthropic"
|
|
27
|
+
? "anthropic-messages"
|
|
28
|
+
: "openai-completions",
|
|
29
|
+
...(primary.auth.method === "api_key" && primary.auth.key
|
|
30
|
+
? { apiKey: buildEnvSecretRef(primary.auth.key) }
|
|
31
|
+
: {}),
|
|
32
|
+
baseUrl: primary.endpoint?.base_url,
|
|
33
|
+
models: [
|
|
34
|
+
{
|
|
35
|
+
id: primary.name,
|
|
36
|
+
name: primary.name
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
const buildOpenClawConfig = (node) => {
|
|
44
|
+
const modelConfig = createOpenClawModelConfig(node);
|
|
45
|
+
const config = {
|
|
46
|
+
agents: {
|
|
47
|
+
defaults: {
|
|
48
|
+
...(modelConfig.model ? { model: modelConfig.model } : {}),
|
|
49
|
+
workspace: "<workspace-path>"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
gateway: {
|
|
53
|
+
auth: {
|
|
54
|
+
mode: "token"
|
|
55
|
+
},
|
|
56
|
+
bind: "lan",
|
|
57
|
+
controlUi: {
|
|
58
|
+
allowedOrigins: [
|
|
59
|
+
"http://127.0.0.1:<gateway-port>",
|
|
60
|
+
"http://localhost:<gateway-port>"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
mode: "local",
|
|
64
|
+
port: "<gateway-port>"
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const channels = buildOpenClawChannelConfig(node.surfaces);
|
|
68
|
+
if (Object.keys(channels).length > 0) {
|
|
69
|
+
config.channels = channels;
|
|
70
|
+
}
|
|
71
|
+
if (modelConfig.providers) {
|
|
72
|
+
config.models = {
|
|
73
|
+
providers: modelConfig.providers
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return `${JSON.stringify(config, null, 2)}\n`;
|
|
77
|
+
};
|
|
78
|
+
const createOpenClawStateFiles = () => [
|
|
79
|
+
{
|
|
80
|
+
// Pre-create the agent state tree so Docker bind mounts do not create root-owned parents.
|
|
81
|
+
content: "",
|
|
82
|
+
path: "home/.openclaw/agents/main/agent/.keep"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
// OpenClaw persists session state under agents/main/sessions at runtime.
|
|
86
|
+
content: "",
|
|
87
|
+
path: "home/.openclaw/agents/main/sessions/.keep"
|
|
88
|
+
}
|
|
89
|
+
];
|
|
90
|
+
const createContainerTargets = async (inputs) => inputs.map((input) => {
|
|
91
|
+
const agent = input.kind === "agent" ? input.value : null;
|
|
92
|
+
return {
|
|
93
|
+
configEnvBindings: buildOpenClawSurfaceEnvBindings(agent?.surfaces),
|
|
94
|
+
files: input.emittedFiles,
|
|
95
|
+
id: `${input.kind}-${input.slug}`,
|
|
96
|
+
sourceIds: [input.id]
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
export const openClawAdapter = {
|
|
100
|
+
assertSupportedModelTarget(target) {
|
|
101
|
+
if (target.endpoint) {
|
|
102
|
+
if (target.auth.method === "claude-code" || target.auth.method === "codex") {
|
|
103
|
+
throw new SpawnfileError("validation_error", `OpenClaw custom or local models do not support ${target.auth.method} auth`);
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (target.provider === "anthropic") {
|
|
108
|
+
if (target.auth.method === "api_key" || target.auth.method === "claude-code") {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else if (target.provider === "openai") {
|
|
113
|
+
if (target.auth.method === "api_key" || target.auth.method === "codex") {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (target.auth.method === "api_key" || target.auth.method === "none") {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
throw new SpawnfileError("validation_error", `OpenClaw does not support model auth method ${target.auth.method} for provider ${target.provider}`);
|
|
121
|
+
},
|
|
122
|
+
assertSupportedSurfaces(surfaces) {
|
|
123
|
+
assertSupportedOpenClawSurfaces(surfaces);
|
|
124
|
+
},
|
|
125
|
+
container: {
|
|
126
|
+
configFileName: "openclaw.json",
|
|
127
|
+
configPathEnv: "OPENCLAW_CONFIG_PATH",
|
|
128
|
+
env: [
|
|
129
|
+
{
|
|
130
|
+
description: "Gateway auth token required for non-loopback OpenClaw access",
|
|
131
|
+
name: "OPENCLAW_GATEWAY_TOKEN",
|
|
132
|
+
required: true
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
homeEnv: "OPENCLAW_HOME",
|
|
136
|
+
instancePaths: {
|
|
137
|
+
configPathTemplate: "<instance-root>/home/.openclaw/<config-file>",
|
|
138
|
+
homePathTemplate: "<instance-root>/home",
|
|
139
|
+
workspacePathTemplate: "<instance-root>/home/.openclaw/workspace"
|
|
140
|
+
},
|
|
141
|
+
port: 18789,
|
|
142
|
+
portEnv: "OPENCLAW_GATEWAY_PORT",
|
|
143
|
+
standaloneBaseImage: "node:24-bookworm-slim",
|
|
144
|
+
startCommand: [
|
|
145
|
+
"node",
|
|
146
|
+
"<runtime-root>/openclaw.mjs",
|
|
147
|
+
"gateway",
|
|
148
|
+
"--allow-unconfigured",
|
|
149
|
+
"--bind",
|
|
150
|
+
"lan",
|
|
151
|
+
"--port",
|
|
152
|
+
"<port>",
|
|
153
|
+
"--verbose"
|
|
154
|
+
],
|
|
155
|
+
systemDeps: ["bash", "ca-certificates", "curl", "git", "hostname", "openssl", "procps"]
|
|
156
|
+
},
|
|
157
|
+
async compileAgent(node) {
|
|
158
|
+
return {
|
|
159
|
+
capabilities: createAgentCapabilities(node, {
|
|
160
|
+
mcpOutcome: node.mcpServers.length > 0 ? "degraded" : "supported",
|
|
161
|
+
subagentOutcome: node.subagents.length > 0 ? "degraded" : "supported"
|
|
162
|
+
}),
|
|
163
|
+
diagnostics: [
|
|
164
|
+
...(node.subagents.length > 0
|
|
165
|
+
? [createDiagnostic("warn", "OpenClaw subagents lower to routed sessions in v0.1")]
|
|
166
|
+
: []),
|
|
167
|
+
...(node.mcpServers.length > 0
|
|
168
|
+
? [createDiagnostic("warn", "OpenClaw MCP goes through mcporter bridge; direct config may not apply")]
|
|
169
|
+
: [])
|
|
170
|
+
],
|
|
171
|
+
files: [
|
|
172
|
+
...createDocumentFiles("workspace", node.docs),
|
|
173
|
+
...createSkillFiles("workspace/skills", node.skills),
|
|
174
|
+
...createOpenClawStateFiles(),
|
|
175
|
+
{
|
|
176
|
+
content: buildOpenClawConfig(node),
|
|
177
|
+
path: "openclaw.json"
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
};
|
|
181
|
+
},
|
|
182
|
+
async createContainerTargets(inputs) {
|
|
183
|
+
return createContainerTargets(inputs);
|
|
184
|
+
},
|
|
185
|
+
name: "openclaw",
|
|
186
|
+
prepareRuntimeAuth: prepareOpenClawRuntimeAuth,
|
|
187
|
+
scaffoldAgentProject: createOpenClawAgentScaffold,
|
|
188
|
+
validateRuntimeOptions(options) {
|
|
189
|
+
if ("profile" in options && typeof options.profile !== "string") {
|
|
190
|
+
return [createDiagnostic("error", "OpenClaw runtime option profile must be a string")];
|
|
191
|
+
}
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
};
|