@townco/agent 0.1.14 → 0.1.15

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,4 +1,4 @@
1
1
  /**
2
- * Copy GUI app to the agent directory
2
+ * Copy GUI app to the agent directory from @townco/gui-template
3
3
  */
4
4
  export declare function copyGuiApp(agentPath: string): Promise<void>;
@@ -1,71 +1,38 @@
1
- import { existsSync } from "node:fs";
2
1
  import { cp, mkdir, writeFile } from "node:fs/promises";
3
2
  import { createRequire } from "node:module";
4
3
  import { dirname, join } from "node:path";
5
- import { fileURLToPath } from "node:url";
6
4
  const require = createRequire(import.meta.url);
7
5
  /**
8
- * Get the source apps/gui directory
9
- * Tries local dev source first, then falls back to npm package
6
+ * Get the GUI template from @townco/gui-template npm package
10
7
  */
11
8
  function getGuiSourceDir() {
12
- const currentFile = fileURLToPath(import.meta.url);
13
- // Try local source (for development in monorepo)
14
- // When compiled: dist/scaffold/copy-gui.js -> dist/scaffold -> dist -> packages/agent -> packages -> root -> apps/gui
15
- const localPath = join(dirname(currentFile), "..", "..", "..", "..", "apps", "gui");
16
- if (existsSync(localPath)) {
17
- return localPath;
18
- }
19
- // Fall back to npm package (for production via npx)
20
9
  try {
21
10
  const packagePath = require.resolve("@townco/gui-template/package.json");
22
11
  return dirname(packagePath);
23
12
  }
24
13
  catch {
25
- throw new Error("Could not find GUI template. Please ensure @townco/gui-template is installed.");
26
- }
27
- }
28
- /**
29
- * Get the source packages/ui directory
30
- * Tries local dev source first, then falls back to npm package
31
- */
32
- function getUiSourceDir() {
33
- const currentFile = fileURLToPath(import.meta.url);
34
- // Try local source (for development in monorepo)
35
- // When compiled: dist/scaffold/copy-gui.js -> dist/scaffold -> dist -> packages/agent -> packages -> ui
36
- const localPath = join(dirname(currentFile), "..", "..", "..", "ui");
37
- if (existsSync(localPath)) {
38
- return localPath;
39
- }
40
- // Fall back to npm package (for production via npx)
41
- try {
42
- const packagePath = require.resolve("@townco/ui/package.json");
43
- return dirname(packagePath);
44
- }
45
- catch {
46
- throw new Error("Could not find UI package. Please ensure @townco/ui is installed.");
14
+ throw new Error("Could not find @townco/gui-template. Please ensure it is installed.");
47
15
  }
48
16
  }
49
17
  /**
50
- * Copy GUI app to the agent directory
18
+ * Copy GUI app to the agent directory from @townco/gui-template
51
19
  */
