spawnfile 0.1.1 → 0.1.2

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.
Files changed (85) hide show
  1. package/README.md +79 -396
  2. package/dist/cli/modelCommands.d.ts +3 -0
  3. package/dist/cli/modelCommands.js +68 -0
  4. package/dist/cli/runCli.d.ts +6 -1
  5. package/dist/cli/runCli.js +12 -67
  6. package/dist/cli/runtimeCommands.d.ts +3 -0
  7. package/dist/cli/runtimeCommands.js +20 -0
  8. package/dist/cli/surfaceCommands.d.ts +3 -0
  9. package/dist/cli/surfaceCommands.js +98 -0
  10. package/dist/compiler/agentSurfaces.js +51 -5
  11. package/dist/compiler/buildCompilePlan.js +36 -40
  12. package/dist/compiler/buildCompilePlanRuntime.d.ts +14 -0
  13. package/dist/compiler/buildCompilePlanRuntime.js +39 -0
  14. package/dist/compiler/buildCompilePlanTeams.d.ts +5 -0
  15. package/dist/compiler/buildCompilePlanTeams.js +38 -0
  16. package/dist/compiler/compilePlanHelpers.js +4 -1
  17. package/dist/compiler/compileProject.js +62 -13
  18. package/dist/compiler/compileProjectSupport.d.ts +17 -0
  19. package/dist/compiler/compileProjectSupport.js +136 -0
  20. package/dist/compiler/containerArtifacts.d.ts +6 -1
  21. package/dist/compiler/containerArtifacts.js +26 -4
  22. package/dist/compiler/containerArtifactsPlans.js +16 -1
  23. package/dist/compiler/containerArtifactsRender.d.ts +4 -2
  24. package/dist/compiler/containerArtifactsRender.js +21 -126
  25. package/dist/compiler/containerArtifactsTypes.d.ts +7 -0
  26. package/dist/compiler/containerEntrypointRender.d.ts +12 -0
  27. package/dist/compiler/containerEntrypointRender.js +186 -0
  28. package/dist/compiler/index.d.ts +2 -0
  29. package/dist/compiler/index.js +2 -0
  30. package/dist/compiler/interactiveSurfaceScopes.d.ts +2 -0
  31. package/dist/compiler/interactiveSurfaceScopes.js +21 -0
  32. package/dist/compiler/moltnetArtifacts.d.ts +27 -0
  33. package/dist/compiler/moltnetArtifacts.js +204 -0
  34. package/dist/compiler/moltnetBinaries.d.ts +4 -0
  35. package/dist/compiler/moltnetBinaries.js +103 -0
  36. package/dist/compiler/moltnetClientConfig.d.ts +11 -0
  37. package/dist/compiler/moltnetClientConfig.js +89 -0
  38. package/dist/compiler/moltnetRepresentativeResolution.d.ts +16 -0
  39. package/dist/compiler/moltnetRepresentativeResolution.js +86 -0
  40. package/dist/compiler/moltnetResolution.d.ts +3 -0
  41. package/dist/compiler/moltnetResolution.js +201 -0
  42. package/dist/compiler/runProject.js +1 -1
  43. package/dist/compiler/surfaceDefinitions.d.ts +55 -0
  44. package/dist/compiler/surfaceDefinitions.js +204 -0
  45. package/dist/compiler/teamContextHelpers.d.ts +18 -0
  46. package/dist/compiler/teamContextHelpers.js +112 -0
  47. package/dist/compiler/teamContextSupport.d.ts +4 -0
  48. package/dist/compiler/teamContextSupport.js +264 -0
  49. package/dist/compiler/teamContextSupport.testHelpers.d.ts +16 -0
  50. package/dist/compiler/teamContextSupport.testHelpers.js +68 -0
  51. package/dist/compiler/teamContextTypes.d.ts +28 -0
  52. package/dist/compiler/teamContextTypes.js +1 -0
  53. package/dist/compiler/teamRoster.d.ts +12 -0
  54. package/dist/compiler/teamRoster.js +48 -0
  55. package/dist/compiler/teamRosterEntries.d.ts +13 -0
  56. package/dist/compiler/teamRosterEntries.js +230 -0
  57. package/dist/compiler/teamRosterTypes.d.ts +45 -0
  58. package/dist/compiler/teamRosterTypes.js +1 -0
  59. package/dist/compiler/types.d.ts +72 -6
  60. package/dist/compiler/updateProjectRuntime.d.ts +9 -0
  61. package/dist/compiler/updateProjectRuntime.js +67 -0
  62. package/dist/compiler/updateProjectSurfaces.d.ts +8 -0
  63. package/dist/compiler/updateProjectSurfaces.js +106 -0
  64. package/dist/manifest/loadManifest.js +4 -4
  65. package/dist/manifest/renderSpawnfile.js +74 -8
  66. package/dist/manifest/scaffold.js +1 -3
  67. package/dist/manifest/schemas.d.ts +227 -17
  68. package/dist/manifest/schemas.js +62 -20
  69. package/dist/manifest/surfaceSchemas.d.ts +154 -0
  70. package/dist/manifest/surfaceSchemas.js +77 -5
  71. package/dist/runtime/common.js +3 -0
  72. package/dist/runtime/openclaw/adapter.js +38 -5
  73. package/dist/runtime/openclaw/moltnet.d.ts +12 -0
  74. package/dist/runtime/openclaw/moltnet.js +124 -0
  75. package/dist/runtime/openclaw/surfaces.js +3 -0
  76. package/dist/runtime/picoclaw/adapter.js +27 -8
  77. package/dist/runtime/picoclaw/pico.d.ts +2 -0
  78. package/dist/runtime/picoclaw/pico.js +2 -0
  79. package/dist/runtime/picoclaw/surfaces.js +11 -0
  80. package/dist/runtime/tinyclaw/adapter.js +22 -8
  81. package/dist/runtime/tinyclaw/runAuth.js +28 -1
  82. package/dist/runtime/tinyclaw/surfaces.js +8 -0
  83. package/dist/runtime/types.d.ts +11 -0
  84. package/package.json +4 -2
  85. package/runtimes.yaml +4 -4
