@townco/agent 0.1.14 → 0.1.16
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/dist/scaffold/copy-gui.d.ts +1 -1
- package/dist/scaffold/copy-gui.js +14 -134
- package/dist/scaffold/index.js +80 -91
- package/dist/templates/index.js +4 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/templates/index.ts +4 -3
|
@@ -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
|
|
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
|
|
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
|
-
//
|
|
58
|
-
const
|
|
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
|
-
|
|
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
|
}
|
package/dist/scaffold/index.js
CHANGED
|
@@ -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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
const bunInstall = spawn("bun", ["install"], {
|
|
80
|
+
cwd: agentPath,
|
|
81
|
+
stdio: "inherit", // Show output for better debugging
|
|
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
|
}
|
package/dist/templates/index.js
CHANGED
|
@@ -9,8 +9,9 @@ export function getTemplateVars(name, definition) {
|
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
11
|
export function generatePackageJson(vars) {
|
|
12
|
-
// Include
|
|
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 "
|
|
47
|
-
import type { AgentDefinition } from "
|
|
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");
|