52
20
  export async function copyGuiApp(agentPath) {
53
- const sourceDir = getGuiSourceDir();
54
21
  const guiDir = join(agentPath, "gui");
55
22
  // Create gui directory
56
23
  await mkdir(guiDir, { recursive: true });
57
- // Copy everything except node_modules and dist (exclude tsconfig.json as we'll generate it)
58
- const itemsToCopy = ["src", "index.html", "postcss.config.js"];
24
+ // Get GUI source from @townco/gui-template npm package
25
+ const sourceDir = getGuiSourceDir();
26
+ const itemsToCopy = [
27
+ "src",
28
+ "index.html",
29
+ "postcss.config.js",
30
+ "vite.config.ts",
31
+ ];
59
32
  for (const item of itemsToCopy) {
60
33
  const sourcePath = join(sourceDir, item);
61
34
  const targetPath = join(guiDir, item);
62
- try {
63
- await cp(sourcePath, targetPath, { recursive: true });
64
- }
65
- catch (error) {
66
- // Item might not exist, that's okay
67
- console.warn(`Warning: Could not copy ${item}:`, error);
68
- }
35
+ await cp(sourcePath, targetPath, { recursive: true });
69
36
  }
70
37
  // Create a standalone tsconfig.json for the GUI (can't extend from workspace tsconfig)
71
38
  const guiTsConfig = {
@@ -103,65 +70,8 @@ export async function copyGuiApp(agentPath) {
103
70
  exclude: ["node_modules", "dist"],
104
71
  };
105
72
  await writeFile(join(guiDir, "tsconfig.json"), JSON.stringify(guiTsConfig, null, 2));
106
- // Create a custom vite.config.ts with path aliases
107
- const viteConfig = `import { defineConfig } from "vite";
108
- import react from "@vitejs/plugin-react";
109
- import { resolve } from "path";
110
-
111
- export default defineConfig({
112
- plugins: [react()],
113
- resolve: {
114
- alias: {
115
- "@townco/ui": resolve(__dirname, "./ui/src"),
116
- // Exclude Node.js-only modules from browser bundle
117
- "node:child_process": resolve(__dirname, "./polyfills/child_process.js"),
118
- "node:stream": resolve(__dirname, "./polyfills/stream.js"),
119
- },
120
- },
121
- optimizeDeps: {
122
- exclude: ["node:child_process", "node:stream"],
123
- },
124
- });
125
- `;
126
- await writeFile(join(guiDir, "vite.config.ts"), viteConfig);
127
- // Create polyfills directory with stub modules for Node.js-only imports
128
- const polyfillsDir = join(guiDir, "polyfills");
129
- await mkdir(polyfillsDir, { recursive: true });
130
- // Provide stub exports for child_process module
131
- const childProcessPolyfill = `// Polyfill for node:child_process (browser-incompatible)
132
- export const spawn = () => {
133
- throw new Error('child_process.spawn is not available in the browser');
134
- };
135
- export class ChildProcess {}
136
- export default { spawn, ChildProcess };
137
- `;
138
- await writeFile(join(polyfillsDir, "child_process.js"), childProcessPolyfill);
139
- // Provide stub exports for stream module
140
- const streamPolyfill = `// Polyfill for node:stream (browser-incompatible)
141
- export class Readable {
142
- constructor() {
143
- throw new Error('stream.Readable is not available in the browser');
144
- }
145
- }
146
- export class Writable {
147
- constructor() {
148
- throw new Error('stream.Writable is not available in the browser');
149
- }
150
- }
151
- export class Duplex {
152
- constructor() {
153
- throw new Error('stream.Duplex is not available in the browser');
154
- }
155
- }
156
- export class Transform {
157
- constructor() {
158
- throw new Error('stream.Transform is not available in the browser');
159
- }
160
- }
161
- export default { Readable, Writable, Duplex, Transform };
162
- `;
163
- await writeFile(join(polyfillsDir, "stream.js"), streamPolyfill);
164
73
  // Generate a custom package.json for the GUI
74
+ // Use @townco/ui as a dependency instead of copying files
165
75
  const packageJson = {
166
76
  name: "agent-gui",
167
77
  version: "0.0.1",
@@ -173,6 +83,7 @@ export default { Readable, Writable, Duplex, Transform };
173
83
  preview: "vite preview",
174
84
  },
175
85
  dependencies: {
86
+ "@townco/ui": "^0.1.0",
176
87
  "@agentclientprotocol/sdk": "^0.5.1",
177
88
  "@radix-ui/react-dialog": "^1.1.15",
178
89
  "@radix-ui/react-label": "^2.1.8",
@@ -203,35 +114,4 @@ export default { Readable, Writable, Duplex, Transform };
203
114
  },
204
115
  };
205
116
  await writeFile(join(guiDir, "package.json"), JSON.stringify(packageJson, null, 2));
206
- // Copy UI package components and styles
207
- const uiSourceDir = getUiSourceDir();
208
- const uiTargetDir = join(guiDir, "ui");
209
- await mkdir(uiTargetDir, { recursive: true });
210
- // Copy the entire UI src directory (which includes styles)
211
- const sourcePath = join(uiSourceDir, "src");
212
- const targetPath = join(uiTargetDir, "src");
213
- try {
214
- await cp(sourcePath, targetPath, { recursive: true });
215
- }
216
- catch (error) {
217
- console.warn(`Warning: Could not copy UI src:`, error);
218
- }
219
- // Create a tsconfig for the ui directory
220
- const uiTsConfig = {
221
- compilerOptions: {
222
- target: "ESNext",
223
- module: "ESNext",
224
- jsx: "react-jsx",
225
- moduleResolution: "bundler",
226
- lib: ["ESNext", "DOM", "DOM.Iterable"],
227
- strict: true,
228
- esModuleInterop: true,
229
- skipLibCheck: true,
230
- forceConsistentCasingInFileNames: true,
231
- resolveJsonModule: true,
232
- allowSyntheticDefaultImports: true,
233
- },
234
- include: ["**/*.ts", "**/*.tsx"],
235
- };
236
- await writeFile(join(uiTargetDir, "tsconfig.json"), JSON.stringify(uiTsConfig, null, 2));
237
117
  }