@@ -0,0 +1,124 @@
1
+ const MOLTNET_OPTION_KEYS = new Set([
2
+ "base_url",
3
+ "enabled",
4
+ "network_id",
5
+ "timeout_ms",
6
+ "token",
7
+ "token_secret"
8
+ ]);
9
+ const parseMoltnetOptions = (options) => {
10
+ const raw = options.moltnet;
11
+ if (raw === undefined) {
12
+ return { errors: [] };
13
+ }
14
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
15
+ return {
16
+ errors: [
17
+ "OpenClaw runtime option moltnet must be an object with enabled/base_url/network_id/timeout_ms/token/token_secret"
18
+ ]
19
+ };
20
+ }
21
+ const value = raw;
22
+ const errors = [];
23
+ for (const key of Object.keys(value)) {
24
+ if (!MOLTNET_OPTION_KEYS.has(key)) {
25
+ errors.push(`OpenClaw runtime option moltnet.${key} is unsupported`);
26
+ }
27
+ }
28
+ const parsed = {};
29
+ if ("enabled" in value) {
30
+ if (typeof value.enabled !== "boolean") {
31
+ errors.push("OpenClaw runtime option moltnet.enabled must be a boolean");
32
+ }
33
+ else {
34
+ parsed.enabled = value.enabled;
35
+ }
36
+ }
37
+ if ("base_url" in value) {
38
+ if (typeof value.base_url !== "string" || value.base_url.trim().length === 0) {
39
+ errors.push("OpenClaw runtime option moltnet.base_url must be a non-empty string");
40
+ }
41
+ else {
42
+ try {
43
+ const url = new URL(value.base_url);
44
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
45
+ errors.push("OpenClaw runtime option moltnet.base_url must use http or https");
46
+ }
47
+ else {
48
+ parsed.baseUrl = value.base_url.trim().replace(/\/+$/, "");
49
+ }
50
+ }
51
+ catch {
52
+ errors.push("OpenClaw runtime option moltnet.base_url must be a valid URL");
53
+ }
54
+ }
55
+ }
56
+ if ("network_id" in value) {
57
+ if (typeof value.network_id !== "string" || value.network_id.trim().length === 0) {
58
+ errors.push("OpenClaw runtime option moltnet.network_id must be a non-empty string");
59
+ }
60
+ else {
61
+ parsed.networkId = value.network_id.trim();
62
+ }
63
+ }
64
+ if ("timeout_ms" in value) {
65
+ if (typeof value.timeout_ms !== "number" ||
66
+ !Number.isInteger(value.timeout_ms) ||
67
+ value.timeout_ms <= 0) {
68
+ errors.push("OpenClaw runtime option moltnet.timeout_ms must be a positive integer");
69
+ }
70
+ else {
71
+ parsed.timeoutMs = value.timeout_ms;
72
+ }
73
+ }
74
+ if ("token" in value) {
75
+ if (typeof value.token !== "string" || value.token.trim().length === 0) {
76
+ errors.push("OpenClaw runtime option moltnet.token must be a non-empty string");
77
+ }
78
+ else {
79
+ parsed.token = value.token.trim();
80
+ }
81
+ }
82
+ if ("token_secret" in value) {
83
+ if (typeof value.token_secret !== "string" || value.token_secret.trim().length === 0) {
84
+ errors.push("OpenClaw runtime option moltnet.token_secret must be a non-empty string");
85
+ }
86
+ else {
87
+ parsed.tokenSecret = value.token_secret.trim();
88
+ }
89
+ }
90
+ if (parsed.enabled === true && !parsed.baseUrl) {
91
+ errors.push("OpenClaw runtime option moltnet.base_url is required when moltnet.enabled=true");
92
+ }
93
+ if (parsed.token && parsed.tokenSecret) {
94
+ errors.push("OpenClaw runtime option moltnet must not declare both token and token_secret");
95
+ }
96
+ return errors.length > 0 ? { errors } : { errors, value: parsed };
97
+ };
98
+ export const validateOpenClawMoltnetRuntimeOptions = (options) => parseMoltnetOptions(options).errors;
99
+ export const buildOpenClawMoltnetConfig = (options) => {
100
+ const parsed = parseMoltnetOptions(options);
101
+ if (parsed.errors.length > 0 || !parsed.value) {
102
+ return undefined;
103
+ }
104
+ const config = {
105
+ ...(parsed.value.enabled !== undefined ? { enabled: parsed.value.enabled } : {}),
106
+ ...(parsed.value.baseUrl ? { baseUrl: parsed.value.baseUrl } : {}),
107
+ ...(parsed.value.networkId ? { networkId: parsed.value.networkId } : {}),
108
+ ...(parsed.value.timeoutMs !== undefined ? { timeoutMs: parsed.value.timeoutMs } : {}),
109
+ ...(parsed.value.token ? { token: parsed.value.token } : {})
110
+ };
111
+ return Object.keys(config).length > 0 ? config : undefined;
112
+ };
113
+ export const buildOpenClawMoltnetEnvBindings = (options) => {
114
+ const parsed = parseMoltnetOptions(options);
115
+ if (parsed.errors.length > 0 || !parsed.value?.tokenSecret) {
116
+ return undefined;
117
+ }
118
+ return [
119
+ {
120
+ envName: parsed.value.tokenSecret,
121
+ jsonPath: "moltnet.token"
122
+ }
123
+ ];
124
+ };
@@ -246,6 +246,9 @@ export const buildOpenClawSurfaceEnvBindings = (surfaces) => {
246
246
  return bindings.length > 0 ? bindings : undefined;
247
247
  };
