@aprovan/stitchery 0.1.0-dev.6bd527d → 0.1.0-dev.9d1cd22

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.
@@ -10,27 +10,27 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- CJS dist/index.cjs 43.77 KB
14
- CJS dist/server/index.cjs 43.51 KB
15
- CJS dist/cli.cjs 46.20 KB
16
- CJS dist/index.cjs.map 71.56 KB
17
- CJS dist/server/index.cjs.map 71.11 KB
18
- CJS dist/cli.cjs.map 76.69 KB
19
- CJS ⚡️ Build success in 89ms
20
- ESM dist/index.js 41.31 KB
21
- ESM dist/cli.js 44.21 KB
22
- ESM dist/server/index.js 41.23 KB
23
- ESM dist/index.js.map 71.11 KB
24
- ESM dist/cli.js.map 76.70 KB
25
- ESM dist/server/index.js.map 71.13 KB
26
- ESM ⚡️ Build success in 91ms
13
+ CJS dist/index.cjs 45.48 KB
14
+ CJS dist/server/index.cjs 45.22 KB
15
+ CJS dist/cli.cjs 48.14 KB
16
+ CJS dist/index.cjs.map 74.97 KB
17
+ CJS dist/server/index.cjs.map 74.52 KB
18
+ CJS dist/cli.cjs.map 80.58 KB
19
+ CJS ⚡️ Build success in 71ms
20
+ ESM dist/index.js 43.02 KB
21
+ ESM dist/cli.js 46.13 KB
22
+ ESM dist/server/index.js 42.94 KB
23
+ ESM dist/index.js.map 74.52 KB
24
+ ESM dist/cli.js.map 80.59 KB
25
+ ESM dist/server/index.js.map 74.54 KB
26
+ ESM ⚡️ Build success in 74ms
27
27
  DTS Build start
28
- DTS ⚡️ Build success in 4995ms
28
+ DTS ⚡️ Build success in 5221ms
29
29
  DTS dist/cli.d.ts 20.00 B
30
- DTS dist/index.d.ts 13.71 KB
30
+ DTS dist/index.d.ts 13.76 KB
31
31
  DTS dist/server/index.d.ts 122.00 B
32
- DTS dist/index-DNQY1UAP.d.ts 5.67 KB
32
+ DTS dist/index-JTHGJYLz.d.ts 5.99 KB
33
33
  DTS dist/cli.d.cts 20.00 B
34
- DTS dist/index.d.cts 13.71 KB
34
+ DTS dist/index.d.cts 13.76 KB
35
35
  DTS dist/server/index.d.cts 123.00 B
36
- DTS dist/index-DNQY1UAP.d.cts 5.67 KB
36
+ DTS dist/index-JTHGJYLz.d.cts 5.99 KB
package/dist/cli.cjs CHANGED
@@ -27,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_path2 = __toESM(require("path"), 1);
28
28
  var import_fs2 = __toESM(require("fs"), 1);
29
29
  var import_commander = require("commander");
30
+ var import_devtools = require("@aprovan/devtools");
30
31
 
31
32
  // src/server/index.ts
32
33
  var import_node_http = require("http");
