devtopia 1.9.0 → 2.0.1

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.
@@ -1,5 +1,39 @@
1
1
  import { executeTool } from '../executor.js';
2
- export async function run(toolName, inputArg) {
2
+ import { API_BASE } from '../config.js';
3
+ import { existsSync } from 'fs';
4
+ import { dirname, join } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ export async function run(toolName, inputArg, options = {}) {
7
+ if (!process.env.DEVTOPIA_CLI) {
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const indexJs = join(__dirname, '..', 'index.js');
10
+ const indexTs = join(__dirname, '..', 'index.ts');
11
+ const indexDist = join(__dirname, '..', '..', 'dist', 'index.js');
12
+ const quotePath = (p) => (p.includes(' ') ? `"${p.replace(/"/g, '\\"')}"` : p);
13
+ if (existsSync(indexJs)) {
14
+ process.env.DEVTOPIA_CLI = `node ${quotePath(indexJs)}`;
15
+ }
16
+ else if (existsSync(indexDist)) {
17
+ process.env.DEVTOPIA_CLI = `node ${quotePath(indexDist)}`;
18
+ }
19
+ else if (existsSync(indexTs)) {
20
+ const tsxCandidates = [
21
+ join(__dirname, '..', '..', 'node_modules', '.bin', 'tsx'),
22
+ join(__dirname, '..', 'node_modules', '.bin', 'tsx'),
23
+ ];
24
+ const tsxBin = tsxCandidates.find((p) => existsSync(p));
25
+ if (tsxBin) {
26
+ process.env.DEVTOPIA_CLI = `node ${quotePath(tsxBin)} ${quotePath(indexTs)}`;
27
+ }
28
+ else {
29
+ process.env.DEVTOPIA_CLI = `npx tsx ${quotePath(indexTs)}`;
30
+ }
31
+ }
32
+ }
33
+ const humanMode = options.human === true;
34
+ const jsonMode = !humanMode;
35
+ const pretty = options.pretty === true || (humanMode && options.pretty !== false);
36
+ const quiet = jsonMode ? (options.quiet !== false) : (options.quiet === true);
3
37
  // Parse input
4
38
  let input = {};
5
39
  if (inputArg) {
@@ -7,19 +41,54 @@ export async function run(toolName, inputArg) {
7
41
  input = JSON.parse(inputArg);
8
42
  }
9
43
  catch {
10
- console.log(`\n❌ Invalid JSON input: ${inputArg}\n`);
11
- process.exit(1);
44
+ if (jsonMode) {
45
+ process.stdout.write(JSON.stringify({ ok: false, error: `Invalid JSON input: ${inputArg}` }) + '\n');
46
+ process.exit(0);
47
+ }
48
+ else {
49
+ console.log(`\n❌ Invalid JSON input: ${inputArg}\n`);
50
+ process.exit(1);
51
+ }
12
52
  }
13
53
  }
14
- console.log(`\n⚡ Running /${toolName} locally...`);
15
- const result = await executeTool(toolName, input);
54
+ if (!jsonMode && !quiet) {
55
+ console.log(`\n⚡ Running /${toolName} locally...`);
56
+ }
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
+ if (jsonMode) {
69
+ const outputIsObject = result.output && typeof result.output === 'object';
70
+ const outputHasError = outputIsObject && (Object.prototype.hasOwnProperty.call(result.output, 'error') ||
71
+ (Object.prototype.hasOwnProperty.call(result.output, 'ok') && result.output.ok === false));
72
+ if (result.success && !outputHasError) {
73
+ process.stdout.write(JSON.stringify(result.output ?? null, null, pretty ? 2 : 0) + '\n');
74
+ }
75
+ else {
76
+ const errMsg = outputIsObject && result.output.error
77
+ ? String(result.output.error)
78
+ : (result.error || 'Execution failed');
79
+ process.stdout.write(JSON.stringify({ ok: false, error: errMsg }, null, pretty ? 2 : 0) + '\n');
80
+ }
81
+ process.exit(0);
82
+ }
16
83
  if (!result.success) {
17
84
  console.log(`\n❌ Execution failed`);
18
85
  console.log(` Error: ${result.error}`);
19
86
  console.log(` Duration: ${result.durationMs}ms\n`);
20
87
  process.exit(1);
21
88
  }
22
- console.log(`\n✅ Success (${result.durationMs}ms)\n`);
89
+ if (!quiet) {
90
+ console.log(`\n✅ Success (${result.durationMs}ms)\n`);
91
+ }
23
92
  // Pretty print output
24
93
  if (typeof result.output === 'object') {
25
94
  console.log(JSON.stringify(result.output, null, 2));
@@ -0,0 +1,5 @@
1
+ interface SearchOptions {
2
+ limit?: string;
3
+ }
4
+ export declare function search(query: string, options?: SearchOptions): Promise<void>;
5
+ export {};
@@ -0,0 +1,52 @@
1
+ import { API_BASE } from '../config.js';
2
+ export async function search(query, options = {}) {
3
+ const limit = parseInt(options.limit || '20');
4
+ try {
5
+ // Try server-side search first
6
+ let results = [];
7
+ try {
8
+ const res = await fetch(`${API_BASE}/api/search?q=${encodeURIComponent(query)}&limit=${limit}`);
9
+ if (res.ok) {
10
+ const data = await res.json();
11
+ results = data.results || [];
12
+ }
13
+ else {
14
+ throw new Error('search endpoint unavailable');
15
+ }
16
+ }
17
+ catch {
18
+ // Fallback: fetch all tools and filter client-side
19
+ console.log(` (using fallback search — server search unavailable)\n`);
20
+ const res = await fetch(`${API_BASE}/api/tools`);
21
+ const data = await res.json();
22
+ const q = query.toLowerCase();
23
+ results = (data.tools || [])
24
+ .filter((t) => t.name.toLowerCase().includes(q) ||
25
+ (t.description && t.description.toLowerCase().includes(q)))
26
+ .slice(0, limit)
27
+ .map((t) => ({
28
+ name: t.name,
29
+ description: t.description,
30
+ language: t.language,
31
+ category: t.category,
32
+ }));
33
+ }
34
+ if (results.length === 0) {
35
+ console.log(`\n No results for "${query}"\n`);
36
+ return;
37
+ }
38
+ console.log(`\n Search: "${query}" (${results.length} results)\n`);
39
+ for (const tool of results) {
40
+ const name = `/${tool.name}`.padEnd(25);
41
+ const desc = (tool.description || 'No description').slice(0, 40).padEnd(40);
42
+ const lang = (tool.language || '').slice(0, 4);
43
+ console.log(` ${name} ${desc} ${lang}`);
44
+ }
45
+ console.log(`\n View source: devtopia cat <tool>`);
46
+ console.log(` Run locally: devtopia run <tool> '{...}'\n`);
47
+ }
48
+ catch (err) {
49
+ console.log(`\n❌ Could not connect to server at ${API_BASE}\n`);
50
+ process.exit(1);
51
+ }
52
+ }
@@ -1 +1 @@
1
- export declare function start(): void;
1
+ export declare function start(): Promise<void>;