towns-agent 2.0.4
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 +140 -0
- package/dist/index.js +1190 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1182 -0
- package/dist/index.mjs.map +1 -0
- package/index.js +2 -0
- package/package.json +64 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// src/index.ts
|
|
26
|
+
var import_path2 = require("path");
|
|
27
|
+
var import_dotenv = require("dotenv");
|
|
28
|
+
var import_picocolors8 = require("picocolors");
|
|
29
|
+
|
|
30
|
+
// src/modules/init.ts
|
|
31
|
+
var fs2 = __toESM(require("fs"));
|
|
32
|
+
var path2 = __toESM(require("path"));
|
|
33
|
+
var import_prompts2 = __toESM(require("prompts"));
|
|
34
|
+
var import_picocolors2 = require("picocolors");
|
|
35
|
+
var jsonc = __toESM(require("jsonc-parser"));
|
|
36
|
+
|
|
37
|
+
// src/modules/utils.ts
|
|
38
|
+
var fs = __toESM(require("fs"));
|
|
39
|
+
var path = __toESM(require("path"));
|
|
40
|
+
var import_cross_spawn = __toESM(require("cross-spawn"));
|
|
41
|
+
var import_prompts = __toESM(require("prompts"));
|
|
42
|
+
var import_picocolors = __toESM(require("picocolors"));
|
|
43
|
+
var getPackageManager = () => {
|
|
44
|
+
if (process.env.npm_config_user_agent) {
|
|
45
|
+
const agent = process.env.npm_config_user_agent;
|
|
46
|
+
if (agent.startsWith("yarn")) return "yarn";
|
|
47
|
+
if (agent.startsWith("npm")) return "npm";
|
|
48
|
+
if (agent.startsWith("pnpm")) return "pnpm";
|
|
49
|
+
if (agent.startsWith("bun")) return "bun";
|
|
50
|
+
}
|
|
51
|
+
return "npm";
|
|
52
|
+
};
|
|
53
|
+
function getInstallCommand(packageManager) {
|
|
54
|
+
switch (packageManager) {
|
|
55
|
+
case "bun":
|
|
56
|
+
return "bun install";
|
|
57
|
+
case "yarn":
|
|
58
|
+
return "yarn";
|
|
59
|
+
case "pnpm":
|
|
60
|
+
return "pnpm install";
|
|
61
|
+
default:
|
|
62
|
+
return "npm install";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function getRunCommand(packageManager, script) {
|
|
66
|
+
switch (packageManager) {
|
|
67
|
+
case "bun":
|
|
68
|
+
return `bun run ${script}`;
|
|
69
|
+
case "yarn":
|
|
70
|
+
return `yarn ${script}`;
|
|
71
|
+
case "pnpm":
|
|
72
|
+
return `pnpm ${script}`;
|
|
73
|
+
default:
|
|
74
|
+
return `npm run ${script}`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function getDlxCommand(packageManager) {
|
|
78
|
+
switch (packageManager) {
|
|
79
|
+
case "bun":
|
|
80
|
+
return "bunx";
|
|
81
|
+
case "yarn":
|
|
82
|
+
return "yarn dlx";
|
|
83
|
+
case "pnpm":
|
|
84
|
+
return "pnpm dlx";
|
|
85
|
+
default:
|
|
86
|
+
return "npx";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function runCommand(command, args, opts = { cwd: void 0, silent: false }) {
|
|
90
|
+
return new Promise((resolve3, reject) => {
|
|
91
|
+
const child = (0, import_cross_spawn.default)(command, args, {
|
|
92
|
+
stdio: opts.silent ? "ignore" : "inherit",
|
|
93
|
+
cwd: opts.cwd,
|
|
94
|
+
shell: process.platform === "win32"
|
|
95
|
+
});
|
|
96
|
+
child.on("close", (code) => {
|
|
97
|
+
if (code !== 0) {
|
|
98
|
+
reject(new Error(`Command failed with exit code ${code}`));
|
|
99
|
+
} else {
|
|
100
|
+
resolve3();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
child.on("error", reject);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async function getLatestTownsProtocolVersion() {
|
|
107
|
+
return new Promise((resolve3, reject) => {
|
|
108
|
+
const child = (0, import_cross_spawn.default)("npm", ["view", "@towns-labs/agent", "version"], {
|
|
109
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
110
|
+
});
|
|
111
|
+
let output = "";
|
|
112
|
+
child.stdout?.on("data", (data) => {
|
|
113
|
+
output += data.toString();
|
|
114
|
+
});
|
|
115
|
+
child.on("close", (code) => {
|
|
116
|
+
if (code !== 0) {
|
|
117
|
+
reject(new Error("Failed to fetch latest @towns-labs/agent version"));
|
|
118
|
+
} else {
|
|
119
|
+
resolve3(output.trim());
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
child.on("error", reject);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function getLatestSdkTag() {
|
|
126
|
+
const tagsResult = import_cross_spawn.default.sync(
|
|
127
|
+
"git",
|
|
128
|
+
["ls-remote", "--tags", "https://github.com/HereNotThere/chat.git", "@towns-labs/sdk@*"],
|
|
129
|
+
{ encoding: "utf8" }
|
|
130
|
+
);
|
|
131
|
+
if (tagsResult.status !== 0 || !tagsResult.stdout) return null;
|
|
132
|
+
const tags = tagsResult.stdout.split("\n").filter(Boolean).map((line) => {
|
|
133
|
+
const [_hash, ref] = line.split(" ");
|
|
134
|
+
const tag = ref.replace("refs/tags/", "").replace(/\^{}$/, "");
|
|
135
|
+
const match = tag.match(/^@towns-protocol\/sdk@(\d+)\.(\d+)\.(\d+)$/);
|
|
136
|
+
if (!match) return null;
|
|
137
|
+
return {
|
|
138
|
+
tag,
|
|
139
|
+
version: [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])]
|
|
140
|
+
};
|
|
141
|
+
}).filter(
|
|
142
|
+
(item) => item !== null && Array.isArray(item.version) && item.version.length === 3
|
|
143
|
+
).sort((a, b) => {
|
|
144
|
+
for (let i = 0; i < 3; i++) {
|
|
145
|
+
if (a.version[i] !== b.version[i]) {
|
|
146
|
+
return b.version[i] - a.version[i];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return 0;
|
|
150
|
+
});
|
|
151
|
+
return tags.length > 0 ? tags[0].tag : null;
|
|
152
|
+
}
|
|
153
|
+
async function cloneTemplate(packagePath, targetDir) {
|
|
154
|
+
console.log(import_picocolors.default.blue("Cloning template from GitHub..."));
|
|
155
|
+
const tempDir = `${targetDir}-temp`;
|
|
156
|
+
const fullTemplatePath = `packages/examples/${packagePath}`;
|
|
157
|
+
const latestSdkTag = getLatestSdkTag();
|
|
158
|
+
if (!latestSdkTag) {
|
|
159
|
+
console.error(import_picocolors.default.red("Failed to get latest SDK tag."));
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
const cloneResult = import_cross_spawn.default.sync(
|
|
163
|
+
"git",
|
|
164
|
+
[
|
|
165
|
+
"clone",
|
|
166
|
+
"--no-checkout",
|
|
167
|
+
"--depth",
|
|
168
|
+
"1",
|
|
169
|
+
"--sparse",
|
|
170
|
+
"--branch",
|
|
171
|
+
latestSdkTag,
|
|
172
|
+
"https://github.com/towns-protocol/towns.git",
|
|
173
|
+
tempDir
|
|
174
|
+
],
|
|
175
|
+
{ stdio: "pipe" }
|
|
176
|
+
);
|
|
177
|
+
if (cloneResult.status !== 0) return false;
|
|
178
|
+
const sparseResult = import_cross_spawn.default.sync("git", ["sparse-checkout", "set", fullTemplatePath], {
|
|
179
|
+
stdio: "pipe",
|
|
180
|
+
cwd: tempDir
|
|
181
|
+
});
|
|
182
|
+
if (sparseResult.status !== 0) {
|
|
183
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
const checkoutResult = import_cross_spawn.default.sync("git", ["checkout"], {
|
|
187
|
+
stdio: "pipe",
|
|
188
|
+
cwd: tempDir
|
|
189
|
+
});
|
|
190
|
+
if (checkoutResult.status !== 0) {
|
|
191
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
const sourceDir = path.join(tempDir, fullTemplatePath);
|
|
195
|
+
if (!fs.existsSync(sourceDir)) {
|
|
196
|
+
console.error(import_picocolors.default.red(`
|
|
197
|
+
Template directory not found at ${sourceDir}`));
|
|
198
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
202
|
+
fs.cpSync(sourceDir, targetDir, {
|
|
203
|
+
recursive: true,
|
|
204
|
+
filter: () => {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
209
|
+
console.log(import_picocolors.default.green("\u2713"), "Template cloned successfully!");
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
function applyReplacements(targetDir, replacements) {
|
|
213
|
+
function processDirectory(dir) {
|
|
214
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
215
|
+
for (const entry of entries) {
|
|
216
|
+
const fullPath = path.join(dir, entry.name);
|
|
217
|
+
if (entry.isDirectory()) {
|
|
218
|
+
if (entry.name === "node_modules" || entry.name === "dist") {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
processDirectory(fullPath);
|
|
222
|
+
} else {
|
|
223
|
+
let content = fs.readFileSync(fullPath, "utf-8");
|
|
224
|
+
let modified = false;
|
|
225
|
+
if (entry.name === "package.json" || entry.name.endsWith(".ts") || entry.name.endsWith(".js")) {
|
|
226
|
+
for (const [search, replace] of replacements) {
|
|
227
|
+
const regex = new RegExp(search, "g");
|
|
228
|
+
if (regex.test(content)) {
|
|
229
|
+
content = content.replace(regex, replace);
|
|
230
|
+
modified = true;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (modified) {
|
|
234
|
+
fs.writeFileSync(fullPath, content);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
processDirectory(targetDir);
|
|
241
|
+
}
|
|
242
|
+
function printSuccess(projectName, packageManager) {
|
|
243
|
+
console.log(import_picocolors.default.green("\u2713"), "Bot project created successfully!");
|
|
244
|
+
console.log();
|
|
245
|
+
console.log("Next steps:");
|
|
246
|
+
console.log(import_picocolors.default.cyan(` cd ${projectName}`));
|
|
247
|
+
console.log(import_picocolors.default.cyan(` ${getInstallCommand(packageManager)}`));
|
|
248
|
+
console.log("Set up your environment variables:");
|
|
249
|
+
console.log(import_picocolors.default.cyan(" cp .env.sample .env"));
|
|
250
|
+
console.log(" Edit .env with your bot credentials");
|
|
251
|
+
console.log("Start your bot:");
|
|
252
|
+
console.log(import_picocolors.default.cyan(` ${getRunCommand(packageManager, "dev")}`));
|
|
253
|
+
}
|
|
254
|
+
async function initializeGitRepository(targetDir) {
|
|
255
|
+
try {
|
|
256
|
+
await runCommand("git", ["init"], { cwd: targetDir, silent: true });
|
|
257
|
+
await runCommand("git", ["add", "."], { cwd: targetDir, silent: true });
|
|
258
|
+
await runCommand("git", ["commit", "-m", "feat: towns bot scaffolding"], {
|
|
259
|
+
cwd: targetDir,
|
|
260
|
+
silent: true
|
|
261
|
+
});
|
|
262
|
+
return true;
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.log(
|
|
265
|
+
import_picocolors.default.yellow("\u26A0"),
|
|
266
|
+
"Failed to initialize git repository:",
|
|
267
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
268
|
+
);
|
|
269
|
+
console.log(import_picocolors.default.yellow(" You can manually initialize git later with: git init"));
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
var TOWNS_SKILL_REPO = "https://github.com/towns-protocol/skills.git";
|
|
274
|
+
var AGENTS_SKILL_FOLDERS = [".claude/skills", ".codex/skills"];
|
|
275
|
+
async function installTownsSkills(projectDir) {
|
|
276
|
+
const tempDir = `${projectDir}-skills-temp`;
|
|
277
|
+
try {
|
|
278
|
+
const cloneResult = import_cross_spawn.default.sync(
|
|
279
|
+
"git",
|
|
280
|
+
["clone", "--depth", "1", "--filter=blob:none", "--sparse", TOWNS_SKILL_REPO, tempDir],
|
|
281
|
+
{ stdio: "pipe" }
|
|
282
|
+
);
|
|
283
|
+
if (cloneResult.status !== 0) {
|
|
284
|
+
if (fs.existsSync(tempDir)) {
|
|
285
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
const sparseResult = import_cross_spawn.default.sync("git", ["sparse-checkout", "set", "skills"], {
|
|
290
|
+
stdio: "pipe",
|
|
291
|
+
cwd: tempDir
|
|
292
|
+
});
|
|
293
|
+
if (sparseResult.status !== 0) {
|
|
294
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
const checkoutResult = import_cross_spawn.default.sync("git", ["checkout"], {
|
|
298
|
+
stdio: "pipe",
|
|
299
|
+
cwd: tempDir
|
|
300
|
+
});
|
|
301
|
+
if (checkoutResult.status !== 0) {
|
|
302
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
const sourceSkillsDir = path.join(tempDir, "skills");
|
|
306
|
+
if (!fs.existsSync(sourceSkillsDir)) {
|
|
307
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
const skillDirs = fs.readdirSync(sourceSkillsDir, { withFileTypes: true });
|
|
311
|
+
for (const skillFolder of AGENTS_SKILL_FOLDERS) {
|
|
312
|
+
const targetDir = path.join(projectDir, skillFolder);
|
|
313
|
+
if (!fs.existsSync(targetDir)) {
|
|
314
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
315
|
+
}
|
|
316
|
+
for (const skillDir of skillDirs) {
|
|
317
|
+
if (skillDir.isDirectory()) {
|
|
318
|
+
const sourcePath = path.join(sourceSkillsDir, skillDir.name);
|
|
319
|
+
const destPath = path.join(targetDir, skillDir.name);
|
|
320
|
+
fs.cpSync(sourcePath, destPath, { recursive: true });
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
325
|
+
return true;
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error(
|
|
328
|
+
import_picocolors.default.red("Error installing skills:"),
|
|
329
|
+
error instanceof Error ? error.message : error
|
|
330
|
+
);
|
|
331
|
+
if (fs.existsSync(tempDir)) {
|
|
332
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
333
|
+
}
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async function downloadAgentsMd(projectDir) {
|
|
338
|
+
const tempDir = `${projectDir}-agents-md-temp`;
|
|
339
|
+
try {
|
|
340
|
+
const agentsMdPath = "packages/examples/agent-quickstart/AGENTS.md";
|
|
341
|
+
const latestSdkTag = getLatestSdkTag();
|
|
342
|
+
if (!latestSdkTag) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
const cloneResult = import_cross_spawn.default.sync(
|
|
346
|
+
"git",
|
|
347
|
+
[
|
|
348
|
+
"clone",
|
|
349
|
+
"--no-checkout",
|
|
350
|
+
"--depth",
|
|
351
|
+
"1",
|
|
352
|
+
"--sparse",
|
|
353
|
+
"--branch",
|
|
354
|
+
latestSdkTag,
|
|
355
|
+
"https://github.com/towns-protocol/towns.git",
|
|
356
|
+
tempDir
|
|
357
|
+
],
|
|
358
|
+
{ stdio: "pipe" }
|
|
359
|
+
);
|
|
360
|
+
if (cloneResult.status !== 0) {
|
|
361
|
+
if (fs.existsSync(tempDir)) {
|
|
362
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
const sparseResult = import_cross_spawn.default.sync("git", ["sparse-checkout", "set", agentsMdPath], {
|
|
367
|
+
stdio: "pipe",
|
|
368
|
+
cwd: tempDir
|
|
369
|
+
});
|
|
370
|
+
if (sparseResult.status !== 0) {
|
|
371
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
const checkoutResult = import_cross_spawn.default.sync("git", ["checkout"], {
|
|
375
|
+
stdio: "pipe",
|
|
376
|
+
cwd: tempDir
|
|
377
|
+
});
|
|
378
|
+
if (checkoutResult.status !== 0) {
|
|
379
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
const sourceFile = path.join(tempDir, agentsMdPath);
|
|
383
|
+
if (!fs.existsSync(sourceFile)) {
|
|
384
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
const destFile = path.join(projectDir, "AGENTS.md");
|
|
388
|
+
fs.copyFileSync(sourceFile, destFile);
|
|
389
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
390
|
+
return true;
|
|
391
|
+
} catch (error) {
|
|
392
|
+
console.error(
|
|
393
|
+
import_picocolors.default.red("Error downloading AGENTS.md:"),
|
|
394
|
+
error instanceof Error ? error.message : error
|
|
395
|
+
);
|
|
396
|
+
if (fs.existsSync(tempDir)) {
|
|
397
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
398
|
+
}
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
async function promptAuth() {
|
|
403
|
+
const { method } = await (0, import_prompts.default)({
|
|
404
|
+
type: "select",
|
|
405
|
+
name: "method",
|
|
406
|
+
message: "Login method",
|
|
407
|
+
choices: [
|
|
408
|
+
{ title: "Bearer Token", value: "bearerToken" },
|
|
409
|
+
{ title: "Private Key", value: "privateKey" }
|
|
410
|
+
]
|
|
411
|
+
});
|
|
412
|
+
if (method === void 0) return void 0;
|
|
413
|
+
const promptMessage = method === "bearerToken" ? "Enter your bearer token" : "Enter your private key";
|
|
414
|
+
const { value } = await (0, import_prompts.default)({
|
|
415
|
+
type: "password",
|
|
416
|
+
name: "value",
|
|
417
|
+
message: promptMessage,
|
|
418
|
+
validate: (v) => v.trim() ? true : "This field is required"
|
|
419
|
+
});
|
|
420
|
+
if (!value) return void 0;
|
|
421
|
+
return { method, value };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// src/modules/init.ts
|
|
425
|
+
var TEMPLATES = {
|
|
426
|
+
quickstart: {
|
|
427
|
+
name: "Agent Quickstart",
|
|
428
|
+
description: "Simple starter agent with basic commands",
|
|
429
|
+
packagePath: "agent-quickstart"
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
async function init(argv) {
|
|
433
|
+
const projectName = argv._[1];
|
|
434
|
+
const template = argv.template || "quickstart";
|
|
435
|
+
if (!projectName) {
|
|
436
|
+
console.error((0, import_picocolors2.red)("Error: Please provide a project name"));
|
|
437
|
+
console.log((0, import_picocolors2.yellow)("Usage: towns-agent init <project-name>"));
|
|
438
|
+
process.exit(1);
|
|
439
|
+
}
|
|
440
|
+
if (!TEMPLATES[template]) {
|
|
441
|
+
console.error((0, import_picocolors2.red)(`Error: Unknown template "${template}"`));
|
|
442
|
+
console.log((0, import_picocolors2.yellow)("Available templates:"), Object.keys(TEMPLATES).join(", "));
|
|
443
|
+
process.exit(1);
|
|
444
|
+
}
|
|
445
|
+
const targetDir = path2.resolve(process.cwd(), projectName);
|
|
446
|
+
if (fs2.existsSync(targetDir)) {
|
|
447
|
+
const { overwrite } = await (0, import_prompts2.default)({
|
|
448
|
+
type: "confirm",
|
|
449
|
+
name: "overwrite",
|
|
450
|
+
message: `Directory ${projectName} already exists. Overwrite?`,
|
|
451
|
+
initial: false
|
|
452
|
+
});
|
|
453
|
+
if (!overwrite) {
|
|
454
|
+
console.log((0, import_picocolors2.yellow)("Operation cancelled"));
|
|
455
|
+
process.exit(0);
|
|
456
|
+
}
|
|
457
|
+
fs2.rmSync(targetDir, { recursive: true, force: true });
|
|
458
|
+
}
|
|
459
|
+
console.log((0, import_picocolors2.cyan)(`Creating a new Towns Protocol agent in ${targetDir}`));
|
|
460
|
+
if (template !== "quickstart") {
|
|
461
|
+
console.log((0, import_picocolors2.cyan)(`Using template: ${TEMPLATES[template].name}`));
|
|
462
|
+
}
|
|
463
|
+
const packageManager = getPackageManager();
|
|
464
|
+
const selectedTemplate = TEMPLATES[template];
|
|
465
|
+
try {
|
|
466
|
+
const success = await cloneTemplate(selectedTemplate.packagePath, targetDir);
|
|
467
|
+
if (!success) {
|
|
468
|
+
console.error((0, import_picocolors2.red)("Failed to clone template"));
|
|
469
|
+
process.exit(1);
|
|
470
|
+
}
|
|
471
|
+
const latestVersion = await getLatestTownsProtocolVersion();
|
|
472
|
+
const replacements = /* @__PURE__ */ new Map([
|
|
473
|
+
["workspace:\\^", `^${latestVersion}`],
|
|
474
|
+
["workspace:\\*", `^${latestVersion}`]
|
|
475
|
+
]);
|
|
476
|
+
applyReplacements(targetDir, replacements);
|
|
477
|
+
const packageJsonPath = path2.join(targetDir, "package.json");
|
|
478
|
+
if (fs2.existsSync(packageJsonPath)) {
|
|
479
|
+
const content = fs2.readFileSync(packageJsonPath, "utf-8");
|
|
480
|
+
const edits = [
|
|
481
|
+
jsonc.modify(content, ["name"], projectName, {}),
|
|
482
|
+
jsonc.modify(content, ["version"], "0.0.1", {})
|
|
483
|
+
];
|
|
484
|
+
let modifiedContent = jsonc.applyEdits(content, edits.flat());
|
|
485
|
+
const parsed = jsonc.parse(modifiedContent);
|
|
486
|
+
delete parsed.private;
|
|
487
|
+
modifiedContent = JSON.stringify(parsed, null, 2);
|
|
488
|
+
fs2.writeFileSync(packageJsonPath, modifiedContent);
|
|
489
|
+
}
|
|
490
|
+
console.log((0, import_picocolors2.cyan)("Installing Towns Agent Skills..."));
|
|
491
|
+
try {
|
|
492
|
+
const skillSuccess = await installTownsSkills(targetDir);
|
|
493
|
+
if (skillSuccess) {
|
|
494
|
+
console.log((0, import_picocolors2.green)("\u2713"), "Towns Agent Skills installed successfully!");
|
|
495
|
+
} else {
|
|
496
|
+
console.log(
|
|
497
|
+
(0, import_picocolors2.yellow)("\u26A0"),
|
|
498
|
+
"Failed to install Towns Agent Skills. You can install them later with:"
|
|
499
|
+
);
|
|
500
|
+
console.log((0, import_picocolors2.yellow)(` cd ${projectName} && towns-agent install-skill`));
|
|
501
|
+
}
|
|
502
|
+
} catch (error) {
|
|
503
|
+
console.log(
|
|
504
|
+
(0, import_picocolors2.yellow)("\u26A0"),
|
|
505
|
+
"Error installing skills:",
|
|
506
|
+
error instanceof Error ? error.message : error
|
|
507
|
+
);
|
|
508
|
+
console.log((0, import_picocolors2.yellow)(` You can install them later with: towns-agent install-skill`));
|
|
509
|
+
}
|
|
510
|
+
await initializeGitRepository(targetDir);
|
|
511
|
+
printSuccess(projectName, packageManager);
|
|
512
|
+
} catch (error) {
|
|
513
|
+
console.error((0, import_picocolors2.red)("Error:"), error instanceof Error ? error.message : error);
|
|
514
|
+
console.error((0, import_picocolors2.red)(`Please delete the directory ${targetDir} and try again.`));
|
|
515
|
+
process.exit(1);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// src/modules/update.ts
|
|
520
|
+
var import_fs = __toESM(require("fs"));
|
|
521
|
+
var import_path = __toESM(require("path"));
|
|
522
|
+
var import_picocolors3 = require("picocolors");
|
|
523
|
+
function getTownsVersions(packageJson) {
|
|
524
|
+
const versions = {};
|
|
525
|
+
for (const deps of [packageJson.dependencies, packageJson.devDependencies]) {
|
|
526
|
+
if (deps) {
|
|
527
|
+
for (const [pkg, version] of Object.entries(deps)) {
|
|
528
|
+
if (pkg.startsWith("@towns-labs/") || pkg.startsWith("@towns-protocol/")) {
|
|
529
|
+
versions[pkg] = version;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return versions;
|
|
535
|
+
}
|
|
536
|
+
async function update(_argv) {
|
|
537
|
+
const packageJsonPath = import_path.default.join(process.cwd(), "package.json");
|
|
538
|
+
if (!import_fs.default.existsSync(packageJsonPath)) {
|
|
539
|
+
console.error((0, import_picocolors3.red)("Error: No package.json found in the current directory"));
|
|
540
|
+
console.log((0, import_picocolors3.yellow)("Please run this command from a Towns Protocol bot project directory"));
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
const packageManager = getPackageManager();
|
|
544
|
+
const dlxCommand = getDlxCommand(packageManager);
|
|
545
|
+
console.log((0, import_picocolors3.cyan)("Checking for @towns-protocol updates..."));
|
|
546
|
+
try {
|
|
547
|
+
const [dlxBin, ...dlxArgs] = dlxCommand.split(" ");
|
|
548
|
+
const packageJsonBefore = JSON.parse(import_fs.default.readFileSync(packageJsonPath, "utf-8"));
|
|
549
|
+
const versionsBefore = getTownsVersions(packageJsonBefore);
|
|
550
|
+
await runCommand(dlxBin, [...dlxArgs, "npm-check-updates", "-u", "-f", "@towns-labs/*"], {
|
|
551
|
+
silent: true
|
|
552
|
+
});
|
|
553
|
+
await runCommand(
|
|
554
|
+
dlxBin,
|
|
555
|
+
[...dlxArgs, "npm-check-updates", "-u", "-f", "@towns-protocol/*"],
|
|
556
|
+
{
|
|
557
|
+
silent: true
|
|
558
|
+
}
|
|
559
|
+
);
|
|
560
|
+
const packageJsonAfter = JSON.parse(import_fs.default.readFileSync(packageJsonPath, "utf-8"));
|
|
561
|
+
const versionsAfter = getTownsVersions(packageJsonAfter);
|
|
562
|
+
const updates = [];
|
|
563
|
+
for (const [pkg, newVersion] of Object.entries(versionsAfter)) {
|
|
564
|
+
const oldVersion = versionsBefore[pkg];
|
|
565
|
+
if (oldVersion && oldVersion !== newVersion) {
|
|
566
|
+
updates.push({ package: pkg, from: oldVersion, to: newVersion });
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (updates.length === 0) {
|
|
570
|
+
console.log((0, import_picocolors3.green)("\u2713"), "All @towns-protocol and @towns-labs packages are up to date!");
|
|
571
|
+
} else {
|
|
572
|
+
console.log();
|
|
573
|
+
for (const update2 of updates) {
|
|
574
|
+
console.log((0, import_picocolors3.green)("\u2713"), `${update2.package} ${update2.from} \u2192 ${update2.to}`);
|
|
575
|
+
}
|
|
576
|
+
console.log();
|
|
577
|
+
console.log((0, import_picocolors3.cyan)(`Installing dependencies with ${packageManager}...`));
|
|
578
|
+
const installCmd = getInstallCommand(packageManager);
|
|
579
|
+
const [installBin, ...installArgs] = installCmd.split(" ");
|
|
580
|
+
await runCommand(installBin, installArgs.length > 0 ? installArgs : []);
|
|
581
|
+
console.log();
|
|
582
|
+
console.log((0, import_picocolors3.green)("\u2713"), "Dependencies updated successfully!");
|
|
583
|
+
}
|
|
584
|
+
const projectDir = process.cwd();
|
|
585
|
+
const claudeSkillsDir = import_path.default.join(projectDir, ".claude", "skills");
|
|
586
|
+
const codexSkillsDir = import_path.default.join(projectDir, ".codex", "skills");
|
|
587
|
+
if (import_fs.default.existsSync(claudeSkillsDir) || import_fs.default.existsSync(codexSkillsDir)) {
|
|
588
|
+
console.log();
|
|
589
|
+
console.log((0, import_picocolors3.cyan)("Updating Towns Agent Skills..."));
|
|
590
|
+
try {
|
|
591
|
+
const skillSuccess = await installTownsSkills(projectDir);
|
|
592
|
+
if (skillSuccess) {
|
|
593
|
+
console.log((0, import_picocolors3.green)("\u2713"), "Towns Agent Skills updated successfully!");
|
|
594
|
+
} else {
|
|
595
|
+
console.log(
|
|
596
|
+
(0, import_picocolors3.yellow)("\u26A0"),
|
|
597
|
+
"Failed to update skills. You can reinstall them with: towns-agent install-skill"
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
} catch (error) {
|
|
601
|
+
console.log(
|
|
602
|
+
(0, import_picocolors3.yellow)("\u26A0"),
|
|
603
|
+
"Error updating skills:",
|
|
604
|
+
error instanceof Error ? error.message : error
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
if (!_argv.skipAgentsMd) {
|
|
609
|
+
console.log();
|
|
610
|
+
console.log((0, import_picocolors3.cyan)("Updating AGENTS.md..."));
|
|
611
|
+
try {
|
|
612
|
+
const agentsMdSuccess = await downloadAgentsMd(projectDir);
|
|
613
|
+
if (agentsMdSuccess) {
|
|
614
|
+
console.log((0, import_picocolors3.green)("\u2713"), "AGENTS.md updated successfully!");
|
|
615
|
+
} else {
|
|
616
|
+
console.log(
|
|
617
|
+
(0, import_picocolors3.yellow)("\u26A0"),
|
|
618
|
+
"Failed to update AGENTS.md. You can update it manually or run update again."
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
} catch (error) {
|
|
622
|
+
console.log(
|
|
623
|
+
(0, import_picocolors3.yellow)("\u26A0"),
|
|
624
|
+
"Error updating AGENTS.md:",
|
|
625
|
+
error instanceof Error ? error.message : error
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
} catch {
|
|
630
|
+
console.error((0, import_picocolors3.red)("Error:"), "Failed to update dependencies");
|
|
631
|
+
process.exit(1);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// src/modules/install-skill.ts
|
|
636
|
+
var import_picocolors4 = require("picocolors");
|
|
637
|
+
async function skill(_argv) {
|
|
638
|
+
const cwd = process.cwd();
|
|
639
|
+
await installSkill(cwd);
|
|
640
|
+
}
|
|
641
|
+
async function installSkill(projectDir) {
|
|
642
|
+
console.log((0, import_picocolors4.cyan)("Installing Towns Agent Skills..."));
|
|
643
|
+
try {
|
|
644
|
+
const success = await installTownsSkills(projectDir);
|
|
645
|
+
if (success) {
|
|
646
|
+
console.log((0, import_picocolors4.green)("\u2713"), "Towns Agent Skills installed successfully!");
|
|
647
|
+
console.log();
|
|
648
|
+
console.log("Skills have been installed to .claude/skills/ and .codex/skills/");
|
|
649
|
+
console.log("They will be available when you open this project in your AI assistant");
|
|
650
|
+
} else {
|
|
651
|
+
console.error((0, import_picocolors4.red)("Failed to install Towns Agent Skills"));
|
|
652
|
+
process.exit(1);
|
|
653
|
+
}
|
|
654
|
+
} catch (error) {
|
|
655
|
+
console.error((0, import_picocolors4.red)("Error:"), error instanceof Error ? error.message : error);
|
|
656
|
+
process.exit(1);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// src/modules/create.ts
|
|
661
|
+
var import_prompts3 = __toESM(require("prompts"));
|
|
662
|
+
var import_picocolors5 = require("picocolors");
|
|
663
|
+
var import_viem = require("viem");
|
|
664
|
+
var import_accounts = require("viem/accounts");
|
|
665
|
+
var import_relayer_client = require("@towns-labs/relayer-client");
|
|
666
|
+
var import_sdk = require("@towns-labs/sdk");
|
|
667
|
+
var import_deployments = require("@towns-labs/contracts/deployments");
|
|
668
|
+
async function create(argv) {
|
|
669
|
+
let ownerPrivateKey = argv.ownerPrivateKey;
|
|
670
|
+
let bearerToken = argv.bearerToken;
|
|
671
|
+
if (!ownerPrivateKey && !bearerToken) {
|
|
672
|
+
const auth = await promptAuth();
|
|
673
|
+
if (!auth) {
|
|
674
|
+
console.error((0, import_picocolors5.red)("Authentication is required."));
|
|
675
|
+
process.exit(1);
|
|
676
|
+
}
|
|
677
|
+
if (auth.method === "privateKey") {
|
|
678
|
+
ownerPrivateKey = auth.value;
|
|
679
|
+
} else {
|
|
680
|
+
bearerToken = auth.value;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
const username = argv.username ?? await promptText("Bot username");
|
|
684
|
+
if (!username) {
|
|
685
|
+
console.error((0, import_picocolors5.red)("Username is required."));
|
|
686
|
+
process.exit(1);
|
|
687
|
+
}
|
|
688
|
+
const displayName = argv.displayName ?? await promptText("Bot display name");
|
|
689
|
+
if (!displayName) {
|
|
690
|
+
console.error((0, import_picocolors5.red)("Display name is required."));
|
|
691
|
+
process.exit(1);
|
|
692
|
+
}
|
|
693
|
+
const description = argv.description ?? await promptText("Bot description");
|
|
694
|
+
if (!description) {
|
|
695
|
+
console.error((0, import_picocolors5.red)("Description is required."));
|
|
696
|
+
process.exit(1);
|
|
697
|
+
}
|
|
698
|
+
const imageUrl = argv.imageUrl ?? await promptText("Bot image URL (optional)", true);
|
|
699
|
+
const owner = ownerPrivateKey ? await (0, import_sdk.makeSignerContextFromViem)(
|
|
700
|
+
(0, import_accounts.privateKeyToAccount)(ownerPrivateKey),
|
|
701
|
+
(0, import_accounts.generatePrivateKey)(),
|
|
702
|
+
{ days: 1 }
|
|
703
|
+
) : await (0, import_sdk.makeSignerContextFromBearerToken)(bearerToken);
|
|
704
|
+
const townsConfig = (0, import_sdk.townsEnv)().makeTownsConfig();
|
|
705
|
+
const chainId = townsConfig.base.chainConfig.chainId;
|
|
706
|
+
const addresses = (0, import_deployments.getAddressesWithFallback)(townsConfig.environmentId, chainId);
|
|
707
|
+
if (!addresses?.accountProxy) {
|
|
708
|
+
throw new Error(`No accountProxy address found for ${townsConfig.environmentId}/${chainId}`);
|
|
709
|
+
}
|
|
710
|
+
const relayerUrl = process.env.RELAYER_URL ?? "http://127.0.0.1:8787";
|
|
711
|
+
const relayerClient = (0, import_viem.createPublicClient)({
|
|
712
|
+
chain: {
|
|
713
|
+
id: chainId,
|
|
714
|
+
name: "Local",
|
|
715
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
716
|
+
rpcUrls: { default: { http: [townsConfig.base.rpcUrl] } }
|
|
717
|
+
},
|
|
718
|
+
transport: (0, import_viem.http)(townsConfig.base.rpcUrl)
|
|
719
|
+
}).extend((0, import_relayer_client.relayerActions)({ relayerUrl }));
|
|
720
|
+
const result = await (0, import_sdk.createApp)({
|
|
721
|
+
owner,
|
|
722
|
+
metadata: {
|
|
723
|
+
username,
|
|
724
|
+
displayName,
|
|
725
|
+
description,
|
|
726
|
+
imageUrl: imageUrl || ""
|
|
727
|
+
},
|
|
728
|
+
relayerClient,
|
|
729
|
+
accountProxy: addresses.accountProxy,
|
|
730
|
+
townsConfig
|
|
731
|
+
});
|
|
732
|
+
console.log(`APP_ADDRESS=${result.appAddress}`);
|
|
733
|
+
console.log(`APP_PRIVATE_KEY=${result.appPrivateKey}`);
|
|
734
|
+
console.log(`APP_PRIVATE_DATA=${result.appPrivateData}`);
|
|
735
|
+
console.log(`JWT_SECRET=${result.jwtSecretBase64}`);
|
|
736
|
+
process.exit(0);
|
|
737
|
+
}
|
|
738
|
+
async function promptText(message, optional = false) {
|
|
739
|
+
const { value } = await (0, import_prompts3.default)({
|
|
740
|
+
type: "text",
|
|
741
|
+
name: "value",
|
|
742
|
+
message,
|
|
743
|
+
validate: optional ? void 0 : (v) => v.trim() ? true : "This field is required"
|
|
744
|
+
});
|
|
745
|
+
return value;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// src/modules/metadata.ts
|
|
749
|
+
var import_prompts4 = __toESM(require("prompts"));
|
|
750
|
+
var import_picocolors6 = require("picocolors");
|
|
751
|
+
var import_accounts2 = require("viem/accounts");
|
|
752
|
+
var import_sdk2 = require("@towns-labs/sdk");
|
|
753
|
+
var import_utils5 = require("@towns-labs/utils");
|
|
754
|
+
var FIELD_DEFS = [
|
|
755
|
+
{ flag: "username", label: "USERNAME", mask: "username", prompt: "Username" },
|
|
756
|
+
{ flag: "displayName", label: "DISPLAY_NAME", mask: "display_name", prompt: "Display name" },
|
|
757
|
+
{ flag: "description", label: "DESCRIPTION", mask: "description", prompt: "Description" },
|
|
758
|
+
{ flag: "imageUrl", label: "IMAGE_URL", mask: "image_url", prompt: "Image URL" },
|
|
759
|
+
{ flag: "avatarUrl", label: "AVATAR_URL", mask: "avatar_url", prompt: "Avatar URL" },
|
|
760
|
+
{
|
|
761
|
+
flag: "externalUrl",
|
|
762
|
+
label: "EXTERNAL_URL",
|
|
763
|
+
mask: "external_url",
|
|
764
|
+
prompt: "External URL"
|
|
765
|
+
},
|
|
766
|
+
{ flag: "motto", label: "MOTTO", mask: "motto", prompt: "Motto" }
|
|
767
|
+
];
|
|
768
|
+
async function metadata(argv) {
|
|
769
|
+
const subcommand = argv._[1];
|
|
770
|
+
const appAddress = argv._[2];
|
|
771
|
+
if (!subcommand || !["view", "update"].includes(subcommand)) {
|
|
772
|
+
console.error((0, import_picocolors6.red)("Usage: towns-agent metadata <view|update> <appAddress> [options]"));
|
|
773
|
+
process.exit(1);
|
|
774
|
+
}
|
|
775
|
+
if (!appAddress) {
|
|
776
|
+
console.error((0, import_picocolors6.red)("App address is required."));
|
|
777
|
+
process.exit(1);
|
|
778
|
+
}
|
|
779
|
+
const env = (0, import_sdk2.townsEnv)();
|
|
780
|
+
const townsConfig = env.makeTownsConfig();
|
|
781
|
+
const appRegistryUrl = env.getAppRegistryUrl(townsConfig.environmentId);
|
|
782
|
+
const appId = (0, import_utils5.bin_fromHexString)(appAddress);
|
|
783
|
+
if (subcommand === "view") {
|
|
784
|
+
const client = (0, import_sdk2.makeAppRegistryRpcClient)(appRegistryUrl, "");
|
|
785
|
+
const { metadata: meta } = await client.getAppMetadata({ appId });
|
|
786
|
+
if (!meta) {
|
|
787
|
+
console.error((0, import_picocolors6.red)("No metadata found for this app."));
|
|
788
|
+
process.exit(1);
|
|
789
|
+
}
|
|
790
|
+
console.log();
|
|
791
|
+
for (const field of FIELD_DEFS) {
|
|
792
|
+
const value = meta[field.flag] ?? "";
|
|
793
|
+
console.log(` ${(0, import_picocolors6.dim)(field.prompt.padEnd(14))} ${value || (0, import_picocolors6.dim)("\u2014")}`);
|
|
794
|
+
}
|
|
795
|
+
console.log();
|
|
796
|
+
process.exit(0);
|
|
797
|
+
}
|
|
798
|
+
let ownerPrivateKey = argv.ownerPrivateKey;
|
|
799
|
+
let bearerToken = ownerPrivateKey ? void 0 : argv.bearerToken;
|
|
800
|
+
if (!ownerPrivateKey && !bearerToken) {
|
|
801
|
+
const auth = await promptAuth();
|
|
802
|
+
if (!auth) {
|
|
803
|
+
console.error((0, import_picocolors6.red)("Authentication is required for update."));
|
|
804
|
+
process.exit(1);
|
|
805
|
+
}
|
|
806
|
+
if (auth.method === "privateKey") {
|
|
807
|
+
ownerPrivateKey = auth.value;
|
|
808
|
+
} else {
|
|
809
|
+
bearerToken = auth.value;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
const signerContext = ownerPrivateKey ? await (0, import_sdk2.makeSignerContextFromViem)(
|
|
813
|
+
(0, import_accounts2.privateKeyToAccount)(ownerPrivateKey),
|
|
814
|
+
(0, import_accounts2.generatePrivateKey)(),
|
|
815
|
+
{ days: 1 }
|
|
816
|
+
) : await (0, import_sdk2.makeSignerContextFromBearerToken)(bearerToken);
|
|
817
|
+
const { appRegistryRpcClient } = await import_sdk2.AppRegistryService.authenticate(
|
|
818
|
+
signerContext,
|
|
819
|
+
appRegistryUrl
|
|
820
|
+
);
|
|
821
|
+
const hasAnyFlag = FIELD_DEFS.some((f) => argv[f.flag] !== void 0);
|
|
822
|
+
let values;
|
|
823
|
+
if (hasAnyFlag) {
|
|
824
|
+
values = {};
|
|
825
|
+
for (const field of FIELD_DEFS) {
|
|
826
|
+
if (argv[field.flag] !== void 0) {
|
|
827
|
+
values[field.flag] = argv[field.flag];
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
} else {
|
|
831
|
+
const unauthClient = (0, import_sdk2.makeAppRegistryRpcClient)(appRegistryUrl, "");
|
|
832
|
+
const { metadata: current } = await unauthClient.getAppMetadata({ appId });
|
|
833
|
+
values = {};
|
|
834
|
+
for (const field of FIELD_DEFS) {
|
|
835
|
+
const { value } = await (0, import_prompts4.default)({
|
|
836
|
+
type: "text",
|
|
837
|
+
name: "value",
|
|
838
|
+
message: field.prompt,
|
|
839
|
+
initial: current?.[field.flag] ?? ""
|
|
840
|
+
});
|
|
841
|
+
if (value === void 0) {
|
|
842
|
+
process.exit(1);
|
|
843
|
+
}
|
|
844
|
+
if (value !== (current?.[field.flag] ?? "")) {
|
|
845
|
+
values[field.flag] = value;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (Object.keys(values).length === 0) {
|
|
849
|
+
console.log("No changes.");
|
|
850
|
+
process.exit(0);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
const updateMask = [];
|
|
854
|
+
const metadataUpdate = {};
|
|
855
|
+
for (const field of FIELD_DEFS) {
|
|
856
|
+
if (values[field.flag] !== void 0) {
|
|
857
|
+
updateMask.push(field.mask);
|
|
858
|
+
metadataUpdate[field.flag] = values[field.flag];
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
await appRegistryRpcClient.updateAppMetadata({
|
|
862
|
+
appId,
|
|
863
|
+
updateMask,
|
|
864
|
+
metadata: metadataUpdate
|
|
865
|
+
});
|
|
866
|
+
for (const field of FIELD_DEFS) {
|
|
867
|
+
if (values[field.flag] !== void 0) {
|
|
868
|
+
console.log(` ${(0, import_picocolors6.dim)(field.prompt.padEnd(14))} ${values[field.flag]}`);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
console.log();
|
|
872
|
+
process.exit(0);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// src/modules/setup.ts
|
|
876
|
+
var import_prompts5 = __toESM(require("prompts"));
|
|
877
|
+
var import_picocolors7 = require("picocolors");
|
|
878
|
+
var import_accounts3 = require("viem/accounts");
|
|
879
|
+
var import_sdk3 = require("@towns-labs/sdk");
|
|
880
|
+
var import_proto = require("@towns-labs/proto");
|
|
881
|
+
var import_utils7 = require("@towns-labs/utils");
|
|
882
|
+
var NOTIFY_MAP = {
|
|
883
|
+
ALL: import_proto.ForwardSettingValue.FORWARD_SETTING_ALL_MESSAGES,
|
|
884
|
+
NONE: import_proto.ForwardSettingValue.FORWARD_SETTING_NO_MESSAGES,
|
|
885
|
+
MENTION_REPLY_REACTION: import_proto.ForwardSettingValue.FORWARD_SETTING_MENTIONS_REPLIES_REACTIONS
|
|
886
|
+
};
|
|
887
|
+
var NOTIFY_LABELS = {
|
|
888
|
+
ALL: "All messages",
|
|
889
|
+
MENTION_REPLY_REACTION: "Mentions, replies & reactions",
|
|
890
|
+
NONE: "No messages"
|
|
891
|
+
};
|
|
892
|
+
async function setup(argv) {
|
|
893
|
+
const appAddress = argv._[1];
|
|
894
|
+
if (!appAddress) {
|
|
895
|
+
console.error((0, import_picocolors7.red)("Usage: towns-agent setup <appAddress> [options]"));
|
|
896
|
+
process.exit(1);
|
|
897
|
+
}
|
|
898
|
+
let ownerPrivateKey = argv.ownerPrivateKey;
|
|
899
|
+
let bearerToken = argv.bearerToken;
|
|
900
|
+
if (!ownerPrivateKey && !bearerToken) {
|
|
901
|
+
const auth = await promptAuth();
|
|
902
|
+
if (!auth) {
|
|
903
|
+
console.error((0, import_picocolors7.red)("Authentication is required."));
|
|
904
|
+
process.exit(1);
|
|
905
|
+
}
|
|
906
|
+
if (auth.method === "privateKey") {
|
|
907
|
+
ownerPrivateKey = auth.value;
|
|
908
|
+
} else {
|
|
909
|
+
bearerToken = auth.value;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
const webhookUrl = argv.webhookUrl ?? await promptWebhookUrl();
|
|
913
|
+
if (!webhookUrl) {
|
|
914
|
+
console.error((0, import_picocolors7.red)("Webhook URL is required."));
|
|
915
|
+
process.exit(1);
|
|
916
|
+
}
|
|
917
|
+
let notifyKey;
|
|
918
|
+
if (argv.notify) {
|
|
919
|
+
notifyKey = argv.notify.toUpperCase();
|
|
920
|
+
} else if (argv.webhookUrl) {
|
|
921
|
+
notifyKey = "ALL";
|
|
922
|
+
} else {
|
|
923
|
+
notifyKey = await promptNotify();
|
|
924
|
+
}
|
|
925
|
+
const forwardSetting = NOTIFY_MAP[notifyKey];
|
|
926
|
+
if (forwardSetting === void 0) {
|
|
927
|
+
console.error(
|
|
928
|
+
(0, import_picocolors7.red)(`Invalid --notify value: ${notifyKey}. Use ALL, NONE, or MENTION_REPLY_REACTION.`)
|
|
929
|
+
);
|
|
930
|
+
process.exit(1);
|
|
931
|
+
}
|
|
932
|
+
const env = (0, import_sdk3.townsEnv)();
|
|
933
|
+
const townsConfig = env.makeTownsConfig();
|
|
934
|
+
const appRegistryUrl = env.getAppRegistryUrl(townsConfig.environmentId);
|
|
935
|
+
const appId = (0, import_utils7.bin_fromHexString)(appAddress);
|
|
936
|
+
const signerContext = ownerPrivateKey ? await (0, import_sdk3.makeSignerContextFromViem)(
|
|
937
|
+
(0, import_accounts3.privateKeyToAccount)(ownerPrivateKey),
|
|
938
|
+
(0, import_accounts3.generatePrivateKey)(),
|
|
939
|
+
{ days: 1 }
|
|
940
|
+
) : await (0, import_sdk3.makeSignerContextFromBearerToken)(bearerToken);
|
|
941
|
+
const { appRegistryRpcClient } = await import_sdk3.AppRegistryService.authenticate(
|
|
942
|
+
signerContext,
|
|
943
|
+
appRegistryUrl
|
|
944
|
+
);
|
|
945
|
+
await appRegistryRpcClient.registerWebhook({ appId, webhookUrl });
|
|
946
|
+
await appRegistryRpcClient.setAppSettings({
|
|
947
|
+
appId,
|
|
948
|
+
settings: { forwardSetting }
|
|
949
|
+
});
|
|
950
|
+
console.log();
|
|
951
|
+
console.log((0, import_picocolors7.green)("Setup complete!"));
|
|
952
|
+
console.log();
|
|
953
|
+
console.log(` ${(0, import_picocolors7.dim)("Webhook URL".padEnd(14))} ${webhookUrl}`);
|
|
954
|
+
console.log(` ${(0, import_picocolors7.dim)("Notify".padEnd(14))} ${NOTIFY_LABELS[notifyKey] ?? notifyKey}`);
|
|
955
|
+
console.log();
|
|
956
|
+
process.exit(0);
|
|
957
|
+
}
|
|
958
|
+
async function promptWebhookUrl() {
|
|
959
|
+
const { value } = await (0, import_prompts5.default)({
|
|
960
|
+
type: "text",
|
|
961
|
+
name: "value",
|
|
962
|
+
message: "Webhook URL",
|
|
963
|
+
validate: (v) => v.trim() ? true : "Webhook URL is required"
|
|
964
|
+
});
|
|
965
|
+
return value;
|
|
966
|
+
}
|
|
967
|
+
async function promptNotify() {
|
|
968
|
+
const { value } = await (0, import_prompts5.default)({
|
|
969
|
+
type: "select",
|
|
970
|
+
name: "value",
|
|
971
|
+
message: "Notify setting",
|
|
972
|
+
choices: [
|
|
973
|
+
{ title: "All messages", value: "ALL" },
|
|
974
|
+
{ title: "Mentions, replies & reactions", value: "MENTION_REPLY_REACTION" },
|
|
975
|
+
{ title: "No messages", value: "NONE" }
|
|
976
|
+
],
|
|
977
|
+
initial: 0
|
|
978
|
+
});
|
|
979
|
+
if (value === void 0) {
|
|
980
|
+
process.exit(1);
|
|
981
|
+
}
|
|
982
|
+
return value;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// src/parser.ts
|
|
986
|
+
var import_minimist = __toESM(require("minimist"));
|
|
987
|
+
var COMMAND_CONFIGS = {
|
|
988
|
+
init: {
|
|
989
|
+
string: ["template"],
|
|
990
|
+
alias: { t: "template" },
|
|
991
|
+
default: { template: "quickstart" }
|
|
992
|
+
},
|
|
993
|
+
update: {
|
|
994
|
+
boolean: ["skipAgentsMd"],
|
|
995
|
+
alias: { "skip-agents-md": "skipAgentsMd" }
|
|
996
|
+
},
|
|
997
|
+
"install-skill": {},
|
|
998
|
+
create: {
|
|
999
|
+
string: [
|
|
1000
|
+
"bearerToken",
|
|
1001
|
+
"ownerPrivateKey",
|
|
1002
|
+
"username",
|
|
1003
|
+
"displayName",
|
|
1004
|
+
"description",
|
|
1005
|
+
"imageUrl"
|
|
1006
|
+
],
|
|
1007
|
+
alias: {
|
|
1008
|
+
b: "bearerToken",
|
|
1009
|
+
k: "ownerPrivateKey",
|
|
1010
|
+
u: "username",
|
|
1011
|
+
n: "displayName",
|
|
1012
|
+
d: "description",
|
|
1013
|
+
i: "imageUrl"
|
|
1014
|
+
}
|
|
1015
|
+
},
|
|
1016
|
+
metadata: {
|
|
1017
|
+
string: [
|
|
1018
|
+
"_",
|
|
1019
|
+
"ownerPrivateKey",
|
|
1020
|
+
"bearerToken",
|
|
1021
|
+
"username",
|
|
1022
|
+
"displayName",
|
|
1023
|
+
"description",
|
|
1024
|
+
"imageUrl",
|
|
1025
|
+
"avatarUrl",
|
|
1026
|
+
"externalUrl",
|
|
1027
|
+
"motto"
|
|
1028
|
+
],
|
|
1029
|
+
alias: {
|
|
1030
|
+
k: "ownerPrivateKey",
|
|
1031
|
+
b: "bearerToken",
|
|
1032
|
+
u: "username",
|
|
1033
|
+
n: "displayName",
|
|
1034
|
+
d: "description",
|
|
1035
|
+
i: "imageUrl",
|
|
1036
|
+
a: "avatarUrl",
|
|
1037
|
+
e: "externalUrl",
|
|
1038
|
+
m: "motto"
|
|
1039
|
+
}
|
|
1040
|
+
},
|
|
1041
|
+
setup: {
|
|
1042
|
+
string: ["_", "ownerPrivateKey", "bearerToken", "webhookUrl", "notify"],
|
|
1043
|
+
alias: {
|
|
1044
|
+
k: "ownerPrivateKey",
|
|
1045
|
+
b: "bearerToken",
|
|
1046
|
+
w: "webhookUrl"
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
function parseArgs(args) {
|
|
1051
|
+
const initial = (0, import_minimist.default)(args, {
|
|
1052
|
+
stopEarly: true,
|
|
1053
|
+
boolean: ["help"],
|
|
1054
|
+
alias: { h: "help" }
|
|
1055
|
+
});
|
|
1056
|
+
const command = initial._[0];
|
|
1057
|
+
if (!command || initial.help) {
|
|
1058
|
+
return initial;
|
|
1059
|
+
}
|
|
1060
|
+
const commandConfig = COMMAND_CONFIGS[command] || {};
|
|
1061
|
+
const booleanOptions = Array.isArray(commandConfig.boolean) ? ["help", ...commandConfig.boolean] : ["help"];
|
|
1062
|
+
const parsed = (0, import_minimist.default)(args, {
|
|
1063
|
+
...commandConfig,
|
|
1064
|
+
boolean: booleanOptions,
|
|
1065
|
+
alias: {
|
|
1066
|
+
...commandConfig.alias,
|
|
1067
|
+
h: "help"
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
return parsed;
|
|
1071
|
+
}
|
|
1072
|
+
function isInitArgs(args) {
|
|
1073
|
+
return args._[0] === "init";
|
|
1074
|
+
}
|
|
1075
|
+
function isUpdateArgs(args) {
|
|
1076
|
+
return args._[0] === "update";
|
|
1077
|
+
}
|
|
1078
|
+
function isSkillArgs(args) {
|
|
1079
|
+
return args._[0] === "install-skill";
|
|
1080
|
+
}
|
|
1081
|
+
function isCreateArgs(args) {
|
|
1082
|
+
return args._[0] === "create";
|
|
1083
|
+
}
|
|
1084
|
+
function isMetadataArgs(args) {
|
|
1085
|
+
return args._[0] === "metadata";
|
|
1086
|
+
}
|
|
1087
|
+
function isSetupArgs(args) {
|
|
1088
|
+
return args._[0] === "setup";
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// src/index.ts
|
|
1092
|
+
(0, import_dotenv.config)({ path: (0, import_path2.resolve)(__dirname, "../../generated/deployments/local_dev/.env") });
|
|
1093
|
+
(0, import_dotenv.config)({ path: (0, import_path2.resolve)(__dirname, "../../contracts/deployments/envs/local/.env") });
|
|
1094
|
+
async function main() {
|
|
1095
|
+
const args = parseArgs(process.argv.slice(2));
|
|
1096
|
+
const command = args._[0];
|
|
1097
|
+
if (args.help || !command) {
|
|
1098
|
+
showHelp();
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
try {
|
|
1102
|
+
switch (command) {
|
|
1103
|
+
case "init":
|
|
1104
|
+
if (isInitArgs(args)) {
|
|
1105
|
+
await init(args);
|
|
1106
|
+
}
|
|
1107
|
+
break;
|
|
1108
|
+
case "update":
|
|
1109
|
+
if (isUpdateArgs(args)) {
|
|
1110
|
+
await update(args);
|
|
1111
|
+
}
|
|
1112
|
+
break;
|
|
1113
|
+
case "install-skill":
|
|
1114
|
+
if (isSkillArgs(args)) {
|
|
1115
|
+
await skill(args);
|
|
1116
|
+
}
|
|
1117
|
+
break;
|
|
1118
|
+
case "create":
|
|
1119
|
+
if (isCreateArgs(args)) {
|
|
1120
|
+
await create(args);
|
|
1121
|
+
}
|
|
1122
|
+
break;
|
|
1123
|
+
case "metadata":
|
|
1124
|
+
if (isMetadataArgs(args)) {
|
|
1125
|
+
await metadata(args);
|
|
1126
|
+
}
|
|
1127
|
+
break;
|
|
1128
|
+
case "setup":
|
|
1129
|
+
if (isSetupArgs(args)) {
|
|
1130
|
+
await setup(args);
|
|
1131
|
+
}
|
|
1132
|
+
break;
|
|
1133
|
+
default:
|
|
1134
|
+
console.error((0, import_picocolors8.red)(`Unknown command: ${command}`));
|
|
1135
|
+
showHelp();
|
|
1136
|
+
process.exit(1);
|
|
1137
|
+
}
|
|
1138
|
+
} catch (error) {
|
|
1139
|
+
console.error((0, import_picocolors8.red)("Error:"), error instanceof Error ? error.message : error);
|
|
1140
|
+
process.exit(1);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
function showHelp() {
|
|
1144
|
+
console.log(`
|
|
1145
|
+
${(0, import_picocolors8.cyan)("towns-agent")} - CLI for creating and managing Towns Protocol agent projects
|
|
1146
|
+
|
|
1147
|
+
${(0, import_picocolors8.yellow)("Usage:")}
|
|
1148
|
+
towns-agent <command> [options]
|
|
1149
|
+
|
|
1150
|
+
${(0, import_picocolors8.yellow)("Commands:")}
|
|
1151
|
+
${(0, import_picocolors8.green)("init")} [project-name] Create a new agent project
|
|
1152
|
+
${(0, import_picocolors8.green)("create")} Create a new bot/app account interactively
|
|
1153
|
+
${(0, import_picocolors8.green)("setup")} <appAddress> Register webhook and notification settings
|
|
1154
|
+
${(0, import_picocolors8.green)("metadata")} <view|update> View or update app metadata
|
|
1155
|
+
${(0, import_picocolors8.green)("update")} Update @towns-labs dependencies and skills
|
|
1156
|
+
${(0, import_picocolors8.green)("install-skill")} Install Towns Agent Skills to current project
|
|
1157
|
+
|
|
1158
|
+
${(0, import_picocolors8.yellow)("Init Options:")}
|
|
1159
|
+
-t, --template <name> Template to use:
|
|
1160
|
+
${Object.entries(TEMPLATES).map(
|
|
1161
|
+
([key, template]) => ` ${key} - ${template.description}`
|
|
1162
|
+
).join("\n")}
|
|
1163
|
+
Default: quickstart
|
|
1164
|
+
|
|
1165
|
+
${(0, import_picocolors8.yellow)("List Commands Options:")}
|
|
1166
|
+
-f, --file <path> Path to commands file
|
|
1167
|
+
|
|
1168
|
+
${(0, import_picocolors8.yellow)("Update Commands Options:")}
|
|
1169
|
+
-f, --file <path> Path to commands file
|
|
1170
|
+
-t, --bearerToken <token> Bearer token for authentication
|
|
1171
|
+
-e, --envFile <path> Path to .env file (default: .env)
|
|
1172
|
+
--skip-agents-md Skip updating AGENTS.md file
|
|
1173
|
+
|
|
1174
|
+
${(0, import_picocolors8.yellow)("Global Options:")}
|
|
1175
|
+
-h, --help Show this help message
|
|
1176
|
+
|
|
1177
|
+
${(0, import_picocolors8.yellow)("Examples:")}
|
|
1178
|
+
${(0, import_picocolors8.cyan)("# Create a new agent project")}
|
|
1179
|
+
towns-agent init my-agent
|
|
1180
|
+
towns-agent init my-ai-agent --template quickstart
|
|
1181
|
+
|
|
1182
|
+
${(0, import_picocolors8.cyan)("# Update dependencies")}
|
|
1183
|
+
towns-agent update
|
|
1184
|
+
`);
|
|
1185
|
+
}
|
|
1186
|
+
main().catch((error) => {
|
|
1187
|
+
console.error((0, import_picocolors8.red)("Unexpected error:"), error);
|
|
1188
|
+
process.exit(1);
|
|
1189
|
+
});
|
|
1190
|
+
//# sourceMappingURL=index.js.map
|