@@ -2,105 +2,94 @@ import { spawn } from "node:child_process";
2
2
  import { chmod, mkdir, writeFile } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { agentExists, ensureAgentsDir, getAgentPath } from "../storage";
5
- import {
6
- generateAgentJson,
7
- generateBinTs,
8
- generateEnvExample,
9
- generateGitignore,
10
- generateIndexTs,
11
- generatePackageJson,
12
- generateReadme,
13
- generateTsConfig,
14
- getTemplateVars,
15
- } from "../templates";
16
- import { bundleAgentDependencies } from "./bundle";
5
+ import { generateAgentJson, generateBinTs, generateEnvExample, generateGitignore, generateIndexTs, generatePackageJson, generateReadme, generateTsConfig, getTemplateVars, } from "../templates";
17
6
  import { copyGuiApp } from "./copy-gui";
18
7
  /**
19
8
  * Scaffold a new agent package
20
9
  */
21
10
  export async function scaffoldAgent(options) {
22
- const { name, definition, overwrite = false, includeGui = true } = options;
23
- try {
24
- // Ensure base directory exists
25
- await ensureAgentsDir();
26
- // Check if agent already exists
27
- const exists = await agentExists(name);
28
- if (exists && !overwrite) {
29
- return {
30
- success: false,
31
- path: getAgentPath(name),
32
- error: `Agent "${name}" already exists. Use overwrite option to replace it.`,
33
- };
34
- }
35
- const agentPath = getAgentPath(name);
36
- // Create the agent directory
37
- await mkdir(agentPath, { recursive: true });
38
- const vars = getTemplateVars(name, definition);
39
- // Generate all template files
40
- const files = [
41
- { path: "package.json", content: generatePackageJson(vars) },
42
- { path: "agent.json", content: generateAgentJson(vars) },
43
- { path: "index.ts", content: generateIndexTs() },
44
- { path: "bin.ts", content: generateBinTs(), executable: true },
45
- { path: "tsconfig.json", content: generateTsConfig() },
46
- { path: "README.md", content: generateReadme(vars) },
47
- { path: ".gitignore", content: generateGitignore() },
48
- ];
49
- // Add .env.example if needed
50
- const envExample = generateEnvExample(vars);
51
- if (envExample) {
52
- files.push({ path: ".env.example", content: envExample });
53
- }
54
- // Write all files
55
- for (const file of files) {
56
- const filePath = join(agentPath, file.path);
57
- await writeFile(filePath, file.content, "utf-8");
58
- // Make executable if needed
59
- if (file.executable) {
60
- await chmod(filePath, 0o755);
61
- }
62
- }
63
- // Bundle agent dependencies (copy lib files)
64
- await bundleAgentDependencies(agentPath);
65
- // Copy GUI app if requested
66
- if (includeGui) {
67
- await copyGuiApp(agentPath);
68
- // Run bun install in the GUI directory
69
- const guiPath = join(agentPath, "gui");
70
- await runBunInstall(guiPath);
71
- }
72
- // Run bun install in agent root
73
- await runBunInstall(agentPath);
74
- return {
75
- success: true,
76
- path: agentPath,
77
- };
78
- } catch (error) {
79
- return {
80
- success: false,
81
- path: getAgentPath(name),
82
- error: error instanceof Error ? error.message : String(error),
83
- };
84
- }
11
+ const { name, definition, overwrite = false, includeGui = true } = options;
12
+ try {
13
+ // Ensure base directory exists
14
+ await ensureAgentsDir();
15
+ // Check if agent already exists
16
+ const exists = await agentExists(name);
17
+ if (exists && !overwrite) {
18
+ return {
19
+ success: false,
20
+ path: getAgentPath(name),
21
+ error: `Agent "${name}" already exists. Use overwrite option to replace it.`,
22
+ };
23
+ }
24
+ const agentPath = getAgentPath(name);
25
+ // Create the agent directory
26
+ await mkdir(agentPath, { recursive: true });
27
+ const vars = getTemplateVars(name, definition);
28
+ // Generate all template files
29
+ const files = [
30
+ { path: "package.json", content: generatePackageJson(vars) },
31
+ { path: "agent.json", content: generateAgentJson(vars) },
32
+ { path: "index.ts", content: generateIndexTs() },
33
+ { path: "bin.ts", content: generateBinTs(), executable: true },
34
+ { path: "tsconfig.json", content: generateTsConfig() },
35
+ { path: "README.md", content: generateReadme(vars) },
36
+ { path: ".gitignore", content: generateGitignore() },
37
+ ];
38
+ // Add .env.example if needed
39
+ const envExample = generateEnvExample(vars);
40
+ if (envExample) {
41
+ files.push({ path: ".env.example", content: envExample });
42
+ }
43
+ // Write all files
44
+ for (const file of files) {
45
+ const filePath = join(agentPath, file.path);
46
+ await writeFile(filePath, file.content, "utf-8");
47
+ // Make executable if needed
48
+ if (file.executable) {
49
+ await chmod(filePath, 0o755);
50
+ }
51
+ }
52
+ // Copy GUI app if requested
53
+ if (includeGui) {
54
+ await copyGuiApp(agentPath);
55
+ // Run bun install in the GUI directory
56
+ const guiPath = join(agentPath, "gui");
57
+ await runBunInstall(guiPath);
58
+ }
59
+ // Run bun install in agent root to fetch dependencies from npm
60
+ await runBunInstall(agentPath);
61
+ return {
62
+ success: true,
63
+ path: agentPath,
64
+ };
65
+ }
66
+ catch (error) {
67
+ return {
68
+ success: false,
69
+ path: getAgentPath(name),
70
+ error: error instanceof Error ? error.message : String(error),
71
+ };
72
+ }
85
73
  }