@@ -111,7 +112,8 @@ export function Card() {
111
112
  ### Requirements
112
113
  - DO think heavily about correctness of code and syntax
113
114
  - DO keep things simple and self-contained
114
- - ALWAYS include the \`path\` attribute specifying the file location. Be generic with the name and describe the general component's use
115
+ - ALWAYS include the \`path\` attribute specifying the file location.
116
+ - ALWAYS use generic component/path names that is not dependent on arguments (e.g. 'dinner.tsx' not 'spaghetti.tsx')
115
117
  - ALWAYS output the COMPLETE code block with opening \`\`\`tsx and closing \`\`\` markers
116
118
  - Use \`note\` attribute to describe what each code block does (optional but encouraged)
117
119
  - NEVER truncate or cut off code - finish the entire component before stopping
@@ -538,7 +540,7 @@ function joinRelPath(base, name) {
538
540
  }
539
541
  async function listAllFiles(rootDir, relPath) {
540
542
  const targetPath = resolvePath(rootDir, relPath);
541
- let entries = [];
543
+ let entries;
542
544
  try {
543
545
  entries = await (0, import_promises.readdir)(targetPath, { withFileTypes: true });
544
546
  } catch {
@@ -546,7 +548,7 @@ async function listAllFiles(rootDir, relPath) {
546
548
  }
547
549
  const results = [];
548
550
  for (const entry of entries) {
549
- const entryRelPath = joinRelPath(relPath, entry.name);
551
+ const entryRelPath = joinRelPath(relPath, String(entry.name));
550
552
  if (entry.isDirectory()) {
551
553
  results.push(...await listAllFiles(rootDir, entryRelPath));
552
554
  } else {
@@ -832,7 +834,15 @@ var ServiceRegistry = class {
832
834
  this.backends.push(backend);
833
835
  if (toolInfos) {
834
836
  for (const info of toolInfos) {
835
- this.toolInfo.set(info.name, info);
837
+ const infoWithInterface = {
838
+ ...info,
839
+ typescriptInterface: this.generateTypeScriptInterfaceFromSchema(
840
+ info.name,
841
+ info.parameters,
842
+ info.outputs
843
+ )
844
+ };
845
+ this.toolInfo.set(info.name, infoWithInterface);
836
846
  const tool = {
837
847
  description: info.description,
838
848
  inputSchema: (0, import_ai2.jsonSchema)(
@@ -851,20 +861,59 @@ var ServiceRegistry = class {
851
861
  */
852
862
  generateTypeScriptInterface(name, tool) {
853
863
  const schema = tool.inputSchema;
854
- const props = schema?.properties ?? {};
855
- const required = schema?.required ?? [];
856
- const params = Object.entries(props).map(([key, val]) => {
857
- const optional = !required.includes(key) ? "?" : "";
864
+ return this.generateTypeScriptInterfaceFromSchema(name, schema);
865
+ }
866
+ /**
867
+ * Convert JSON Schema type to TypeScript type
868
+ */
869
+ schemaTypeToTs(schema) {
870
+ const type = schema.type;
871
+ const items = schema.items;
872
+ const properties = schema.properties;
873
+ const required = schema.required ?? [];
874
+ if (type === "number" || type === "integer") return "number";
875
+ if (type === "boolean") return "boolean";
876
+ if (type === "null") return "null";
877
+ if (type === "array") {
878
+ if (items) {
879
+ return `${this.schemaTypeToTs(items)}[]`;
880
+ }
881
+ return "unknown[]";
882
+ }
883
+ if (type === "object" && properties) {
884
+ const props = Object.entries(properties).map(([key, val]) => {
885
+ const optional = !required.includes(key) ? "?" : "";
886
+ const propType = this.schemaTypeToTs(val);
887
+ return `${key}${optional}: ${propType}`;
888
+ }).join("; ");
889
+ return `{ ${props} }`;
890
+ }
891
+ if (type === "object") return "Record<string, unknown>";
892
+ return "string";
893
+ }
894
+ /**
895
+ * Generate TypeScript interface from JSON Schema (supports both inputs and outputs)
896
+ */
897
+ generateTypeScriptInterfaceFromSchema(name, inputSchema, outputSchema) {
898
+ const safeName = name.replace(/[^a-zA-Z0-9]/g, "_");
899
+ const inputProps = inputSchema?.properties ?? {};
900
+ const inputRequired = inputSchema?.required ?? [];
901
+ const inputParams = Object.entries(inputProps).map(([key, val]) => {
902
+ const optional = !inputRequired.includes(key) ? "?" : "";
858
903
  const type = val.type === "number" ? "number" : val.type === "boolean" ? "boolean" : val.type === "array" ? "unknown[]" : val.type === "object" ? "Record<string, unknown>" : "string";
859
904
  const comment = val.description ? ` // ${val.description}` : "";
860
905
  return ` ${key}${optional}: ${type};${comment}`;
861
906
  }).join("\n");
