towns-agent 2.0.5 → 2.0.6
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 +28 -8
- package/dist/index.js +209 -220
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +198 -210
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -8
- package/templates/quickstart/.env.sample +4 -0
- package/templates/quickstart/.turbo/turbo-build.log +2 -0
- package/templates/quickstart/AGENTS.md +267 -0
- package/templates/quickstart/README.md +95 -0
- package/templates/quickstart/_gitignore +33 -0
- package/templates/quickstart/package.json +35 -0
- package/templates/quickstart/src/commands.ts +12 -0
- package/templates/quickstart/src/index.ts +56 -0
- package/templates/quickstart/tsconfig.json +25 -0
package/dist/index.js
CHANGED
|
@@ -35,9 +35,18 @@ var jsonc = __toESM(require("jsonc-parser"));
|
|
|
35
35
|
// src/modules/utils.ts
|
|
36
36
|
var fs = __toESM(require("fs"));
|
|
37
37
|
var path = __toESM(require("path"));
|
|
38
|
+
var import_node_url = require("url");
|
|
38
39
|
var import_cross_spawn = __toESM(require("cross-spawn"));
|
|
39
40
|
var import_prompts = __toESM(require("prompts"));
|
|
40
41
|
var import_picocolors = __toESM(require("picocolors"));
|
|
42
|
+
var import_dotenv = require("dotenv");
|
|
43
|
+
var import_sdk = require("@towns-labs/sdk");
|
|
44
|
+
|
|
45
|
+
// package.json
|
|
46
|
+
var version = "2.0.6";
|
|
47
|
+
|
|
48
|
+
// src/modules/utils.ts
|
|
49
|
+
var import_meta = {};
|
|
41
50
|
var getPackageManager = () => {
|
|
42
51
|
if (process.env.npm_config_user_agent) {
|
|
43
52
|
const agent = process.env.npm_config_user_agent;
|
|
@@ -85,7 +94,7 @@ function getDlxCommand(packageManager) {
|
|
|
85
94
|
}
|
|
86
95
|
}
|
|
87
96
|
function runCommand(command, args, opts = { cwd: void 0, silent: false }) {
|
|
88
|
-
return new Promise((
|
|
97
|
+
return new Promise((resolve3, reject) => {
|
|
89
98
|
const child = (0, import_cross_spawn.default)(command, args, {
|
|
90
99
|
stdio: opts.silent ? "ignore" : "inherit",
|
|
91
100
|
cwd: opts.cwd,
|
|
@@ -95,118 +104,58 @@ function runCommand(command, args, opts = { cwd: void 0, silent: false }) {
|
|
|
95
104
|
if (code !== 0) {
|
|
96
105
|
reject(new Error(`Command failed with exit code ${code}`));
|
|
97
106
|
} else {
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
child.on("error", reject);
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
async function getLatestTownsProtocolVersion() {
|
|
105
|
-
return new Promise((resolve2, reject) => {
|
|
106
|
-
const child = (0, import_cross_spawn.default)("npm", ["view", "@towns-labs/agent", "version"], {
|
|
107
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
108
|
-
});
|
|
109
|
-
let output = "";
|
|
110
|
-
child.stdout?.on("data", (data) => {
|
|
111
|
-
output += data.toString();
|
|
112
|
-
});
|
|
113
|
-
child.on("close", (code) => {
|
|
114
|
-
if (code !== 0) {
|
|
115
|
-
reject(new Error("Failed to fetch latest @towns-labs/agent version"));
|
|
116
|
-
} else {
|
|
117
|
-
resolve2(output.trim());
|
|
107
|
+
resolve3();
|
|
118
108
|
}
|
|
119
109
|
});
|
|
120
110
|
child.on("error", reject);
|
|
121
111
|
});
|
|
122
112
|
}
|
|
123
|
-
function
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
const tags = tagsResult.stdout.split("\n").filter(Boolean).map((line) => {
|
|
131
|
-
const [_hash, ref] = line.split(" ");
|
|
132
|
-
const tag = ref.replace("refs/tags/", "").replace(/\^{}$/, "");
|
|
133
|
-
const match = tag.match(/^@towns-protocol\/sdk@(\d+)\.(\d+)\.(\d+)$/);
|
|
134
|
-
if (!match) return null;
|
|
135
|
-
return {
|
|
136
|
-
tag,
|
|
137
|
-
version: [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])]
|
|
138
|
-
};
|
|
139
|
-
}).filter(
|
|
140
|
-
(item) => item !== null && Array.isArray(item.version) && item.version.length === 3
|
|
141
|
-
).sort((a, b) => {
|
|
142
|
-
for (let i = 0; i < 3; i++) {
|
|
143
|
-
if (a.version[i] !== b.version[i]) {
|
|
144
|
-
return b.version[i] - a.version[i];
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return 0;
|
|
148
|
-
});
|
|
149
|
-
return tags.length > 0 ? tags[0].tag : null;
|
|
113
|
+
function getTemplatesDir() {
|
|
114
|
+
const currentDir = typeof __dirname !== "undefined" ? __dirname : path.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
115
|
+
const fromDist = path.resolve(currentDir, "..", "templates");
|
|
116
|
+
if (fs.existsSync(fromDist)) return fromDist;
|
|
117
|
+
const fromSrc = path.resolve(currentDir, "..", "..", "templates");
|
|
118
|
+
if (fs.existsSync(fromSrc)) return fromSrc;
|
|
119
|
+
throw new Error("Templates directory not found");
|
|
150
120
|
}
|
|
151
|
-
|
|
152
|
-
console.log(import_picocolors.default.blue("
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const latestSdkTag = getLatestSdkTag();
|
|
156
|
-
if (!latestSdkTag) {
|
|
157
|
-
console.error(import_picocolors.default.red("Failed to get latest SDK tag."));
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
const cloneResult = import_cross_spawn.default.sync(
|
|
161
|
-
"git",
|
|
162
|
-
[
|
|
163
|
-
"clone",
|
|
164
|
-
"--no-checkout",
|
|
165
|
-
"--depth",
|
|
166
|
-
"1",
|
|
167
|
-
"--sparse",
|
|
168
|
-
"--branch",
|
|
169
|
-
latestSdkTag,
|
|
170
|
-
"https://github.com/towns-protocol/towns.git",
|
|
171
|
-
tempDir
|
|
172
|
-
],
|
|
173
|
-
{ stdio: "pipe" }
|
|
174
|
-
);
|
|
175
|
-
if (cloneResult.status !== 0) return false;
|
|
176
|
-
const sparseResult = import_cross_spawn.default.sync("git", ["sparse-checkout", "set", fullTemplatePath], {
|
|
177
|
-
stdio: "pipe",
|
|
178
|
-
cwd: tempDir
|
|
179
|
-
});
|
|
180
|
-
if (sparseResult.status !== 0) {
|
|
181
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
182
|
-
return false;
|
|
183
|
-
}
|
|
184
|
-
const checkoutResult = import_cross_spawn.default.sync("git", ["checkout"], {
|
|
185
|
-
stdio: "pipe",
|
|
186
|
-
cwd: tempDir
|
|
187
|
-
});
|
|
188
|
-
if (checkoutResult.status !== 0) {
|
|
189
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
const sourceDir = path.join(tempDir, fullTemplatePath);
|
|
121
|
+
function copyTemplate(templateName, targetDir) {
|
|
122
|
+
console.log(import_picocolors.default.blue("Copying template..."));
|
|
123
|
+
const templatesDir = getTemplatesDir();
|
|
124
|
+
const sourceDir = path.join(templatesDir, templateName);
|
|
193
125
|
if (!fs.existsSync(sourceDir)) {
|
|
194
|
-
console.error(import_picocolors.default.red(`
|
|
195
|
-
Template directory not found at ${sourceDir}`));
|
|
196
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
126
|
+
console.error(import_picocolors.default.red(`Template "${templateName}" not found at ${sourceDir}`));
|
|
197
127
|
return false;
|
|
198
128
|
}
|
|
199
129
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
200
130
|
fs.cpSync(sourceDir, targetDir, {
|
|
201
131
|
recursive: true,
|
|
202
|
-
filter: () => {
|
|
203
|
-
|
|
132
|
+
filter: (source) => {
|
|
133
|
+
const basename2 = path.basename(source);
|
|
134
|
+
return basename2 !== "node_modules" && basename2 !== "dist";
|
|
204
135
|
}
|
|
205
136
|
});
|
|
206
|
-
|
|
207
|
-
|
|
137
|
+
const gitignoreSrc = path.join(targetDir, "_gitignore");
|
|
138
|
+
const gitignoreDest = path.join(targetDir, ".gitignore");
|
|
139
|
+
if (fs.existsSync(gitignoreSrc)) {
|
|
140
|
+
fs.renameSync(gitignoreSrc, gitignoreDest);
|
|
141
|
+
}
|
|
142
|
+
console.log(import_picocolors.default.green("\u2713"), "Template copied successfully!");
|
|
208
143
|
return true;
|
|
209
144
|
}
|
|
145
|
+
function copyAgentsMd(projectDir) {
|
|
146
|
+
try {
|
|
147
|
+
const templatesDir = getTemplatesDir();
|
|
148
|
+
const sourceFile = path.join(templatesDir, "quickstart", "AGENTS.md");
|
|
149
|
+
if (!fs.existsSync(sourceFile)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const destFile = path.join(projectDir, "AGENTS.md");
|
|
153
|
+
fs.copyFileSync(sourceFile, destFile);
|
|
154
|
+
return true;
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
210
159
|
function applyReplacements(targetDir, replacements) {
|
|
211
160
|
function processDirectory(dir) {
|
|
212
161
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
@@ -332,70 +281,34 @@ async function installTownsSkills(projectDir) {
|
|
|
332
281
|
return false;
|
|
333
282
|
}
|
|
334
283
|
}
|
|
335
|
-
|
|
336
|
-
|
|
284
|
+
function parseDotenv() {
|
|
285
|
+
return (0, import_dotenv.config)({ override: false }).parsed;
|
|
286
|
+
}
|
|
287
|
+
function envFromAppPrivateData(parsed) {
|
|
288
|
+
const appPrivateData = parsed?.APP_PRIVATE_DATA;
|
|
289
|
+
if (!appPrivateData) {
|
|
290
|
+
return void 0;
|
|
291
|
+
}
|
|
337
292
|
try {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
"--branch",
|
|
352
|
-
latestSdkTag,
|
|
353
|
-
"https://github.com/towns-protocol/towns.git",
|
|
354
|
-
tempDir
|
|
355
|
-
],
|
|
356
|
-
{ stdio: "pipe" }
|
|
357
|
-
);
|
|
358
|
-
if (cloneResult.status !== 0) {
|
|
359
|
-
if (fs.existsSync(tempDir)) {
|
|
360
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
361
|
-
}
|
|
362
|
-
return false;
|
|
363
|
-
}
|
|
364
|
-
const sparseResult = import_cross_spawn.default.sync("git", ["sparse-checkout", "set", agentsMdPath], {
|
|
365
|
-
stdio: "pipe",
|
|
366
|
-
cwd: tempDir
|
|
367
|
-
});
|
|
368
|
-
if (sparseResult.status !== 0) {
|
|
369
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
370
|
-
return false;
|
|
371
|
-
}
|
|
372
|
-
const checkoutResult = import_cross_spawn.default.sync("git", ["checkout"], {
|
|
373
|
-
stdio: "pipe",
|
|
374
|
-
cwd: tempDir
|
|
375
|
-
});
|
|
376
|
-
if (checkoutResult.status !== 0) {
|
|
377
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
378
|
-
return false;
|
|
379
|
-
}
|
|
380
|
-
const sourceFile = path.join(tempDir, agentsMdPath);
|
|
381
|
-
if (!fs.existsSync(sourceFile)) {
|
|
382
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
385
|
-
const destFile = path.join(projectDir, "AGENTS.md");
|
|
386
|
-
fs.copyFileSync(sourceFile, destFile);
|
|
387
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
388
|
-
return true;
|
|
389
|
-
} catch (error) {
|
|
390
|
-
console.error(
|
|
391
|
-
import_picocolors.default.red("Error downloading AGENTS.md:"),
|
|
392
|
-
error instanceof Error ? error.message : error
|
|
393
|
-
);
|
|
394
|
-
if (fs.existsSync(tempDir)) {
|
|
395
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
396
|
-
}
|
|
397
|
-
return false;
|
|
293
|
+
return (0, import_sdk.parseAppPrivateData)(appPrivateData);
|
|
294
|
+
} catch {
|
|
295
|
+
return void 0;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function resolveAppAddress(positionalArg) {
|
|
299
|
+
if (positionalArg) {
|
|
300
|
+
return positionalArg;
|
|
301
|
+
}
|
|
302
|
+
const parsed = parseDotenv();
|
|
303
|
+
const appAddress = parsed?.APP_ADDRESS;
|
|
304
|
+
if (appAddress) {
|
|
305
|
+
return appAddress;
|
|
398
306
|
}
|
|
307
|
+
return envFromAppPrivateData(parsed)?.appAddress;
|
|
308
|
+
}
|
|
309
|
+
function resolveRiverEnv() {
|
|
310
|
+
const parsed = parseDotenv();
|
|
311
|
+
return parsed?.RIVER_ENV ?? envFromAppPrivateData(parsed)?.env;
|
|
399
312
|
}
|
|
400
313
|
async function promptAuth() {
|
|
401
314
|
const { method } = await (0, import_prompts.default)({
|
|
@@ -424,7 +337,7 @@ var TEMPLATES = {
|
|
|
424
337
|
quickstart: {
|
|
425
338
|
name: "Agent Quickstart",
|
|
426
339
|
description: "Simple starter agent with basic commands",
|
|
427
|
-
packagePath: "
|
|
340
|
+
packagePath: "quickstart"
|
|
428
341
|
}
|
|
429
342
|
};
|
|
430
343
|
async function init(argv) {
|
|
@@ -461,12 +374,12 @@ async function init(argv) {
|
|
|
461
374
|
const packageManager = getPackageManager();
|
|
462
375
|
const selectedTemplate = TEMPLATES[template];
|
|
463
376
|
try {
|
|
464
|
-
const success =
|
|
377
|
+
const success = copyTemplate(selectedTemplate.packagePath, targetDir);
|
|
465
378
|
if (!success) {
|
|
466
|
-
console.error((0, import_picocolors2.red)("Failed to
|
|
379
|
+
console.error((0, import_picocolors2.red)("Failed to copy template"));
|
|
467
380
|
process.exit(1);
|
|
468
381
|
}
|
|
469
|
-
const latestVersion =
|
|
382
|
+
const latestVersion = version;
|
|
470
383
|
const replacements = /* @__PURE__ */ new Map([
|
|
471
384
|
["workspace:\\^", `^${latestVersion}`],
|
|
472
385
|
["workspace:\\*", `^${latestVersion}`]
|
|
@@ -491,19 +404,12 @@ async function init(argv) {
|
|
|
491
404
|
if (skillSuccess) {
|
|
492
405
|
console.log((0, import_picocolors2.green)("\u2713"), "Towns Agent Skills installed successfully!");
|
|
493
406
|
} else {
|
|
494
|
-
console.log(
|
|
495
|
-
(0, import_picocolors2.yellow)("\u26A0"),
|
|
496
|
-
"Failed to install Towns Agent Skills. You can install them later with:"
|
|
497
|
-
);
|
|
407
|
+
console.log((0, import_picocolors2.yellow)("\u26A0"), "Skipping Towns Agent Skills. Install later with:");
|
|
498
408
|
console.log((0, import_picocolors2.yellow)(` cd ${projectName} && towns-agent install-skill`));
|
|
499
409
|
}
|
|
500
|
-
} catch
|
|
501
|
-
console.log(
|
|
502
|
-
|
|
503
|
-
"Error installing skills:",
|
|
504
|
-
error instanceof Error ? error.message : error
|
|
505
|
-
);
|
|
506
|
-
console.log((0, import_picocolors2.yellow)(` You can install them later with: towns-agent install-skill`));
|
|
410
|
+
} catch {
|
|
411
|
+
console.log((0, import_picocolors2.yellow)("\u26A0"), "Skipping Towns Agent Skills. Install later with:");
|
|
412
|
+
console.log((0, import_picocolors2.yellow)(` cd ${projectName} && towns-agent install-skill`));
|
|
507
413
|
}
|
|
508
414
|
await initializeGitRepository(targetDir);
|
|
509
415
|
printSuccess(projectName, packageManager);
|
|
@@ -522,9 +428,9 @@ function getTownsVersions(packageJson) {
|
|
|
522
428
|
const versions = {};
|
|
523
429
|
for (const deps of [packageJson.dependencies, packageJson.devDependencies]) {
|
|
524
430
|
if (deps) {
|
|
525
|
-
for (const [pkg,
|
|
431
|
+
for (const [pkg, version2] of Object.entries(deps)) {
|
|
526
432
|
if (pkg.startsWith("@towns-labs/") || pkg.startsWith("@towns-protocol/")) {
|
|
527
|
-
versions[pkg] =
|
|
433
|
+
versions[pkg] = version2;
|
|
528
434
|
}
|
|
529
435
|
}
|
|
530
436
|
}
|
|
@@ -607,7 +513,7 @@ async function update(_argv) {
|
|
|
607
513
|
console.log();
|
|
608
514
|
console.log((0, import_picocolors3.cyan)("Updating AGENTS.md..."));
|
|
609
515
|
try {
|
|
610
|
-
const agentsMdSuccess =
|
|
516
|
+
const agentsMdSuccess = copyAgentsMd(projectDir);
|
|
611
517
|
if (agentsMdSuccess) {
|
|
612
518
|
console.log((0, import_picocolors3.green)("\u2713"), "AGENTS.md updated successfully!");
|
|
613
519
|
} else {
|
|
@@ -661,7 +567,7 @@ var import_picocolors5 = require("picocolors");
|
|
|
661
567
|
var import_viem = require("viem");
|
|
662
568
|
var import_accounts = require("viem/accounts");
|
|
663
569
|
var import_relayer_client = require("@towns-labs/relayer-client");
|
|
664
|
-
var
|
|
570
|
+
var import_sdk2 = require("@towns-labs/sdk");
|
|
665
571
|
var import_deployments = require("@towns-labs/contracts/deployments");
|
|
666
572
|
async function create(argv) {
|
|
667
573
|
let ownerPrivateKey = argv.ownerPrivateKey;
|
|
@@ -694,12 +600,12 @@ async function create(argv) {
|
|
|
694
600
|
process.exit(1);
|
|
695
601
|
}
|
|
696
602
|
const imageUrl = argv.imageUrl ?? await promptText("Bot image URL (optional)", true);
|
|
697
|
-
const owner = ownerPrivateKey ? await (0,
|
|
603
|
+
const owner = ownerPrivateKey ? await (0, import_sdk2.makeSignerContextFromViem)(
|
|
698
604
|
(0, import_accounts.privateKeyToAccount)(ownerPrivateKey),
|
|
699
605
|
(0, import_accounts.generatePrivateKey)(),
|
|
700
606
|
{ days: 1 }
|
|
701
|
-
) : await (0,
|
|
702
|
-
const townsConfig = (0,
|
|
607
|
+
) : await (0, import_sdk2.makeSignerContextFromBearerToken)(bearerToken);
|
|
608
|
+
const townsConfig = (0, import_sdk2.townsEnv)().makeTownsConfig();
|
|
703
609
|
const chainId = townsConfig.base.chainConfig.chainId;
|
|
704
610
|
const addresses = (0, import_deployments.getAddressesWithFallback)(townsConfig.environmentId, chainId);
|
|
705
611
|
if (!addresses?.accountProxy) {
|
|
@@ -715,7 +621,7 @@ async function create(argv) {
|
|
|
715
621
|
},
|
|
716
622
|
transport: (0, import_viem.http)(townsConfig.base.rpcUrl)
|
|
717
623
|
}).extend((0, import_relayer_client.relayerActions)({ relayerUrl }));
|
|
718
|
-
const result = await (0,
|
|
624
|
+
const result = await (0, import_sdk2.createApp)({
|
|
719
625
|
owner,
|
|
720
626
|
metadata: {
|
|
721
627
|
username,
|
|
@@ -747,7 +653,7 @@ async function promptText(message, optional = false) {
|
|
|
747
653
|
var import_prompts4 = __toESM(require("prompts"));
|
|
748
654
|
var import_picocolors6 = require("picocolors");
|
|
749
655
|
var import_accounts2 = require("viem/accounts");
|
|
750
|
-
var
|
|
656
|
+
var import_sdk3 = require("@towns-labs/sdk");
|
|
751
657
|
var import_utils5 = require("@towns-labs/utils");
|
|
752
658
|
var FIELD_DEFS = [
|
|
753
659
|
{ flag: "username", label: "USERNAME", mask: "username", prompt: "Username" },
|
|
@@ -765,21 +671,25 @@ var FIELD_DEFS = [
|
|
|
765
671
|
];
|
|
766
672
|
async function metadata(argv) {
|
|
767
673
|
const subcommand = argv._[1];
|
|
768
|
-
const appAddress = argv._[2];
|
|
674
|
+
const appAddress = resolveAppAddress(argv._[2]);
|
|
769
675
|
if (!subcommand || !["view", "update"].includes(subcommand)) {
|
|
770
|
-
console.error((0, import_picocolors6.red)("Usage: towns-agent metadata <view|update>
|
|
676
|
+
console.error((0, import_picocolors6.red)("Usage: towns-agent metadata <view|update> [appAddress] [options]"));
|
|
771
677
|
process.exit(1);
|
|
772
678
|
}
|
|
773
679
|
if (!appAddress) {
|
|
774
|
-
console.error(
|
|
680
|
+
console.error(
|
|
681
|
+
(0, import_picocolors6.red)(
|
|
682
|
+
"App address is required. Provide it as an argument, or set APP_ADDRESS or APP_PRIVATE_DATA in .env."
|
|
683
|
+
)
|
|
684
|
+
);
|
|
775
685
|
process.exit(1);
|
|
776
686
|
}
|
|
777
|
-
const env = (0,
|
|
687
|
+
const env = (0, import_sdk3.townsEnv)();
|
|
778
688
|
const townsConfig = env.makeTownsConfig();
|
|
779
689
|
const appRegistryUrl = env.getAppRegistryUrl(townsConfig.environmentId);
|
|
780
690
|
const appId = (0, import_utils5.bin_fromHexString)(appAddress);
|
|
781
691
|
if (subcommand === "view") {
|
|
782
|
-
const client = (0,
|
|
692
|
+
const client = (0, import_sdk3.makeAppRegistryRpcClient)(appRegistryUrl, "");
|
|
783
693
|
const { metadata: meta } = await client.getAppMetadata({ appId });
|
|
784
694
|
if (!meta) {
|
|
785
695
|
console.error((0, import_picocolors6.red)("No metadata found for this app."));
|
|
@@ -807,12 +717,12 @@ async function metadata(argv) {
|
|
|
807
717
|
bearerToken = auth.value;
|
|
808
718
|
}
|
|
809
719
|
}
|
|
810
|
-
const signerContext = ownerPrivateKey ? await (0,
|
|
720
|
+
const signerContext = ownerPrivateKey ? await (0, import_sdk3.makeSignerContextFromViem)(
|
|
811
721
|
(0, import_accounts2.privateKeyToAccount)(ownerPrivateKey),
|
|
812
722
|
(0, import_accounts2.generatePrivateKey)(),
|
|
813
723
|
{ days: 1 }
|
|
814
|
-
) : await (0,
|
|
815
|
-
const { appRegistryRpcClient } = await
|
|
724
|
+
) : await (0, import_sdk3.makeSignerContextFromBearerToken)(bearerToken);
|
|
725
|
+
const { appRegistryRpcClient } = await import_sdk3.AppRegistryService.authenticate(
|
|
816
726
|
signerContext,
|
|
817
727
|
appRegistryUrl
|
|
818
728
|
);
|
|
@@ -826,7 +736,7 @@ async function metadata(argv) {
|
|
|
826
736
|
}
|
|
827
737
|
}
|
|
828
738
|
} else {
|
|
829
|
-
const unauthClient = (0,
|
|
739
|
+
const unauthClient = (0, import_sdk3.makeAppRegistryRpcClient)(appRegistryUrl, "");
|
|
830
740
|
const { metadata: current } = await unauthClient.getAppMetadata({ appId });
|
|
831
741
|
values = {};
|
|
832
742
|
for (const field of FIELD_DEFS) {
|
|
@@ -874,7 +784,7 @@ async function metadata(argv) {
|
|
|
874
784
|
var import_prompts5 = __toESM(require("prompts"));
|
|
875
785
|
var import_picocolors7 = require("picocolors");
|
|
876
786
|
var import_accounts3 = require("viem/accounts");
|
|
877
|
-
var
|
|
787
|
+
var import_sdk4 = require("@towns-labs/sdk");
|
|
878
788
|
var import_proto = require("@towns-labs/proto");
|
|
879
789
|
var import_utils7 = require("@towns-labs/utils");
|
|
880
790
|
var NOTIFY_MAP = {
|
|
@@ -888,9 +798,13 @@ var NOTIFY_LABELS = {
|
|
|
888
798
|
NONE: "No messages"
|
|
889
799
|
};
|
|
890
800
|
async function setup(argv) {
|
|
891
|
-
const appAddress = argv._[1];
|
|
801
|
+
const appAddress = resolveAppAddress(argv._[1]);
|
|
892
802
|
if (!appAddress) {
|
|
893
|
-
console.error(
|
|
803
|
+
console.error(
|
|
804
|
+
(0, import_picocolors7.red)(
|
|
805
|
+
"App address is required. Provide it as an argument, or set APP_ADDRESS or APP_PRIVATE_DATA in .env."
|
|
806
|
+
)
|
|
807
|
+
);
|
|
894
808
|
process.exit(1);
|
|
895
809
|
}
|
|
896
810
|
let ownerPrivateKey = argv.ownerPrivateKey;
|
|
@@ -907,11 +821,18 @@ async function setup(argv) {
|
|
|
907
821
|
bearerToken = auth.value;
|
|
908
822
|
}
|
|
909
823
|
}
|
|
910
|
-
|
|
824
|
+
let webhookUrl = argv.webhookUrl ?? await promptWebhookUrl();
|
|
911
825
|
if (!webhookUrl) {
|
|
912
826
|
console.error((0, import_picocolors7.red)("Webhook URL is required."));
|
|
913
827
|
process.exit(1);
|
|
914
828
|
}
|
|
829
|
+
if (argv.webhookUrl) {
|
|
830
|
+
const urlError = validateWebhookUrl(webhookUrl);
|
|
831
|
+
if (urlError !== true) {
|
|
832
|
+
console.error((0, import_picocolors7.red)(urlError));
|
|
833
|
+
process.exit(1);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
915
836
|
let notifyKey;
|
|
916
837
|
if (argv.notify) {
|
|
917
838
|
notifyKey = argv.notify.toUpperCase();
|
|
@@ -927,20 +848,39 @@ async function setup(argv) {
|
|
|
927
848
|
);
|
|
928
849
|
process.exit(1);
|
|
929
850
|
}
|
|
930
|
-
const env = (0,
|
|
851
|
+
const env = (0, import_sdk4.townsEnv)();
|
|
931
852
|
const townsConfig = env.makeTownsConfig();
|
|
932
853
|
const appRegistryUrl = env.getAppRegistryUrl(townsConfig.environmentId);
|
|
933
854
|
const appId = (0, import_utils7.bin_fromHexString)(appAddress);
|
|
934
|
-
const signerContext = ownerPrivateKey ? await (0,
|
|
855
|
+
const signerContext = ownerPrivateKey ? await (0, import_sdk4.makeSignerContextFromViem)(
|
|
935
856
|
(0, import_accounts3.privateKeyToAccount)(ownerPrivateKey),
|
|
936
857
|
(0, import_accounts3.generatePrivateKey)(),
|
|
937
858
|
{ days: 1 }
|
|
938
|
-
) : await (0,
|
|
939
|
-
const { appRegistryRpcClient } = await
|
|
859
|
+
) : await (0, import_sdk4.makeSignerContextFromBearerToken)(bearerToken);
|
|
860
|
+
const { appRegistryRpcClient } = await import_sdk4.AppRegistryService.authenticate(
|
|
940
861
|
signerContext,
|
|
941
862
|
appRegistryUrl
|
|
942
863
|
);
|
|
943
|
-
|
|
864
|
+
try {
|
|
865
|
+
await appRegistryRpcClient.registerWebhook({ appId, webhookUrl });
|
|
866
|
+
} catch (error) {
|
|
867
|
+
if (!hasWebhookPath(webhookUrl)) {
|
|
868
|
+
const webhookUrlWithPath = appendWebhookPath(webhookUrl);
|
|
869
|
+
try {
|
|
870
|
+
await appRegistryRpcClient.registerWebhook({
|
|
871
|
+
appId,
|
|
872
|
+
webhookUrl: webhookUrlWithPath
|
|
873
|
+
});
|
|
874
|
+
console.log();
|
|
875
|
+
console.log((0, import_picocolors7.yellow)(`Registration succeeded with ${webhookUrlWithPath}`));
|
|
876
|
+
webhookUrl = webhookUrlWithPath;
|
|
877
|
+
} catch {
|
|
878
|
+
throw error;
|
|
879
|
+
}
|
|
880
|
+
} else {
|
|
881
|
+
throw error;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
944
884
|
await appRegistryRpcClient.setAppSettings({
|
|
945
885
|
appId,
|
|
946
886
|
settings: { forwardSetting }
|
|
@@ -953,12 +893,48 @@ async function setup(argv) {
|
|
|
953
893
|
console.log();
|
|
954
894
|
process.exit(0);
|
|
955
895
|
}
|
|
896
|
+
function hasWebhookPath(url) {
|
|
897
|
+
const trimmed = url.trim();
|
|
898
|
+
try {
|
|
899
|
+
const parsed = new URL(trimmed);
|
|
900
|
+
const normalizedPath = parsed.pathname.endsWith("/") ? parsed.pathname.slice(0, -1) : parsed.pathname;
|
|
901
|
+
return normalizedPath.endsWith("/webhook");
|
|
902
|
+
} catch {
|
|
903
|
+
return trimmed.replace(/\s*$/, "").endsWith("/webhook");
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
function appendWebhookPath(url) {
|
|
907
|
+
const trimmed = url.trim();
|
|
908
|
+
const parsed = new URL(trimmed);
|
|
909
|
+
const normalizedPath = parsed.pathname.endsWith("/") ? parsed.pathname.slice(0, -1) : parsed.pathname;
|
|
910
|
+
parsed.pathname = normalizedPath === "/" ? "/webhook" : `${normalizedPath}/webhook`;
|
|
911
|
+
return parsed.toString();
|
|
912
|
+
}
|
|
913
|
+
function validateWebhookUrl(url) {
|
|
914
|
+
const trimmed = url.trim();
|
|
915
|
+
if (!trimmed) {
|
|
916
|
+
return "Webhook URL is required";
|
|
917
|
+
}
|
|
918
|
+
try {
|
|
919
|
+
const parsed = new URL(trimmed);
|
|
920
|
+
const isLocalhost = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "0.0.0.0";
|
|
921
|
+
if (isLocalhost && parsed.protocol === "http:") {
|
|
922
|
+
return "Local bots must use HTTPS. Use https:// instead of http://";
|
|
923
|
+
}
|
|
924
|
+
if (isLocalhost && !parsed.port) {
|
|
925
|
+
return "Localhost URL is missing a port. Example: https://localhost:3000";
|
|
926
|
+
}
|
|
927
|
+
return true;
|
|
928
|
+
} catch {
|
|
929
|
+
return "Invalid URL format. Example: https://localhost:3000/webhook";
|
|
930
|
+
}
|
|
931
|
+
}
|
|
956
932
|
async function promptWebhookUrl() {
|
|
957
933
|
const { value } = await (0, import_prompts5.default)({
|
|
958
934
|
type: "text",
|
|
959
935
|
name: "value",
|
|
960
936
|
message: "Webhook URL",
|
|
961
|
-
validate:
|
|
937
|
+
validate: validateWebhookUrl
|
|
962
938
|
});
|
|
963
939
|
return value;
|
|
964
940
|
}
|
|
@@ -981,7 +957,7 @@ async function promptNotify() {
|
|
|
981
957
|
}
|
|
982
958
|
|
|
983
959
|
// src/index.ts
|
|
984
|
-
var
|
|
960
|
+
var import_sdk5 = require("@towns-labs/sdk");
|
|
985
961
|
|
|
986
962
|
// src/parser.ts
|
|
987
963
|
var import_minimist = __toESM(require("minimist"));
|
|
@@ -1100,20 +1076,31 @@ async function main() {
|
|
|
1100
1076
|
showHelp();
|
|
1101
1077
|
return;
|
|
1102
1078
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1079
|
+
const requiresEnv = ["create", "setup", "metadata"].includes(command);
|
|
1080
|
+
if (requiresEnv) {
|
|
1081
|
+
if (args.env) {
|
|
1082
|
+
process.env.RIVER_ENV = args.env;
|
|
1083
|
+
}
|
|
1084
|
+
if (!process.env.RIVER_ENV) {
|
|
1085
|
+
const resolvedEnv = resolveRiverEnv();
|
|
1086
|
+
if (resolvedEnv) {
|
|
1087
|
+
process.env.RIVER_ENV = resolvedEnv;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
if (!process.env.RIVER_ENV) {
|
|
1091
|
+
console.error(
|
|
1092
|
+
(0, import_picocolors8.red)(
|
|
1093
|
+
"Environment is required. Use --env <local_dev|stage|prod>, set RIVER_ENV, or set APP_PRIVATE_DATA in .env."
|
|
1094
|
+
)
|
|
1095
|
+
);
|
|
1096
|
+
process.exit(1);
|
|
1097
|
+
}
|
|
1098
|
+
try {
|
|
1099
|
+
(0, import_sdk5.townsEnv)().makeTownsConfig();
|
|
1100
|
+
} catch {
|
|
1101
|
+
console.error((0, import_picocolors8.red)(`Invalid environment: ${process.env.RIVER_ENV}`));
|
|
1102
|
+
process.exit(1);
|
|
1103
|
+
}
|
|
1117
1104
|
}
|
|
1118
1105
|
try {
|
|
1119
1106
|
switch (command) {
|
|
@@ -1167,8 +1154,8 @@ ${(0, import_picocolors8.yellow)("Usage:")}
|
|
|
1167
1154
|
${(0, import_picocolors8.yellow)("Commands:")}
|
|
1168
1155
|
${(0, import_picocolors8.green)("init")} [project-name] Create a new agent project
|
|
1169
1156
|
${(0, import_picocolors8.green)("create")} Create a new bot/app account interactively
|
|
1170
|
-
${(0, import_picocolors8.green)("setup")}
|
|
1171
|
-
${(0, import_picocolors8.green)("metadata")} <view|update> View or update app metadata
|
|
1157
|
+
${(0, import_picocolors8.green)("setup")} [appAddress] Register webhook and notification settings
|
|
1158
|
+
${(0, import_picocolors8.green)("metadata")} <view|update> [appAddress] View or update app metadata
|
|
1172
1159
|
${(0, import_picocolors8.green)("update")} Update @towns-labs dependencies and skills
|
|
1173
1160
|
${(0, import_picocolors8.green)("install-skill")} Install Towns Agent Skills to current project
|
|
1174
1161
|
|
|
@@ -1188,8 +1175,10 @@ ${(0, import_picocolors8.yellow)("Update Commands Options:")}
|
|
|
1188
1175
|
-e, --envFile <path> Path to .env file (default: .env)
|
|
1189
1176
|
--skip-agents-md Skip updating AGENTS.md file
|
|
1190
1177
|
|
|
1191
|
-
${(0, import_picocolors8.yellow)("
|
|
1178
|
+
${(0, import_picocolors8.yellow)("Environment Options (create, setup, metadata):")}
|
|
1192
1179
|
--env <name> Environment to use: local_dev, stage, prod
|
|
1180
|
+
|
|
1181
|
+
${(0, import_picocolors8.yellow)("Global Options:")}
|
|
1193
1182
|
-h, --help Show this help message
|
|
1194
1183
|
|
|
1195
1184
|
${(0, import_picocolors8.yellow)("Examples:")}
|