86
74
  /**
87
75
  * Run bun install in the agent directory
88
76
  */
89
77
  function runBunInstall(agentPath) {
90
- return new Promise((resolve, reject) => {
91
- const bunInstall = spawn("bun", ["install"], {
92
- cwd: agentPath,
93
- stdio: "ignore",
94
- });
95
- bunInstall.on("close", (code) => {
96
- if (code === 0) {
97
- resolve();
98
- } else {
99
- reject(new Error(`bun install failed with code ${code}`));
100
- }
101
- });
102
- bunInstall.on("error", (error) => {
103
- reject(error);
104
- });
105
- });
78
+ return new Promise((resolve, reject) => {
79
+ const bunInstall = spawn("bun", ["install"], {
80
+ cwd: agentPath,
81
+ stdio: "ignore",
82
+ });
83
+ bunInstall.on("close", (code) => {
84
+ if (code === 0) {
85
+ resolve();
86
+ }
87
+ else {
88
+ reject(new Error(`bun install failed with code ${code}`));
89
+ }
90
+ });
91
+ bunInstall.on("error", (error) => {
92
+ reject(error);
93
+ });
94
+ });
106
95
  }
@@ -9,8 +9,9 @@ export function getTemplateVars(name, definition) {
9
9
  };
10
10
  }
11
11
  export function generatePackageJson(vars) {
12
- // Include all dependencies since the bundled lib/runner includes all tool implementations
12
+ // Include @townco/agent as a dependency instead of bundling
13
13
  const dependencies = {
14
+ "@townco/agent": "^0.1.14",
14
15
  "@agentclientprotocol/sdk": "^0.5.1",
15
16
  "@langchain/anthropic": "^1.0.0",
16
17
  "@langchain/core": "^1.0.3",
@@ -43,8 +44,8 @@ export function generatePackageJson(vars) {
43
44
  export function generateIndexTs() {
44
45
  return `import { readFileSync } from "node:fs";
45
46
  import { join } from "node:path";
46
- import { makeHttpTransport, makeStdioTransport } from "./lib/acp-server/index.js";
47
- import type { AgentDefinition } from "./lib/definition/index.js";
47
+ import { makeHttpTransport, makeStdioTransport } from "@townco/agent/acp-server";
48
+ import type { AgentDefinition } from "@townco/agent/definition";
48
49
 
49
50
  // Load agent definition from JSON file
50
51
  const configPath = join(import.meta.dir, "agent.json");