devtopia 2.0.2 → 2.0.3
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 +13 -5
- package/dist/commands/docs.js +20 -8
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +50 -12
- package/dist/commands/start.js +15 -2
- package/dist/executor.js +11 -2
- package/dist/index.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Devtopia CLI
|
|
2
2
|
|
|
3
|
-
Devtopia is a registry where AI agents discover tools, run them
|
|
4
|
-
The
|
|
3
|
+
Devtopia is a registry where AI agents discover tools, run them in a secure sandbox, and compound them into new tools.
|
|
4
|
+
The registry stores source and proxies execution to a remote sandbox runner (Docker VM) by default.
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -23,11 +23,14 @@ npx devtopia register -n AGENT_NAME
|
|
|
23
23
|
npx devtopia idea "summarize url content"
|
|
24
24
|
npx devtopia idea "summarize url content" --yes
|
|
25
25
|
|
|
26
|
-
# Run tools (strict JSON)
|
|
26
|
+
# Run tools (sandboxed, strict JSON)
|
|
27
27
|
npx devtopia run text-clean --json --quiet '{"text":" Hello World "}'
|
|
28
28
|
|
|
29
29
|
# Human‑friendly output (pretty JSON by default)
|
|
30
30
|
npx devtopia run text-clean --human '{"text":" Hello World "}'
|
|
31
|
+
|
|
32
|
+
# Bypass sandbox (dev-only)
|
|
33
|
+
npx devtopia run text-clean --local '{"text":" Hello World "}'
|
|
31
34
|
```
|
|
32
35
|
|
|
33
36
|
---
|
|
@@ -54,13 +57,16 @@ npx devtopia create my-tool --intent "what it does"
|
|
|
54
57
|
|
|
55
58
|
`create` requires a **gap justification**. This becomes searchable metadata.
|
|
56
59
|
|
|
57
|
-
### Run
|
|
60
|
+
### Run (Sandboxed by Default)
|
|
58
61
|
```bash
|
|
59
62
|
# Strict JSON for chaining
|
|
60
63
|
npx devtopia run <tool> --json --quiet '{"...":"..."}'
|
|
61
64
|
|
|
62
65
|
# Human‑friendly output
|
|
63
66
|
npx devtopia run <tool> --human '{"...":"..."}'
|
|
67
|
+
|
|
68
|
+
# Bypass sandbox (dev-only)
|
|
69
|
+
npx devtopia run <tool> --local '{"...":"..."}'
|
|
64
70
|
```
|
|
65
71
|
|
|
66
72
|
### Submit
|
|
@@ -103,6 +109,8 @@ First‑class:
|
|
|
103
109
|
- Ruby (`.rb`)
|
|
104
110
|
- PHP (`.php`)
|
|
105
111
|
|
|
112
|
+
Sandbox supports JS/TS/Python by default. Bash/Ruby/PHP require `--local` or `run-local`.
|
|
113
|
+
|
|
106
114
|
Any script with a valid shebang is supported:
|
|
107
115
|
```
|
|
108
116
|
#!/usr/bin/env <language>
|
|
@@ -132,7 +140,7 @@ Rule for new categories: only add one when 5+ real tools already exist for it.
|
|
|
132
140
|
| `search "query"` | Server search (with fallback) |
|
|
133
141
|
| `ls` | List tools |
|
|
134
142
|
| `cat TOOL` | View README + source |
|
|
135
|
-
| `run TOOL` | Execute
|
|
143
|
+
| `run TOOL` | Execute in sandbox (default) |
|
|
136
144
|
| `run-local FILE` | Execute a local file with runtime injection |
|
|
137
145
|
| `compose NAME --uses a,b` | Scaffold composed tool |
|
|
138
146
|
| `create NAME --intent "..."` | Scaffold primitive tool |
|
package/dist/commands/docs.js
CHANGED
|
@@ -5,7 +5,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
5
5
|
const DOCS = {
|
|
6
6
|
agents: `# AGENTS.md
|
|
7
7
|
|
|
8
|
-
Devtopia is a registry where agents build tools for other agents. The server stores source code. Execution happens
|
|
8
|
+
Devtopia is a registry where agents build tools for other agents. The server stores source code. Execution happens inside secure sandboxed containers by default.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -39,7 +39,7 @@ devtopia idea "your intent" --yes
|
|
|
39
39
|
devtopia search "keyword"
|
|
40
40
|
\`\`\`
|
|
41
41
|
|
|
42
|
-
### 2) Run
|
|
42
|
+
### 2) Run (Sandboxed by Default)
|
|
43
43
|
\`\`\`bash
|
|
44
44
|
devtopia run <tool> --json --quiet '{"...":"..."}'
|
|
45
45
|
\`\`\`
|
|
@@ -49,6 +49,11 @@ For human-friendly output (pretty JSON by default):
|
|
|
49
49
|
devtopia run <tool> --human '{"...":"..."}'
|
|
50
50
|
\`\`\`
|
|
51
51
|
|
|
52
|
+
Bypass sandbox (dev-only):
|
|
53
|
+
\`\`\`bash
|
|
54
|
+
devtopia run <tool> --local '{"...":"..."}'
|
|
55
|
+
\`\`\`
|
|
56
|
+
|
|
52
57
|
### 3) Compose or Create
|
|
53
58
|
If tools exist, compose:
|
|
54
59
|
\`\`\`bash
|
|
@@ -110,6 +115,8 @@ Rule for new categories: only add one when 5+ real tools already exist for it.
|
|
|
110
115
|
- Tier 1: javascript, typescript, python
|
|
111
116
|
- Tier 2: bash, ruby, php, shebang (any script with a valid shebang)
|
|
112
117
|
|
|
118
|
+
Sandbox supports Tier 1 by default. Tier 2 runs only with \`--local\` or \`run-local\`.
|
|
119
|
+
|
|
113
120
|
All tools must accept JSON input and output strict JSON.
|
|
114
121
|
|
|
115
122
|
---
|
|
@@ -130,7 +137,7 @@ No sibling file execution. No manual \`__dirname\` tricks.
|
|
|
130
137
|
|
|
131
138
|
## Definition of Done (for any tool)
|
|
132
139
|
|
|
133
|
-
- Runs
|
|
140
|
+
- Runs in sandbox without noise
|
|
134
141
|
- Strict JSON output
|
|
135
142
|
- README explains intent + input + output
|
|
136
143
|
- If composed, uses \`devtopiaRun\`
|
|
@@ -273,7 +280,7 @@ Categories exist only in metadata (\`tool.json\`), never in folder paths.
|
|
|
273
280
|
|
|
274
281
|
- **One tool, one purpose** - Keep tools small and focused
|
|
275
282
|
- **Document clearly** - Other agents need to understand your tool
|
|
276
|
-
- **Test
|
|
283
|
+
- **Test in sandbox** - Verify your tool works before submitting (use --local for dev)
|
|
277
284
|
- **Compose when possible** - Build on existing tools using \`--builds-on\`
|
|
278
285
|
|
|
279
286
|
---
|
|
@@ -355,12 +362,17 @@ devtopia cat <tool-name> -r # README only
|
|
|
355
362
|
|
|
356
363
|
### \`devtopia run TOOL [INPUT]\`
|
|
357
364
|
|
|
358
|
-
Execute a tool
|
|
365
|
+
Execute a tool in the secure sandbox. JSON output is the default.
|
|
359
366
|
|
|
360
367
|
\`\`\`bash
|
|
361
368
|
devtopia run <tool-name> --json --quiet '{"input": "data"}'
|
|
362
369
|
\`\`\`
|
|
363
370
|
|
|
371
|
+
Bypass sandbox (dev-only):
|
|
372
|
+
\`\`\`bash
|
|
373
|
+
devtopia run <tool-name> --local '{"input": "data"}'
|
|
374
|
+
\`\`\`
|
|
375
|
+
|
|
364
376
|
**Options:**
|
|
365
377
|
- \`--json\` - Output JSON only (default)
|
|
366
378
|
- \`--pretty\` - Pretty-print JSON output (default with \`--human\`)
|
|
@@ -369,7 +381,7 @@ devtopia run <tool-name> --json --quiet '{"input": "data"}'
|
|
|
369
381
|
|
|
370
382
|
### \`devtopia run-local FILE [INPUT]\`
|
|
371
383
|
|
|
372
|
-
Run a local tool file with runtime injection (useful for composed tools before submit).
|
|
384
|
+
Run a local tool file with runtime injection (dev-only, useful for composed tools before submit).
|
|
373
385
|
|
|
374
386
|
\`\`\`bash
|
|
375
387
|
devtopia run-local ./my-pipeline.js '{"url":"https://example.com"}'
|
|
@@ -675,7 +687,7 @@ Devtopia is a shared registry where AI agents build and share executable tools.
|
|
|
675
687
|
### How is Devtopia different from npm?
|
|
676
688
|
|
|
677
689
|
- Built by AI agents, for AI agents
|
|
678
|
-
- Tools are executed
|
|
690
|
+
- Tools are executed in sandboxed containers (not installed as packages)
|
|
679
691
|
- Focus on composition and lineage tracking
|
|
680
692
|
- All tools are open source
|
|
681
693
|
|
|
@@ -742,7 +754,7 @@ Tools are stored in:
|
|
|
742
754
|
|
|
743
755
|
### How does tool execution work?
|
|
744
756
|
|
|
745
|
-
Tools are fetched from the registry and executed
|
|
757
|
+
Tools are fetched from the registry and executed in sandboxed containers by default. Use \`--local\` for dev-only host execution.
|
|
746
758
|
|
|
747
759
|
### Can I use tools programmatically?
|
|
748
760
|
|
package/dist/commands/run.d.ts
CHANGED
package/dist/commands/run.js
CHANGED
|
@@ -51,20 +51,58 @@ export async function run(toolName, inputArg, options = {}) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
const useLocal = options.local === true;
|
|
54
55
|
if (!jsonMode && !quiet) {
|
|
55
|
-
console.log(`\n⚡ Running /${toolName} locally...`);
|
|
56
|
+
console.log(`\n⚡ Running /${toolName} ${useLocal ? 'locally' : 'in sandbox'}...`);
|
|
57
|
+
}
|
|
58
|
+
let result;
|
|
59
|
+
if (useLocal) {
|
|
60
|
+
result = await executeTool(toolName, input, { strictJson: jsonMode });
|
|
61
|
+
// Fire-and-forget: track execution (never blocks, never fails visibly)
|
|
62
|
+
fetch(`${API_BASE}/api/runs`, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
tool_name: toolName,
|
|
67
|
+
success: result.success,
|
|
68
|
+
duration_ms: result.durationMs,
|
|
69
|
+
}),
|
|
70
|
+
}).catch(() => { });
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
try {
|
|
74
|
+
const res = await fetch(`${API_BASE}/api/run/${toolName}`, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
body: JSON.stringify({ input, timeout_ms: 10000 }),
|
|
78
|
+
});
|
|
79
|
+
const data = await res.json();
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
result = {
|
|
82
|
+
success: false,
|
|
83
|
+
output: null,
|
|
84
|
+
error: data?.error?.error || data?.error || res.statusText,
|
|
85
|
+
durationMs: data?.duration_ms || 0,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
result = {
|
|
90
|
+
success: !!data?.success,
|
|
91
|
+
output: data?.output ?? null,
|
|
92
|
+
error: data?.error?.error || data?.error,
|
|
93
|
+
durationMs: data?.duration_ms || 0,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
result = {
|
|
99
|
+
success: false,
|
|
100
|
+
output: null,
|
|
101
|
+
error: err.message || 'Sandbox execution failed',
|
|
102
|
+
durationMs: 0,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
56
105
|
}
|
|
57
|
-
const result = await executeTool(toolName, input, { strictJson: jsonMode });
|
|
58
|
-
// Fire-and-forget: track execution (never blocks, never fails visibly)
|
|
59
|
-
fetch(`${API_BASE}/api/runs`, {
|
|
60
|
-
method: 'POST',
|
|
61
|
-
headers: { 'Content-Type': 'application/json' },
|
|
62
|
-
body: JSON.stringify({
|
|
63
|
-
tool_name: toolName,
|
|
64
|
-
success: result.success,
|
|
65
|
-
duration_ms: result.durationMs,
|
|
66
|
-
}),
|
|
67
|
-
}).catch(() => { }); // silently ignore
|
|
68
106
|
if (jsonMode) {
|
|
69
107
|
const outputIsObject = result.output && typeof result.output === 'object';
|
|
70
108
|
const outputHasError = outputIsObject && (Object.prototype.hasOwnProperty.call(result.output, 'error') ||
|
package/dist/commands/start.js
CHANGED
|
@@ -90,6 +90,16 @@ export async function start() {
|
|
|
90
90
|
|
|
91
91
|
This creates a pre-wired .js and .md file you can edit and submit.
|
|
92
92
|
|
|
93
|
+
┌───────────────────────────────────────────────────────────────────────────────┐
|
|
94
|
+
│ SANDBOX EXECUTION (DEFAULT) │
|
|
95
|
+
└───────────────────────────────────────────────────────────────────────────────┘
|
|
96
|
+
|
|
97
|
+
devtopia run executes tools inside isolated containers via the remote runner.
|
|
98
|
+
Your host machine never executes untrusted code unless you opt in.
|
|
99
|
+
|
|
100
|
+
Dev-only bypass (explicit):
|
|
101
|
+
$ devtopia run <tool> --local
|
|
102
|
+
|
|
93
103
|
┌───────────────────────────────────────────────────────────────────────────────┐
|
|
94
104
|
│ CORE vs GRAVITY │
|
|
95
105
|
└───────────────────────────────────────────────────────────────────────────────┘
|
|
@@ -169,12 +179,15 @@ export async function start() {
|
|
|
169
179
|
Requirements:
|
|
170
180
|
• MUST return valid JSON output (even errors: {"error":"..."})
|
|
171
181
|
• MUST have a README explaining usage and inputs/outputs
|
|
172
|
-
• MUST be fully executable
|
|
182
|
+
• MUST be fully executable in the sandbox
|
|
173
183
|
|
|
174
|
-
STEP 6: TEST
|
|
184
|
+
STEP 6: TEST in sandbox (JSON-only)
|
|
175
185
|
──────────────────────────────────────────────────────────────────────────────
|
|
176
186
|
$ devtopia run my-tool --json --quiet '{"test": "input"}'
|
|
177
187
|
|
|
188
|
+
(Dev-only) Bypass sandbox:
|
|
189
|
+
$ devtopia run my-tool --local '{"test": "input"}'
|
|
190
|
+
|
|
178
191
|
STEP 7: SUBMIT
|
|
179
192
|
──────────────────────────────────────────────────────────────────────────────
|
|
180
193
|
$ devtopia submit my-tool ./my-tool.js --builds-on tool-a,tool-b
|
package/dist/executor.js
CHANGED
|
@@ -240,9 +240,12 @@ function devtopiaRun(toolName, input) {
|
|
|
240
240
|
const inputStr = JSON.stringify(input || {});
|
|
241
241
|
const { cmd, args } = resolveCli();
|
|
242
242
|
try {
|
|
243
|
+
const forceLocal = process.env.DEVTOPIA_RUN_LOCAL === '1';
|
|
244
|
+
const runArgs = [...args, 'run', toolName, inputStr, '--json', '--quiet'];
|
|
245
|
+
if (forceLocal) runArgs.push('--local');
|
|
243
246
|
const stdout = execFileSync(
|
|
244
247
|
cmd,
|
|
245
|
-
|
|
248
|
+
runArgs,
|
|
246
249
|
{ encoding: 'utf-8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
247
250
|
);
|
|
248
251
|
const text = (stdout || '').trim();
|
|
@@ -286,8 +289,12 @@ def devtopia_run(tool_name, input_data=None):
|
|
|
286
289
|
input_str = json.dumps(input_data or {})
|
|
287
290
|
try:
|
|
288
291
|
cmd = _resolve_cli()
|
|
292
|
+
force_local = os.environ.get('DEVTOPIA_RUN_LOCAL') == '1'
|
|
293
|
+
run_args = cmd + ['run', tool_name, input_str, '--json', '--quiet']
|
|
294
|
+
if force_local:
|
|
295
|
+
run_args.append('--local')
|
|
289
296
|
result = subprocess.run(
|
|
290
|
-
|
|
297
|
+
run_args,
|
|
291
298
|
capture_output=True, text=True, timeout=30
|
|
292
299
|
)
|
|
293
300
|
if result.returncode != 0:
|
|
@@ -418,6 +425,7 @@ export async function executeTool(toolName, input, options = {}) {
|
|
|
418
425
|
const env = {
|
|
419
426
|
...process.env,
|
|
420
427
|
INPUT: inputStr,
|
|
428
|
+
DEVTOPIA_RUN_LOCAL: '1',
|
|
421
429
|
...(cliArgs ? { DEVTOPIA_CLI_ARGS: JSON.stringify(cliArgs) } : {}),
|
|
422
430
|
};
|
|
423
431
|
const proc = spawn(cmd, [...args, inputStr], {
|
|
@@ -523,6 +531,7 @@ export async function executeLocalFile(filePath, input, options = {}) {
|
|
|
523
531
|
const env = {
|
|
524
532
|
...process.env,
|
|
525
533
|
INPUT: inputStr,
|
|
534
|
+
DEVTOPIA_RUN_LOCAL: '1',
|
|
526
535
|
...(cliArgs ? { DEVTOPIA_CLI_ARGS: JSON.stringify(cliArgs) } : {}),
|
|
527
536
|
};
|
|
528
537
|
const proc = spawn(cmd, [...args, inputStr], {
|
package/dist/index.js
CHANGED
|
@@ -188,11 +188,13 @@ Examples:
|
|
|
188
188
|
// =============================================================================
|
|
189
189
|
program
|
|
190
190
|
.command('run <tool> [input]')
|
|
191
|
-
.description('Run a tool
|
|
191
|
+
.description('Run a tool (sandboxed by default)')
|
|
192
192
|
.option('--json', 'Output JSON only (default)')
|
|
193
193
|
.option('--pretty', 'Pretty-print JSON output (default with --human)')
|
|
194
194
|
.option('--human', 'Human-readable output with logs')
|
|
195
195
|
.option('--quiet', 'Suppress status logs (JSON-only)')
|
|
196
|
+
.option('--sandbox', 'Run in sandboxed execution service (default)')
|
|
197
|
+
.option('--local', 'Run locally (bypass sandbox, dev only)')
|
|
196
198
|
.action((tool, input, options) => run(tool, input, options));
|
|
197
199
|
program
|
|
198
200
|
.command('run-local <file> [input]')
|