248
248
  export const assertSupportedOpenClawSurfaces = (surfaces) => {
249
+ if (surfaces?.http) {
250
+ throw new SpawnfileError("validation_error", "OpenClaw does not support portable HTTP surfaces in Spawnfile v0.1");
251
+ }
249
252
  const discordAccess = surfaces?.discord?.access;
250
253
  if (discordAccess && discordAccess.mode === "allowlist" && discordAccess.channels.length > 0 && discordAccess.guilds.length !== 1) {
251
254
  throw new SpawnfileError("validation_error", "OpenClaw Discord channel allowlists require exactly one guild id");
@@ -4,6 +4,7 @@ import { createAgentCapabilities, createDiagnostic, createDocumentFiles, createS
4
4
  import { preparePicoClawRuntimeAuth } from "./runAuth.js";
5
5
  import { createPicoClawAgentScaffold } from "./scaffold.js";
6
6
  import { assertSupportedPicoClawSurfaces, buildPicoClawChannelConfig, buildPicoClawSurfaceEnvBindings } from "./surfaces.js";
7
+ import { PICOCLAW_GATEWAY_BASE_PORT, PICOCLAW_INTERNAL_PICO_TOKEN } from "./pico.js";
7
8
  const formatModelName = (node) => {
8
9
  const primary = node.execution?.model?.primary;
9
10
  if (!primary)
@@ -79,6 +80,21 @@ const buildMcpServers = (servers) => {
79
80
  }
80
81
  return result;
81
82
  };
83
+ const buildPicoClawToolsConfig = (node) => {
84
+ const tools = {};
85
+ if (node.surfaces?.moltnet) {
86
+ tools.exec = {
87
+ enabled: true
88
+ };
89
+ }
90
+ if (node.mcpServers.length > 0) {
91
+ tools.mcp = {
92
+ enabled: true,
93
+ servers: buildMcpServers(node.mcpServers)
94
+ };
95
+ }
96
+ return Object.keys(tools).length > 0 ? tools : undefined;
97
+ };
82
98
  const buildPicoClawConfig = (node) => {
83
99
  const modelName = formatModelName(node);
84
100
  const restrictToWorkspace = node.runtime.options.restrict_to_workspace ?? true;
@@ -98,13 +114,9 @@ const buildPicoClawConfig = (node) => {
98
114
  if (Object.keys(channels).length > 0) {
99
115
  config.channels = channels;
100
116
  }
101
- if (node.mcpServers.length > 0) {
102
- config.tools = {
103
- mcp: {
104
- enabled: true,
105
- servers: buildMcpServers(node.mcpServers)
106
- }
107
- };
117
+ const tools = buildPicoClawToolsConfig(node);
118
+ if (tools) {
119
+ config.tools = tools;
108
120
  }
109
121
  return `${JSON.stringify(config, null, 2)}\n`;
110
122
  };
@@ -163,15 +175,22 @@ export const picoClawAdapter = {
163
175
  homePathTemplate: "<instance-root>/picoclaw",
164
176
  workspacePathTemplate: "<instance-root>/picoclaw/workspace"
165
177
  },
166
- port: 18790,
178
+ port: PICOCLAW_GATEWAY_BASE_PORT,
167
179
  portEnv: "PICOCLAW_GATEWAY_PORT",
168
180
  standaloneBaseImage: "debian:bookworm-slim",
169
181
  startCommand: ["picoclaw", "gateway", "--allow-empty"],
170
182
  staticEnv: {
183
+ PICOCLAW_CHANNELS_PICO_TOKEN: PICOCLAW_INTERNAL_PICO_TOKEN,
171
184
  PICOCLAW_GATEWAY_HOST: "0.0.0.0"
172
185
  },
173
186
  systemDeps: ["bash", "ca-certificates", "curl", "nodejs", "npm", "tar"]
174
187
  },
188
+ systemInstructionSurface: {
189
+ placement: "append_pointer",
190
+ resolvePath() {
191
+ return "workspace/AGENTS.md";
192
+ }
193
+ },
175
194
  async compileAgent(node) {
176
195
  return {
177
196
  capabilities: createAgentCapabilities(node),
@@ -0,0 +1,2 @@
1
+ export declare const PICOCLAW_GATEWAY_BASE_PORT = 18990;
2
+ export declare const PICOCLAW_INTERNAL_PICO_TOKEN = "spawnfile-internal-pico";
@@ -0,0 +1,2 @@
1
+ export const PICOCLAW_GATEWAY_BASE_PORT = 18990;
2
+ export const PICOCLAW_INTERNAL_PICO_TOKEN = "spawnfile-internal-pico";
@@ -1,4 +1,5 @@
1
1
  import { SpawnfileError } from "../../shared/index.js";
2
+ import { PICOCLAW_INTERNAL_PICO_TOKEN } from "./pico.js";
2
3
  export const buildPicoClawChannelConfig = (surfaces) => {
3
4
  if (!surfaces) {
4
5
  return {};
@@ -41,6 +42,13 @@ export const buildPicoClawChannelConfig = (surfaces) => {
41
42
  }
42
43
  };
43
44
  }
45
+ if (surfaces.moltnet) {
46
+ channels.pico = {
47
+ allow_token_query: true,
48
+ enabled: true,
49
+ token: PICOCLAW_INTERNAL_PICO_TOKEN
50
+ };
51
+ }
44
52
  return channels;
45
53
  };
46
54
  export const buildPicoClawSurfaceEnvBindings = (surfaces) => {
@@ -72,6 +80,9 @@ export const buildPicoClawSurfaceEnvBindings = (surfaces) => {
72
80
  return bindings.length > 0 ? bindings : undefined;
73
81
  };
74
82
  export const assertSupportedPicoClawSurfaces = (surfaces) => {
83
+ if (surfaces?.http) {
84
+ throw new SpawnfileError("validation_error", "PicoClaw does not support portable HTTP surfaces in Spawnfile v0.1");
85
+ }
75
86
  const discordAccess = surfaces?.discord?.access;
76
87
  if (discordAccess) {
77
88
  if (discordAccess.mode === "pairing") {
@@ -5,6 +5,7 @@ import { prepareTinyClawRuntimeAuth } from "./runAuth.js";
5
5
  import { createTinyClawAgentScaffold } from "./scaffold.js";
6
6
  import { assertSupportedTinyClawSurfaces, buildTinyClawChannels, resolveTinyClawSurfaceTokenBindings } from "./surfaces.js";
7
7
  const WORKSPACE_PLACEHOLDER = "<workspace-path>";
8
+ const SUPPORTED_TINYCLAW_OPENAI_MODEL_PREFIXES = ["gpt-5"];
8
9
  const TINYCLAW_START_SCRIPT = `
9
10
  set -euo pipefail
10
11
  PIDS=()
@@ -55,6 +56,7 @@ for pid in "\${PIDS[@]}"; do
55
56
  done
56
57
  exit "$status"
57
58
  `.trim();
59
+ const isSupportedTinyClawOpenAiModel = (modelName) => SUPPORTED_TINYCLAW_OPENAI_MODEL_PREFIXES.some((prefix) => modelName.startsWith(prefix));
58
60
  const buildTinyClawSettings = (node) => {
59
61
  const [primary] = listEffectiveExecutionModelTargets(node.execution);
60
62
  const agentEntry = {
@@ -168,12 +170,15 @@ export const tinyClawAdapter = {
168
170
  throw new SpawnfileError("validation_error", "TinyClaw custom or local endpoints are not supported in Spawnfile v0.1");
169
171
  }
170
172
  if (target.provider === "anthropic") {
171
- if (target.auth.method === "claude-code") {
173
+ if (target.auth.method === "claude-code" || target.auth.method === "api_key") {
172
174
  return;
173
175
  }
174
176
  }
175
177
  else if (target.provider === "openai") {
176
- if (target.auth.method === "codex") {
178
+ if (!isSupportedTinyClawOpenAiModel(target.name)) {
179
+ throw new SpawnfileError("validation_error", `TinyClaw OpenAI models must use the runtime's Codex/GPT-5 path; received ${target.name}`);
180
+ }
181
+ if (target.auth.method === "codex" || target.auth.method === "api_key") {
177
182
  return;
178
183
  }
179
184
  }
@@ -190,11 +195,11 @@ export const tinyClawAdapter = {
190
195
  configEnvBindings: [
191
196
  {
192
197
  envName: "ANTHROPIC_API_KEY",
193
- jsonPath: "models.anthropic.auth_token"
198
+ jsonPath: "models.anthropic.api_key"
194
199
  },
195
200
  {
196
201
  envName: "OPENAI_API_KEY",
197
- jsonPath: "models.openai.auth_token"
202
+ jsonPath: "models.openai.api_key"
198
203
  }
199
204
  ],
200
205
  homeEnv: "TINYAGI_HOME",
@@ -210,6 +215,12 @@ export const tinyClawAdapter = {
210
215
  startCommand: ["bash", "-lc", TINYCLAW_START_SCRIPT],
211
216
  systemDeps: ["bash", "ca-certificates", "curl", "g++", "make", "python3", "tar"]
212
217
  },
218
+ systemInstructionSurface: {
219
+ placement: "append_pointer",
220
+ resolvePath({ node }) {
221
+ return `workspace/${node.name}/AGENTS.md`;
222
+ }
223
+ },
213
224
  async compileAgent(node) {
214
225
  return {
215
226
  capabilities: createAgentCapabilities(node, {
@@ -234,17 +245,20 @@ export const tinyClawAdapter = {
234
245
  const agentIds = node.members
235
246
  .filter((member) => member.kind === "agent")
236
247
  .map((member) => member.id);
248
+ const leadAgent = node.lead
249
+ ? node.members.find((member) => member.id === node.lead && member.kind === "agent")
250
+ : null;
237
251
  const teamConfig = {
238
252
  name: node.name,
239
253
  agents: agentIds,
240
- leader_agent: node.structure.leader ?? agentIds[0] ?? "leader"
254
+ ...(leadAgent ? { leader_agent: leadAgent.id } : {})
241
255
  };
242
256
  return {
243
257
  capabilities: [
244
258
  createCapability("team.members", "supported"),
245
- createCapability("team.structure.mode", node.structure.mode === "hierarchical" ? "supported" : "degraded", "TinyClaw only supports leader-led teams"),
246
- createCapability("team.structure.leader", node.structure.leader ? "supported" : "degraded", "TinyClaw requires a leader_agent"),
247
- createCapability("team.structure.external", "degraded", "TinyClaw does not enforce external boundary"),
259
+ createCapability("team.mode", node.mode === "hierarchical" ? "supported" : "degraded", "TinyClaw only supports leader-led teams"),
260
+ createCapability("team.lead", leadAgent ? "supported" : "degraded", leadAgent ? "" : "TinyClaw requires a concrete leader_agent"),
261
+ createCapability("team.external", "degraded", "TinyClaw does not enforce external boundary"),
248
262
  createCapability("team.shared", "supported"),
249
263
  createCapability("team.nested", "degraded", "TinyClaw nested teams flatten in v0.1")
250
264
  ],
@@ -1,4 +1,20 @@
1
+ import path from "node:path";
1
2
  import { loadImportedClaudeCodeCredential, loadImportedCodexCredential } from "../../auth/index.js";
3
+ import { ensureDirectory, readUtf8File, writeUtf8File } from "../../filesystem/index.js";
4
+ const resolveRootfsSourcePath = (outputDirectory, containerPath) => path.join(outputDirectory, "container", "rootfs", ...path.posix.relative("/", containerPath).split("/"));
5
+ const createMountArgs = (hostPath, containerPath) => [
6
+ "-v",
7
+ `${hostPath}:${containerPath}`
8
+ ];
9
+ const writeRuntimeAuthFile = async (input, relativePath, content) => {
10
+ const hostPath = path.join(input.tempRoot, "runtime-auth", "tinyclaw", input.instance.id, ...relativePath.split("/"));
11
+ await ensureDirectory(path.dirname(hostPath));
12
+ await writeUtf8File(hostPath, content);
13
+ return hostPath;
14
+ };
15
+ const writePatchedSettings = async (input, config) => {
16
+ return writeRuntimeAuthFile(input, "settings.json", `${JSON.stringify(config, null, 2)}\n`);
17
+ };
2
18
  export const prepareTinyClawRuntimeAuth = async (input) => {
3
19
  const coveredModelSecrets = [];
4
20
  const claudeCode = input.authProfile.imports["claude-code"]
@@ -15,8 +31,19 @@ export const prepareTinyClawRuntimeAuth = async (input) => {
15
31
  codex) {
16
32
  coveredModelSecrets.push("OPENAI_API_KEY");
17
33
  }
34
+ const sourceConfig = JSON.parse(await readUtf8File(resolveRootfsSourcePath(input.outputDirectory, input.instance.config_path)));
35
+ const patchedConfigPath = await writePatchedSettings(input, sourceConfig);
36
+ const mountArgs = createMountArgs(patchedConfigPath, input.instance.config_path);
37
+ if (input.instance.home_path && input.instance.model_auth_methods.anthropic === "claude-code" && claudeCode) {
38
+ const mountedClaudePath = await writeRuntimeAuthFile(input, ".claude/.credentials.json", await readUtf8File(path.join(input.authProfile.imports["claude-code"].path, ".credentials.json")));
39
+ mountArgs.push(...createMountArgs(mountedClaudePath, path.posix.join(input.instance.home_path, ".claude", ".credentials.json")));
40
+ }
41
+ if (input.instance.home_path && input.instance.model_auth_methods.openai === "codex" && codex) {
42
+ const mountedAuthPath = await writeRuntimeAuthFile(input, ".codex/auth.json", await readUtf8File(path.join(input.authProfile.imports.codex.path, "auth.json")));
43
+ mountArgs.push(...createMountArgs(mountedAuthPath, path.posix.join(input.instance.home_path, ".codex", "auth.json")));
44
+ }
18
45
  return {
19
46
  coveredModelSecrets,
20
- mountArgs: []
47
+ mountArgs
21
48
  };
22
49
  };
@@ -1,3 +1,4 @@
1
+ import { listInteractiveSurfaceScopes } from "../../compiler/interactiveSurfaceScopes.js";
1
2
  import { SpawnfileError } from "../../shared/index.js";
2
3
  export const buildTinyClawChannels = (surfaces) => {
3
4
  const enabled = [];
@@ -51,6 +52,9 @@ export const resolveTinyClawSurfaceTokenBindings = (inputs) => {
51
52
  return bindings.length > 0 ? bindings : undefined;
52
53
  };
53
54
  export const assertSupportedTinyClawSurfaces = (surfaces) => {
55
+ if (surfaces?.http) {
56
+ throw new SpawnfileError("validation_error", "TinyClaw does not support portable HTTP surfaces in Spawnfile v0.1");
57
+ }
54
58
  const discordAccess = surfaces?.discord?.access;
55
59
  if (discordAccess) {
56
60
  if (discordAccess.mode !== "pairing") {
@@ -83,4 +87,8 @@ export const assertSupportedTinyClawSurfaces = (surfaces) => {
83
87
  if (surfaces?.slack) {
84
88
  throw new SpawnfileError("validation_error", "TinyClaw does not support Slack in Spawnfile v0.1");
85
89
  }
90
+ const interactiveScopes = listInteractiveSurfaceScopes(surfaces);
91
+ if (interactiveScopes.length > 1) {
92
+ throw new SpawnfileError("validation_error", `TinyClaw preserves only one interactive conversation scope in Spawnfile v0.1; declared ${interactiveScopes.join(", ")}`);
93
+ }
86
94
  };
@@ -5,6 +5,7 @@ import type { CapabilityReport, DiagnosticReport } from "../report/index.js";
5
5
  import type { ContainerRuntimeInstanceReport } from "../report/index.js";
6
6
  export interface EmittedFile {
7
7
  content: string;
8
+ mode?: number;
8
9
  path: string;
9
10
  }
10
11
  export interface RuntimeAgentScaffold {
@@ -51,6 +52,7 @@ export interface RuntimeContainerMeta {
51
52
  instancePaths: RuntimeContainerInstancePaths;
52
53
  globalNpmPackages?: string[];
53
54
  port?: number;
55
+ portStride?: number;
54
56
  portEnv?: string;
55
57
  standaloneBaseImage: string;
56
58
  startCommand: string[];
@@ -68,6 +70,14 @@ export interface RuntimeAuthPreparationResult {
68
70
  coveredModelSecrets: string[];
69
71
  mountArgs: string[];
70
72
  }
73
+ export interface RuntimeSystemInstructionSurfaceInput {
74
+ node: ResolvedAgentNode;
75
+ }
76
+ export type RuntimeSystemInstructionPlacement = "append_pointer" | "append_inline" | "replace_generated_block";
77
+ export interface RuntimeSystemInstructionSurface {
78
+ placement: RuntimeSystemInstructionPlacement;
79
+ resolvePath(input: RuntimeSystemInstructionSurfaceInput): string;
80
+ }
71
81
  export interface AdapterCompileResult {
72
82
  capabilities: CapabilityReport[];
73
83
  diagnostics: DiagnosticReport[];
@@ -83,5 +93,6 @@ export interface RuntimeAdapter {
83
93
  name: string;
84
94
  prepareRuntimeAuth?(input: RuntimeAuthPreparationInput): Promise<RuntimeAuthPreparationResult>;
85
95
  scaffoldAgentProject?(): RuntimeAgentScaffold;
96
+ systemInstructionSurface?: RuntimeSystemInstructionSurface;
86
97
  validateRuntimeOptions?(options: Record<string, unknown>): DiagnosticReport[];
87
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spawnfile",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Canonical source compiler for autonomous agents and teams.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -28,6 +28,7 @@
28
28
  "runtimes": "./scripts/runtimes.sh",
29
29
  "runtimes:sync": "./scripts/runtimes.sh && ./scripts/blueprints.sh",
30
30
  "test:e2e:docker-auth": "tsx src/e2e/cli.ts",
31
+ "test:e2e:moltnet-team-chat": "tsx src/e2e/cli.ts moltnet-team-chat",
31
32
  "test": "vitest run",
32
33
  "typecheck": "tsc --project tsconfig.json --noEmit"
33
34
  },
@@ -42,5 +43,6 @@
42
43
  "tsx": "^4.20.6",
43
44
  "typescript": "^5.9.3",
44
45
  "vitest": "^3.2.4"
45
- }
46
+ },
47
+ "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be"
46
48
  }
package/runtimes.yaml CHANGED
@@ -26,12 +26,12 @@ runtimes:
26
26
 
27
27
  picoclaw:
28
28
  remote: git@github.com:sipeed/picoclaw.git
29
- ref: v0.2.3
29
+ ref: v0.2.5
30
30
  default_branch: main
31
31
  install:
32
32
  kind: github_release_archive
33
33
  repository: sipeed/picoclaw
34
- tag: v0.2.3
34
+ tag: v0.2.5
35
35
  binary: picoclaw
36
36
  assets:
37
37
  linux_amd64: picoclaw_Linux_x86_64.tar.gz
@@ -40,12 +40,12 @@ runtimes:
40
40
 
41
41
  tinyclaw:
42
42
  remote: git@github.com:TinyAGI/tinyclaw.git
43
- ref: v0.0.15
43
+ ref: v0.0.20
44
44
  default_branch: main
45
45
  install:
46
46
  kind: github_release_bundle
47
47
  repository: TinyAGI/tinyagi
48
- tag: v0.0.15
48
+ tag: v0.0.20
49
49
  asset: tinyagi-bundle.tar.gz
50
50
  status: active
51
51