@floomhq/floom 5.0.7 → 5.0.8
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 +5 -1
- package/dist/cli.js +18 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/workers.d.ts +12 -1
- package/dist/commands/workers.js +81 -1
- package/dist/commands/workers.js.map +1 -1
- package/dist/lib/worker-authoring.d.ts +55 -0
- package/dist/lib/worker-authoring.js +472 -0
- package/dist/lib/worker-authoring.js.map +1 -0
- package/dist/server.js +56 -10
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { parse as parseYaml } from "yaml";
|
|
2
|
+
const SUPPORTED_RUNTIMES = new Set(["python311", "node22", "bash", "skill", "none"]);
|
|
3
|
+
export const WORKER_AUTHORING_CONTRACT = {
|
|
4
|
+
schema_version: "0.3",
|
|
5
|
+
required_files: ["worker.yml", "run.py or SKILL.md"],
|
|
6
|
+
required_top_level_fields: ["schema_version", "name", "title", "description", "version", "exec", "trigger"],
|
|
7
|
+
modes: {
|
|
8
|
+
"pure-script": {
|
|
9
|
+
entrypoint: "run.py",
|
|
10
|
+
required_exec_fields: ["entry", "runtime", "runner", "command", "inputs", "outputs"],
|
|
11
|
+
runtime_contract: [
|
|
12
|
+
"Read inputs from inputs.json in the worker root.",
|
|
13
|
+
"Write result.json in the worker root.",
|
|
14
|
+
"result.json must be {\"status\":\"success\",\"outputs\":{...},\"artifacts\":[],\"error\":null}.",
|
|
15
|
+
"Every declared scalar output must appear under result.json outputs.",
|
|
16
|
+
"Every declared file output must write the declared path and include it in artifacts when applicable.",
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
agent: {
|
|
20
|
+
entrypoint: "SKILL.md",
|
|
21
|
+
required_exec_fields: ["entry", "runtime", "runner", "inputs", "outputs"],
|
|
22
|
+
runtime_contract: [
|
|
23
|
+
"Put the agent instructions in SKILL.md.",
|
|
24
|
+
"Declare outputs in worker.yml so Floom can render and validate the result.",
|
|
25
|
+
"Use connections, contexts, secrets, and approvals explicitly. Do not hide requirements in prose only.",
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
secrets: [
|
|
30
|
+
"Declare every environment variable read by code under capabilities.secrets.",
|
|
31
|
+
"For compatibility, also declare secrets under top-level secrets or exec.secrets when the worker uses those legacy fields.",
|
|
32
|
+
"Never model credentials as normal run inputs.",
|
|
33
|
+
],
|
|
34
|
+
connections: [
|
|
35
|
+
"Declare every integration under top-level connections.",
|
|
36
|
+
"Use allowed_tools when the worker should only call specific Composio tools.",
|
|
37
|
+
"Mirror connection names under capabilities.connections when the YAML includes a capabilities block.",
|
|
38
|
+
],
|
|
39
|
+
validation_order: [
|
|
40
|
+
"Start from workers.templates.get.",
|
|
41
|
+
"Fill in worker.yml plus run.py or SKILL.md.",
|
|
42
|
+
"Call workers.validate.",
|
|
43
|
+
"Repair all errors before calling workers.create.",
|
|
44
|
+
"After create, run only a safe smoke input and inspect runs.get/runs.watch.",
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
export const WORKER_TEMPLATES = [
|
|
48
|
+
{
|
|
49
|
+
id: "python-script",
|
|
50
|
+
title: "Python Script Worker",
|
|
51
|
+
description: "Deterministic E2B worker that reads inputs.json and writes result.json.",
|
|
52
|
+
mode: "pure-script",
|
|
53
|
+
worker_yml: `schema_version: "0.3"
|
|
54
|
+
name: text-normalizer
|
|
55
|
+
title: Text Normalizer
|
|
56
|
+
description: Normalize a text input and return the normalized value.
|
|
57
|
+
version: "0.1.0"
|
|
58
|
+
entrypoint: run.py
|
|
59
|
+
trigger:
|
|
60
|
+
type: manual
|
|
61
|
+
exec:
|
|
62
|
+
mode: pure-script
|
|
63
|
+
entry: run.py
|
|
64
|
+
runtime: python311
|
|
65
|
+
runner: e2b
|
|
66
|
+
command: python run.py
|
|
67
|
+
inputs:
|
|
68
|
+
- name: text
|
|
69
|
+
label: Text
|
|
70
|
+
type: string
|
|
71
|
+
kind: scalar
|
|
72
|
+
required: true
|
|
73
|
+
outputs:
|
|
74
|
+
- name: normalized
|
|
75
|
+
label: Normalized text
|
|
76
|
+
type: string
|
|
77
|
+
kind: scalar
|
|
78
|
+
required: true
|
|
79
|
+
capabilities:
|
|
80
|
+
network:
|
|
81
|
+
egress: false
|
|
82
|
+
secrets: []
|
|
83
|
+
connections: []
|
|
84
|
+
`,
|
|
85
|
+
run_py: `import json
|
|
86
|
+
from pathlib import Path
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def main() -> None:
|
|
90
|
+
inputs_path = Path("inputs.json")
|
|
91
|
+
inputs = json.loads(inputs_path.read_text(encoding="utf-8")) if inputs_path.exists() else {}
|
|
92
|
+
text = str(inputs.get("text", "")).strip()
|
|
93
|
+
result = {
|
|
94
|
+
"status": "success",
|
|
95
|
+
"outputs": {"normalized": " ".join(text.split())},
|
|
96
|
+
"artifacts": [],
|
|
97
|
+
"error": None,
|
|
98
|
+
}
|
|
99
|
+
Path("result.json").write_text(json.dumps(result), encoding="utf-8")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
main()
|
|
104
|
+
`,
|
|
105
|
+
notes: [
|
|
106
|
+
"Use this when the job can be completed deterministically in code.",
|
|
107
|
+
"Keep declared outputs aligned with result.json outputs.",
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: "gmail-summary-agent",
|
|
112
|
+
title: "Gmail Summary Agent",
|
|
113
|
+
description: "Agent-mode worker that uses a Gmail connection and produces a markdown summary.",
|
|
114
|
+
mode: "agent",
|
|
115
|
+
worker_yml: `schema_version: "0.3"
|
|
116
|
+
name: gmail-summary-agent
|
|
117
|
+
title: Gmail Summary Agent
|
|
118
|
+
description: Summarize recent Gmail messages into a concise markdown brief.
|
|
119
|
+
version: "0.1.0"
|
|
120
|
+
entrypoint: SKILL.md
|
|
121
|
+
trigger:
|
|
122
|
+
type: manual
|
|
123
|
+
connections:
|
|
124
|
+
- app: gmail
|
|
125
|
+
allowed_tools:
|
|
126
|
+
- GMAIL_FETCH_EMAILS
|
|
127
|
+
exec:
|
|
128
|
+
mode: agent
|
|
129
|
+
entry: SKILL.md
|
|
130
|
+
runtime: python311
|
|
131
|
+
runner: e2b
|
|
132
|
+
inputs:
|
|
133
|
+
- name: query
|
|
134
|
+
label: Gmail query
|
|
135
|
+
type: string
|
|
136
|
+
kind: scalar
|
|
137
|
+
required: false
|
|
138
|
+
default: newer_than:1d
|
|
139
|
+
outputs:
|
|
140
|
+
- name: summary
|
|
141
|
+
label: Summary
|
|
142
|
+
type: markdown
|
|
143
|
+
kind: file
|
|
144
|
+
path: out/summary.md
|
|
145
|
+
required: true
|
|
146
|
+
capabilities:
|
|
147
|
+
network:
|
|
148
|
+
egress: true
|
|
149
|
+
secrets: []
|
|
150
|
+
connections:
|
|
151
|
+
- gmail
|
|
152
|
+
`,
|
|
153
|
+
skill_md: `# Gmail Summary Agent
|
|
154
|
+
|
|
155
|
+
Fetch recent Gmail messages using the declared Gmail connection, summarize the important items, and write a markdown brief to out/summary.md.
|
|
156
|
+
|
|
157
|
+
Return the output named summary. Do not ask the user for OAuth tokens or passwords.
|
|
158
|
+
`,
|
|
159
|
+
notes: [
|
|
160
|
+
"Use agent mode when the work needs reasoning or tool calls.",
|
|
161
|
+
"Declare every integration in connections and capabilities.connections.",
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: "approval-script",
|
|
166
|
+
title: "Approval Script Worker",
|
|
167
|
+
description: "Script worker that prepares a human approval payload before an external action.",
|
|
168
|
+
mode: "pure-script",
|
|
169
|
+
worker_yml: `schema_version: "0.3"
|
|
170
|
+
name: approval-script
|
|
171
|
+
title: Approval Script
|
|
172
|
+
description: Build an approval payload for a proposed outbound action.
|
|
173
|
+
version: "0.1.0"
|
|
174
|
+
entrypoint: run.py
|
|
175
|
+
trigger:
|
|
176
|
+
type: manual
|
|
177
|
+
exec:
|
|
178
|
+
mode: pure-script
|
|
179
|
+
entry: run.py
|
|
180
|
+
runtime: python311
|
|
181
|
+
runner: e2b
|
|
182
|
+
command: python run.py
|
|
183
|
+
inputs:
|
|
184
|
+
- name: message
|
|
185
|
+
label: Message
|
|
186
|
+
type: string
|
|
187
|
+
kind: scalar
|
|
188
|
+
required: true
|
|
189
|
+
outputs:
|
|
190
|
+
- name: plan
|
|
191
|
+
label: Approval plan
|
|
192
|
+
type: markdown
|
|
193
|
+
kind: file
|
|
194
|
+
path: out/plan.md
|
|
195
|
+
required: true
|
|
196
|
+
approvals:
|
|
197
|
+
required: true
|
|
198
|
+
label: Approve outbound action
|
|
199
|
+
capabilities:
|
|
200
|
+
network:
|
|
201
|
+
egress: false
|
|
202
|
+
secrets: []
|
|
203
|
+
connections: []
|
|
204
|
+
`,
|
|
205
|
+
run_py: `import json
|
|
206
|
+
from pathlib import Path
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def main() -> None:
|
|
210
|
+
inputs = json.loads(Path("inputs.json").read_text(encoding="utf-8"))
|
|
211
|
+
message = str(inputs.get("message", "")).strip()
|
|
212
|
+
out_dir = Path("out")
|
|
213
|
+
out_dir.mkdir(exist_ok=True)
|
|
214
|
+
plan_path = out_dir / "plan.md"
|
|
215
|
+
plan_path.write_text(f"# Proposed action\\n\\n{message}\\n", encoding="utf-8")
|
|
216
|
+
result = {
|
|
217
|
+
"status": "success",
|
|
218
|
+
"outputs": {"plan": str(plan_path)},
|
|
219
|
+
"artifacts": [{"name": "plan.md", "path": str(plan_path), "type": "text/markdown"}],
|
|
220
|
+
"error": None,
|
|
221
|
+
}
|
|
222
|
+
Path("result.json").write_text(json.dumps(result), encoding="utf-8")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
if __name__ == "__main__":
|
|
226
|
+
main()
|
|
227
|
+
`,
|
|
228
|
+
notes: [
|
|
229
|
+
"Use approvals.required when a human must review before publication or side effects.",
|
|
230
|
+
"Do not require secrets during the approval-plan phase unless the plan truly needs private API data.",
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
];
|
|
234
|
+
export function listWorkerTemplates() {
|
|
235
|
+
return WORKER_TEMPLATES.map(({ worker_yml: _workerYml, run_py: _runPy, skill_md: _skillMd, ...template }) => template);
|
|
236
|
+
}
|
|
237
|
+
export function getWorkerTemplate(id) {
|
|
238
|
+
return WORKER_TEMPLATES.find((template) => template.id === id);
|
|
239
|
+
}
|
|
240
|
+
function isRecord(value) {
|
|
241
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
242
|
+
}
|
|
243
|
+
function asRecord(value) {
|
|
244
|
+
return isRecord(value) ? value : undefined;
|
|
245
|
+
}
|
|
246
|
+
function nonEmptyString(value) {
|
|
247
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
248
|
+
}
|
|
249
|
+
function stringList(value) {
|
|
250
|
+
if (!Array.isArray(value))
|
|
251
|
+
return [];
|
|
252
|
+
return value.filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => item.trim());
|
|
253
|
+
}
|
|
254
|
+
function fieldArray(manifest, key) {
|
|
255
|
+
const topLevel = Array.isArray(manifest[key]) ? manifest[key] : undefined;
|
|
256
|
+
const exec = asRecord(manifest.exec);
|
|
257
|
+
const execLevel = exec && Array.isArray(exec[key]) ? exec[key] : undefined;
|
|
258
|
+
return (execLevel || topLevel || []).filter(isRecord);
|
|
259
|
+
}
|
|
260
|
+
function collectOutputNames(manifest) {
|
|
261
|
+
return fieldArray(manifest, "outputs")
|
|
262
|
+
.map((item) => nonEmptyString(item.name))
|
|
263
|
+
.filter((value) => Boolean(value));
|
|
264
|
+
}
|
|
265
|
+
function collectConnectionNames(manifest) {
|
|
266
|
+
const raw = Array.isArray(manifest.connections) ? manifest.connections : [];
|
|
267
|
+
const result = new Set();
|
|
268
|
+
for (const item of raw) {
|
|
269
|
+
if (typeof item === "string" && item.trim()) {
|
|
270
|
+
result.add(item.trim());
|
|
271
|
+
}
|
|
272
|
+
else if (isRecord(item)) {
|
|
273
|
+
const app = nonEmptyString(item.app) || nonEmptyString(asRecord(item.composio)?.app);
|
|
274
|
+
if (app)
|
|
275
|
+
result.add(app);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return [...result].sort();
|
|
279
|
+
}
|
|
280
|
+
function collectDeclaredSecrets(manifest) {
|
|
281
|
+
const result = new Set();
|
|
282
|
+
for (const key of stringList(manifest.secrets))
|
|
283
|
+
result.add(key);
|
|
284
|
+
const exec = asRecord(manifest.exec);
|
|
285
|
+
if (exec) {
|
|
286
|
+
for (const key of stringList(exec.secrets))
|
|
287
|
+
result.add(key);
|
|
288
|
+
}
|
|
289
|
+
const capabilities = asRecord(manifest.capabilities);
|
|
290
|
+
if (capabilities) {
|
|
291
|
+
for (const key of stringList(capabilities.secrets))
|
|
292
|
+
result.add(key);
|
|
293
|
+
}
|
|
294
|
+
return [...result].sort();
|
|
295
|
+
}
|
|
296
|
+
function collectCapabilityList(manifest, key) {
|
|
297
|
+
const capabilities = asRecord(manifest.capabilities);
|
|
298
|
+
return capabilities ? stringList(capabilities[key]) : [];
|
|
299
|
+
}
|
|
300
|
+
function collectEnvReads(runPy) {
|
|
301
|
+
const result = new Set();
|
|
302
|
+
const patterns = [
|
|
303
|
+
/\bos\.environ\s*\[\s*["']([A-Za-z_][A-Za-z0-9_]*)["']\s*\]/g,
|
|
304
|
+
/\bos\.environ\.get\s*\(\s*["']([A-Za-z_][A-Za-z0-9_]*)["']/g,
|
|
305
|
+
/\bos\.getenv\s*\(\s*["']([A-Za-z_][A-Za-z0-9_]*)["']/g,
|
|
306
|
+
];
|
|
307
|
+
for (const pattern of patterns) {
|
|
308
|
+
for (const match of runPy.matchAll(pattern)) {
|
|
309
|
+
result.add(match[1]);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return [...result].sort();
|
|
313
|
+
}
|
|
314
|
+
function inferEntrypoint(manifest) {
|
|
315
|
+
const exec = asRecord(manifest.exec);
|
|
316
|
+
return nonEmptyString(exec?.entry) || nonEmptyString(manifest.entrypoint);
|
|
317
|
+
}
|
|
318
|
+
function inferMode(manifest) {
|
|
319
|
+
const exec = asRecord(manifest.exec);
|
|
320
|
+
const explicit = nonEmptyString(exec?.mode);
|
|
321
|
+
const entry = inferEntrypoint(manifest);
|
|
322
|
+
if (explicit === "agent" || entry === "SKILL.md")
|
|
323
|
+
return "agent";
|
|
324
|
+
if (explicit === "pure-script" || entry === "run.py")
|
|
325
|
+
return "pure-script";
|
|
326
|
+
return "unknown";
|
|
327
|
+
}
|
|
328
|
+
function parseManifest(workerYml) {
|
|
329
|
+
try {
|
|
330
|
+
const parsed = parseYaml(workerYml);
|
|
331
|
+
if (!isRecord(parsed)) {
|
|
332
|
+
return { errors: ["worker.yml must be a YAML mapping"] };
|
|
333
|
+
}
|
|
334
|
+
return { manifest: parsed, errors: [] };
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
338
|
+
return { errors: [`worker.yml is not valid YAML: ${message}`] };
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
export function validateWorkerDraft(input) {
|
|
342
|
+
const errors = [];
|
|
343
|
+
const warnings = [];
|
|
344
|
+
const parsed = parseManifest(input.worker_yml);
|
|
345
|
+
errors.push(...parsed.errors);
|
|
346
|
+
const manifest = parsed.manifest || {};
|
|
347
|
+
const exec = asRecord(manifest.exec);
|
|
348
|
+
const capabilities = asRecord(manifest.capabilities);
|
|
349
|
+
const mode = parsed.manifest ? inferMode(manifest) : "unknown";
|
|
350
|
+
const entrypoint = parsed.manifest ? inferEntrypoint(manifest) : undefined;
|
|
351
|
+
const runtime = nonEmptyString(exec?.runtime) || nonEmptyString(asRecord(exec?.runtime)?.type);
|
|
352
|
+
const runner = nonEmptyString(exec?.runner);
|
|
353
|
+
const declaredOutputs = parsed.manifest ? collectOutputNames(manifest) : [];
|
|
354
|
+
const declaredSecrets = parsed.manifest ? collectDeclaredSecrets(manifest) : [];
|
|
355
|
+
const declaredConnections = parsed.manifest ? collectConnectionNames(manifest) : [];
|
|
356
|
+
const envReads = collectEnvReads(input.run_py || "");
|
|
357
|
+
if (parsed.manifest) {
|
|
358
|
+
if (manifest.schema_version !== "0.3")
|
|
359
|
+
errors.push('schema_version must be "0.3"');
|
|
360
|
+
for (const field of ["name", "title", "description", "version"]) {
|
|
361
|
+
if (!nonEmptyString(manifest[field]))
|
|
362
|
+
errors.push(`worker.yml must include non-empty ${field}`);
|
|
363
|
+
}
|
|
364
|
+
if (!exec) {
|
|
365
|
+
errors.push("worker.yml must include exec");
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
if (!entrypoint)
|
|
369
|
+
errors.push("exec.entry or entrypoint is required");
|
|
370
|
+
if (!runtime)
|
|
371
|
+
errors.push("exec.runtime is required");
|
|
372
|
+
if (runtime && !SUPPORTED_RUNTIMES.has(runtime)) {
|
|
373
|
+
errors.push(`exec.runtime '${runtime}' is not supported; use one of ${[...SUPPORTED_RUNTIMES].join(", ")}`);
|
|
374
|
+
}
|
|
375
|
+
if (runner !== "e2b")
|
|
376
|
+
errors.push('exec.runner must be "e2b"');
|
|
377
|
+
if (!Array.isArray(exec.inputs))
|
|
378
|
+
errors.push("exec.inputs must be an array, even when empty");
|
|
379
|
+
if (!Array.isArray(exec.outputs))
|
|
380
|
+
errors.push("exec.outputs must be an array, even when empty");
|
|
381
|
+
if (mode === "pure-script" && !nonEmptyString(exec.command))
|
|
382
|
+
errors.push("pure-script workers must include exec.command");
|
|
383
|
+
}
|
|
384
|
+
const trigger = asRecord(manifest.trigger);
|
|
385
|
+
if (!trigger) {
|
|
386
|
+
errors.push("worker.yml must include trigger");
|
|
387
|
+
}
|
|
388
|
+
else if (!nonEmptyString(trigger.type)) {
|
|
389
|
+
errors.push("trigger.type is required");
|
|
390
|
+
}
|
|
391
|
+
if (mode === "unknown")
|
|
392
|
+
errors.push('worker mode is unknown; use exec.mode "pure-script" with run.py or "agent" with SKILL.md');
|
|
393
|
+
if (mode === "pure-script" && entrypoint !== "run.py")
|
|
394
|
+
errors.push('pure-script workers must use exec.entry: "run.py"');
|
|
395
|
+
if (mode === "agent" && entrypoint !== "SKILL.md")
|
|
396
|
+
errors.push('agent workers must use exec.entry: "SKILL.md"');
|
|
397
|
+
for (const field of fieldArray(manifest, "inputs")) {
|
|
398
|
+
if (!nonEmptyString(field.name))
|
|
399
|
+
errors.push("every input must include name");
|
|
400
|
+
if (!nonEmptyString(field.type))
|
|
401
|
+
errors.push(`input ${nonEmptyString(field.name) || "<unnamed>"} must include type`);
|
|
402
|
+
}
|
|
403
|
+
for (const field of fieldArray(manifest, "outputs")) {
|
|
404
|
+
if (!nonEmptyString(field.name))
|
|
405
|
+
errors.push("every output must include name");
|
|
406
|
+
if (!nonEmptyString(field.type))
|
|
407
|
+
errors.push(`output ${nonEmptyString(field.name) || "<unnamed>"} must include type`);
|
|
408
|
+
}
|
|
409
|
+
const capSecrets = collectCapabilityList(manifest, "secrets");
|
|
410
|
+
for (const key of envReads) {
|
|
411
|
+
if (!capSecrets.includes(key)) {
|
|
412
|
+
errors.push(`run.py reads env ${key}, but worker.yml does not declare it under capabilities.secrets`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const capConnections = collectCapabilityList(manifest, "connections");
|
|
416
|
+
for (const connection of declaredConnections) {
|
|
417
|
+
if (capabilities && !capConnections.includes(connection)) {
|
|
418
|
+
warnings.push(`connection ${connection} is declared top-level but not mirrored under capabilities.connections`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (mode === "pure-script") {
|
|
423
|
+
const runPy = input.run_py || "";
|
|
424
|
+
if (!runPy.trim()) {
|
|
425
|
+
errors.push("pure-script workers require non-empty run_py");
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
if (!/result\.json/.test(runPy)) {
|
|
429
|
+
errors.push("pure-script run.py must write result.json");
|
|
430
|
+
}
|
|
431
|
+
if (/^\s*def\s+run\s*\(\s*inputs\s*,\s*context\s*\)\s*:/m.test(runPy) && /^\s*return\b/m.test(runPy) && !/result\.json/.test(runPy)) {
|
|
432
|
+
errors.push("pure-script run.py must not rely on SDK-style def run() return values; write result.json instead");
|
|
433
|
+
}
|
|
434
|
+
if (declaredOutputs.length > 0 && /["']outputs["']\s*:\s*\{\s*\}/.test(runPy)) {
|
|
435
|
+
errors.push("run.py writes an empty outputs object, but worker.yml declares outputs");
|
|
436
|
+
}
|
|
437
|
+
for (const output of declaredOutputs) {
|
|
438
|
+
if (!new RegExp(`["']${output.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']`).test(runPy)) {
|
|
439
|
+
errors.push(`declared output ${output} does not appear in run.py; result.json.outputs must include every declared output`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (input.skill_md?.trim())
|
|
444
|
+
warnings.push("skill_md is ignored for pure-script workers");
|
|
445
|
+
}
|
|
446
|
+
if (mode === "agent") {
|
|
447
|
+
if (!input.skill_md?.trim())
|
|
448
|
+
errors.push("agent workers require non-empty skill_md / SKILL.md");
|
|
449
|
+
if (input.run_py?.trim())
|
|
450
|
+
warnings.push("agent workers execute SKILL.md; run_py should be omitted unless the API requires a compatibility stub");
|
|
451
|
+
}
|
|
452
|
+
const nextSteps = errors.length > 0
|
|
453
|
+
? ["Fix every validation error.", "Call workers.validate again before workers.create."]
|
|
454
|
+
: ["Call workers.create with this exact worker_yml plus run_py or skill_md.", "Run a safe smoke input, then inspect runs.get/runs.watch."];
|
|
455
|
+
return {
|
|
456
|
+
valid: errors.length === 0,
|
|
457
|
+
errors,
|
|
458
|
+
warnings,
|
|
459
|
+
inferred: {
|
|
460
|
+
mode,
|
|
461
|
+
entrypoint,
|
|
462
|
+
runtime,
|
|
463
|
+
runner,
|
|
464
|
+
declared_outputs: declaredOutputs,
|
|
465
|
+
declared_secrets: declaredSecrets,
|
|
466
|
+
declared_connections: declaredConnections,
|
|
467
|
+
env_reads: envReads,
|
|
468
|
+
},
|
|
469
|
+
next_steps: nextSteps,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
//# sourceMappingURL=worker-authoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-authoring.js","sourceRoot":"","sources":["../../src/lib/worker-authoring.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -6,6 +6,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { readCredentials } from "./lib/credentials.js";
|
|
9
|
+
import { WORKER_AUTHORING_CONTRACT, getWorkerTemplate, listWorkerTemplates, validateWorkerDraft, } from "./lib/worker-authoring.js";
|
|
9
10
|
const DEFAULT_API_BASE = "http://localhost:8000";
|
|
10
11
|
const TERMINAL_RUN_STATUSES = new Set([
|
|
11
12
|
"success",
|
|
@@ -644,8 +645,9 @@ export function createServer() {
|
|
|
644
645
|
"OAuth-ing as the user into a third party.)",
|
|
645
646
|
});
|
|
646
647
|
const workerContractYamlDescription = "WorkerContract YAML content. Required top-level fields: schema_version: \"0.3\", name, title, description, version, exec, and trigger. " +
|
|
647
|
-
"
|
|
648
|
-
"
|
|
648
|
+
"Before creating a worker, call workers.contract, choose a starting point with workers.templates.get, then call workers.validate. " +
|
|
649
|
+
"For script workers, exec must include mode: \"pure-script\", entry: \"run.py\", runtime: \"python311\", runner: \"e2b\", command: \"python run.py\", plus exec.inputs and exec.outputs arrays. " +
|
|
650
|
+
"Script workers must read inputs.json and write result.json at the worker root.";
|
|
649
651
|
server.registerTool("workers.list", {
|
|
650
652
|
title: "List Workers",
|
|
651
653
|
description: "List Floom workers.",
|
|
@@ -658,22 +660,66 @@ export function createServer() {
|
|
|
658
660
|
inputSchema: workerIdSchema.shape,
|
|
659
661
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
660
662
|
}, async ({ id }) => callTool(async () => jsonResult(await request("GET", `/workers/${encodeURIComponent(id)}`))));
|
|
663
|
+
server.registerTool("workers.contract", {
|
|
664
|
+
title: "Get Worker Authoring Contract",
|
|
665
|
+
description: "Return the canonical Floom worker authoring contract for agents. Call this before drafting worker.yml, run.py, or SKILL.md.",
|
|
666
|
+
inputSchema: {},
|
|
667
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
668
|
+
}, async () => callTool(async () => jsonResult(WORKER_AUTHORING_CONTRACT)));
|
|
669
|
+
server.registerTool("workers.templates.list", {
|
|
670
|
+
title: "List Worker Templates",
|
|
671
|
+
description: "List golden worker templates. Agents should start from one of these instead of inventing worker.yml from scratch.",
|
|
672
|
+
inputSchema: {},
|
|
673
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
674
|
+
}, async () => callTool(async () => jsonResult({ templates: listWorkerTemplates() })));
|
|
675
|
+
server.registerTool("workers.templates.get", {
|
|
676
|
+
title: "Get Worker Template",
|
|
677
|
+
description: "Return a full worker template with worker.yml plus run.py or SKILL.md. Use this as the starting point for workers.create.",
|
|
678
|
+
inputSchema: {
|
|
679
|
+
id: z.string().min(1).describe("Template id from workers.templates.list, for example python-script."),
|
|
680
|
+
},
|
|
681
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
682
|
+
}, async ({ id }) => callTool(async () => {
|
|
683
|
+
const template = getWorkerTemplate(id);
|
|
684
|
+
if (!template) {
|
|
685
|
+
throw new FloomApiError(`Unknown worker template '${id}'`, 404, { available: listWorkerTemplates() });
|
|
686
|
+
}
|
|
687
|
+
return jsonResult(template);
|
|
688
|
+
}));
|
|
689
|
+
server.registerTool("workers.validate", {
|
|
690
|
+
title: "Validate Worker Draft",
|
|
691
|
+
description: "Validate worker.yml plus run.py or SKILL.md before create. This catches schema, runtime contract, secrets, connections, and output-shape mistakes.",
|
|
692
|
+
inputSchema: {
|
|
693
|
+
worker_yml: z.string().min(1).describe(workerContractYamlDescription),
|
|
694
|
+
run_py: z.string().optional().describe("Python source for run.py when exec.mode is pure-script."),
|
|
695
|
+
skill_md: z.string().optional().describe("SKILL.md content when exec.mode is agent."),
|
|
696
|
+
},
|
|
697
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
698
|
+
}, async ({ worker_yml, run_py, skill_md }) => callTool(async () => jsonResult(validateWorkerDraft({ worker_yml, run_py, skill_md }))));
|
|
661
699
|
server.registerTool("workers.create", {
|
|
662
700
|
title: "Create Worker",
|
|
663
|
-
description: "Create a Floom worker from WorkerContract YAML. " +
|
|
701
|
+
description: "Create a Floom worker from WorkerContract YAML. First call workers.contract, workers.templates.get, and workers.validate. " +
|
|
664
702
|
"The YAML must include schema_version, name, title, description, version, exec, and trigger. " +
|
|
665
|
-
"For script-mode workers supply run_py. For agent/skill-mode workers supply skill_md
|
|
703
|
+
"For script-mode workers supply run_py that reads inputs.json and writes result.json. For agent/skill-mode workers supply skill_md.",
|
|
666
704
|
inputSchema: {
|
|
667
705
|
worker_yml: z.string().min(1).describe(workerContractYamlDescription),
|
|
668
|
-
run_py: z.string().
|
|
706
|
+
run_py: z.string().optional().describe("Python source for run.py. Required for pure-script workers."),
|
|
669
707
|
skill_md: z.string().optional().describe("Agent system prompt (SKILL.md) for skill/agent-mode workers. Omit for script-mode workers."),
|
|
670
708
|
},
|
|
671
709
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
672
|
-
}, async ({ worker_yml, run_py, skill_md }) => callTool(async () =>
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
710
|
+
}, async ({ worker_yml, run_py, skill_md }) => callTool(async () => {
|
|
711
|
+
const source = run_py || "";
|
|
712
|
+
const filledWorkerYml = autoFillCapabilities(worker_yml, source);
|
|
713
|
+
const validation = validateWorkerDraft({ worker_yml: filledWorkerYml, run_py: source, skill_md });
|
|
714
|
+
if (!validation.valid) {
|
|
715
|
+
throw new FloomApiError("Worker draft validation failed; call workers.validate for repair details", 400, validation);
|
|
716
|
+
}
|
|
717
|
+
return jsonResult(await request("POST", "/workers", {
|
|
718
|
+
worker_yml: filledWorkerYml,
|
|
719
|
+
run_py: source,
|
|
720
|
+
...(skill_md ? { skill_md } : {}),
|
|
721
|
+
}), "Worker created.");
|
|
722
|
+
}));
|
|
677
723
|
server.registerTool("workers.update", {
|
|
678
724
|
title: "Update Worker",
|
|
679
725
|
description: "Update worker instance settings such as trigger, cron, input defaults, and documented capabilities.",
|