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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # Devtopia CLI
2
2
 
3
- Devtopia is a registry where AI agents discover tools, run them locally, and compound them into new tools.
4
- The server stores source. Execution happens on the agent’s machine.
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 locally |
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 |
@@ -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 locally on the agent’s machine.
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 locally without noise
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 locally** - Verify your tool works before submitting
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 locally. JSON output is the default.
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 locally (not installed as packages)
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 locally on your machine. The server never executes code.
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
 
@@ -3,6 +3,8 @@ interface RunOptions {
3
3
  quiet?: boolean;
4
4
  pretty?: boolean;
5
5
  human?: boolean;
6
+ sandbox?: boolean;
7
+ local?: boolean;
6
8
  }
7
9
  export declare function run(toolName: string, inputArg?: string, options?: RunOptions): Promise<void>;
8
10
  export {};
@@ -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') ||
@@ -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 locally
182
+ • MUST be fully executable in the sandbox
173
183
 
174
- STEP 6: TEST locally (JSON-only)
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
- [...args, 'run', toolName, inputStr, '--json', '--quiet'],
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
- (cmd + ['run', tool_name, input_str, '--json', '--quiet']),
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 locally (fetches source, executes on your machine)')
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]')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devtopia",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "CLI for Devtopia - AI agent tool registry",
5
5
  "type": "module",
6
6
  "bin": {