862
- return `interface ${name.replace(
863
- /[^a-zA-Z0-9]/g,
864
- "_"
865
- )}Args {
866
- ${params}
907
+ let result = `interface ${safeName}Args {
908
+ ${inputParams}
867
909
  }`;
910
+ if (outputSchema && Object.keys(outputSchema).length > 0) {
911
+ const responseType = this.schemaTypeToTs(outputSchema);
912
+ result += `
913
+
914
+ type ${safeName}Response = ${responseType};`;
915
+ }
916
+ return result;
868
917
  }
869
918
  /**
870
919
  * Convert internal tool name (namespace.procedure) to LLM-safe name (namespace_procedure)
@@ -1013,7 +1062,7 @@ function generateServicesPrompt(registry) {
1013
1062
  existing.push(service);
1014
1063
  byNamespace.set(service.namespace, existing);
1015
1064
  }
1016
- let prompt = `## Available Services
1065
+ let prompt = `## Services
1017
1066
 
1018
1067
  The following services are available for generated widgets to call:
1019
1068
 
@@ -1320,27 +1369,31 @@ async function createStitcheryServer(config = {}) {
1320
1369
  // src/cli.ts
1321
1370
  var program = new import_commander.Command();
1322
1371
  program.name("stitchery").description("Backend services for LLM-generated artifacts").version("0.1.0");
1323
- program.command("serve").description("Start the stitchery server").option("-p, --port <port>", "Port to listen on", "6434").option("-h, --host <host>", "Host to bind to", "127.0.0.1").option(
1372
+ program.command("serve").description("Start the stitchery server").option(
1373
+ "-p, --port <port>",
1374
+ "Port to listen on (auto-finds available if in use)",
1375
+ "6434"
1376
+ ).option("-h, --host <host>", "Host to bind to", "127.0.0.1").option(
1324
1377
  "--copilot-proxy-url <url>",
1325
1378
  "Copilot proxy URL",
1326
1379
  "http://127.0.0.1:6433/v1"
1327
- ).option(
1380
+ ).option("--strict", "Fail if the specified port is in use", false).option(
1328
1381
  "--mcp <servers...>",
1329
1382
  "MCP server commands (format: name:command:arg1,arg2)"
1330
1383
  ).option("--utcp-config <path>", "Load UTCP configuration from JSON file").option(
1331
1384
  "--local-package <packages...>",
1332
1385
  "Local package overrides (format: name:path)"
1333
- ).option(
1334
- "--vfs-dir <path>",
1335
- "Directory for virtual file system storage",
1336
- ".working/widgets"
1337
- ).option(
1386
+ ).option("--vfs-dir <path>", "Directory for virtual file system storage", ".").option(
1338
1387
  "--vfs-use-paths",
1339
1388
  "Use file paths from code blocks instead of UUIDs for VFS storage"
1340
1389
  ).option("-v, --verbose", "Enable verbose logging").action(async (options) => {
1341
1390
  if (options.verbose) {
1342
1391
  console.log("[stitchery] CLI options:", JSON.stringify(options, null, 2));
1343
1392
  }
1393
+ let port = parseInt(options.port, 10);
1394
+ if (!options.strict) {
1395
+ port = await (0, import_devtools.getAvailablePort)(port);
1396
+ }
1344
1397
  const mcpServers = (options.mcp ?? []).map((spec) => {
1345
1398
  const [name, command, ...rest] = spec.split(":");
1346
1399
  const rawArgs = rest.join(":").split(",").filter(Boolean);
@@ -1378,7 +1431,7 @@ program.command("serve").description("Start the stitchery server").option("-p, -
1378
1431
  console.log("[stitchery] VFS directory:", vfsDir);
1379
1432
  }
1380
1433
  const server = await createStitcheryServer({
1381
- port: parseInt(options.port, 10),
1434
+ port,
1382
1435
  host: options.host,
1383
1436
  copilotProxyUrl: options.copilotProxyUrl,
1384
1437
  mcpServers,
@@ -1388,8 +1441,8 @@ program.command("serve").description("Start the stitchery server").option("-p, -
1388
1441
  vfsUsePaths: options.vfsUsePaths ?? false,
1389
1442
  verbose: options.verbose
1390
1443
  });
1391
- const { port, host } = await server.start();
1392
- console.log(`Stitchery server running at http://${host}:${port}`);
1444
+ const addr = await server.start();
1445
+ console.log(`Stitchery server running at http://${addr.host}:${addr.port}`);
1393
1446
  process.on("SIGINT", async () => {
1394
1447
  console.log("\nShutting down...");
1395
1448
  await server.stop();