towns-agent 2.0.4 → 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 +214 -207
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +205 -199
- 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
|
@@ -23,8 +23,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
|
|
25
25
|
// src/index.ts
|
|
26
|
-
var import_path2 = require("path");
|
|
27
|
-
var import_dotenv = require("dotenv");
|
|
28
26
|
var import_picocolors8 = require("picocolors");
|
|
29
27
|
|
|
30
28
|
// src/modules/init.ts
|
|
@@ -37,9 +35,18 @@ var jsonc = __toESM(require("jsonc-parser"));
|
|
|
37
35
|
// src/modules/utils.ts
|
|
38
36
|
var fs = __toESM(require("fs"));
|
|
39
37
|
var path = __toESM(require("path"));
|
|
38
|
+
var import_node_url = require("url");
|
|
40
39
|
var import_cross_spawn = __toESM(require("cross-spawn"));
|
|
41
40
|
var import_prompts = __toESM(require("prompts"));
|
|
42
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 = {};
|
|
43
50
|
var getPackageManager = () => {
|
|
44
51
|
if (process.env.npm_config_user_agent) {
|
|
45
52
|
const agent = process.env.npm_config_user_agent;
|
|
@@ -103,112 +110,52 @@ function runCommand(command, args, opts = { cwd: void 0, silent: false }) {
|
|
|
103
110
|
child.on("error", reject);
|
|
104
111
|
});
|
|
105
112
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
});
|
|
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");
|
|
124
120
|
}
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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);
|
|
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);
|
|
195
125
|
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 });
|
|
126
|
+
console.error(import_picocolors.default.red(`Template "${templateName}" not found at ${sourceDir}`));
|
|
199
127
|
return false;
|
|
200
128
|
}
|
|
201
129
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
202
130
|
fs.cpSync(sourceDir, targetDir, {
|
|
203
131
|
recursive: true,
|
|
204
|
-
filter: () => {
|
|
205
|
-
|
|
132
|
+
filter: (source) => {
|
|
133
|
+
const basename2 = path.basename(source);
|
|
134
|
+
return basename2 !== "node_modules" && basename2 !== "dist";
|
|
206
135
|
}
|
|
207
136
|
});
|
|
208
|
-
|
|
209
|
-
|
|
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!");
|
|
210
143
|
return true;
|
|
211
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
|
+
}
|
|
212
159
|
function applyReplacements(targetDir, replacements) {
|
|
213
160
|
function processDirectory(dir) {
|
|
214
161
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
@@ -334,71 +281,35 @@ async function installTownsSkills(projectDir) {
|
|
|
334
281
|
return false;
|
|
335
282
|
}
|
|
336
283
|
}
|
|
337
|
-
|
|
338
|
-
|
|
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
|
+
}
|
|
339
292
|
try {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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;
|
|
293
|
+
return (0, import_sdk.parseAppPrivateData)(appPrivateData);
|
|
294
|
+
} catch {
|
|
295
|
+
return void 0;
|
|
400
296
|
}
|
|
401
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;
|
|
306
|
+
}
|
|
307
|
+
return envFromAppPrivateData(parsed)?.appAddress;
|
|
308
|
+
}
|
|
309
|
+
function resolveRiverEnv() {
|
|
310
|
+
const parsed = parseDotenv();
|
|
311
|
+
return parsed?.RIVER_ENV ?? envFromAppPrivateData(parsed)?.env;
|
|
312
|
+
}
|
|
402
313
|
async function promptAuth() {
|
|
403
314
|
const { method } = await (0, import_prompts.default)({
|
|
404
315
|
type: "select",
|
|
@@ -426,7 +337,7 @@ var TEMPLATES = {
|
|
|
426
337
|
quickstart: {
|
|
427
338
|
name: "Agent Quickstart",
|
|
428
339
|
description: "Simple starter agent with basic commands",
|
|
429
|
-
packagePath: "
|
|
340
|
+
packagePath: "quickstart"
|
|
430
341
|
}
|
|
431
342
|
};
|
|
432
343
|
async function init(argv) {
|
|
@@ -463,12 +374,12 @@ async function init(argv) {
|
|
|
463
374
|
const packageManager = getPackageManager();
|
|
464
375
|
const selectedTemplate = TEMPLATES[template];
|
|
465
376
|
try {
|
|
466
|
-
const success =
|
|
377
|
+
const success = copyTemplate(selectedTemplate.packagePath, targetDir);
|
|
467
378
|
if (!success) {
|
|
468
|
-
console.error((0, import_picocolors2.red)("Failed to
|
|
379
|
+
console.error((0, import_picocolors2.red)("Failed to copy template"));
|
|
469
380
|
process.exit(1);
|
|
470
381
|
}
|
|
471
|
-
const latestVersion =
|
|
382
|
+
const latestVersion = version;
|
|
472
383
|
const replacements = /* @__PURE__ */ new Map([
|
|
473
384
|
["workspace:\\^", `^${latestVersion}`],
|
|
474
385
|
["workspace:\\*", `^${latestVersion}`]
|
|
@@ -493,19 +404,12 @@ async function init(argv) {
|
|
|
493
404
|
if (skillSuccess) {
|
|
494
405
|
console.log((0, import_picocolors2.green)("\u2713"), "Towns Agent Skills installed successfully!");
|
|
495
406
|
} else {
|
|
496
|
-
console.log(
|
|
497
|
-
(0, import_picocolors2.yellow)("\u26A0"),
|
|
498
|
-
"Failed to install Towns Agent Skills. You can install them later with:"
|
|
499
|
-
);
|
|
407
|
+
console.log((0, import_picocolors2.yellow)("\u26A0"), "Skipping Towns Agent Skills. Install later with:");
|
|
500
408
|
console.log((0, import_picocolors2.yellow)(` cd ${projectName} && towns-agent install-skill`));
|
|
501
409
|
}
|
|
502
|
-
} catch
|
|
503
|
-
console.log(
|
|
504
|
-
|
|
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`));
|
|
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`));
|
|
509
413
|
}
|
|
510
414
|
await initializeGitRepository(targetDir);
|
|
511
415
|
printSuccess(projectName, packageManager);
|
|
@@ -524,9 +428,9 @@ function getTownsVersions(packageJson) {
|
|
|
524
428
|
const versions = {};
|
|
525
429
|
for (const deps of [packageJson.dependencies, packageJson.devDependencies]) {
|
|
526
430
|
if (deps) {
|
|
527
|
-
for (const [pkg,
|
|
431
|
+
for (const [pkg, version2] of Object.entries(deps)) {
|
|
528
432
|
if (pkg.startsWith("@towns-labs/") || pkg.startsWith("@towns-protocol/")) {
|
|
529
|
-
versions[pkg] =
|
|
433
|
+
versions[pkg] = version2;
|
|
530
434
|
}
|
|
531
435
|
}
|
|
532
436
|
}
|
|
@@ -609,7 +513,7 @@ async function update(_argv) {
|
|
|
609
513
|
console.log();
|
|
610
514
|
console.log((0, import_picocolors3.cyan)("Updating AGENTS.md..."));
|
|
611
515
|
try {
|
|
612
|
-
const agentsMdSuccess =
|
|
516
|
+
const agentsMdSuccess = copyAgentsMd(projectDir);
|
|
613
517
|
if (agentsMdSuccess) {
|
|
614
518
|
console.log((0, import_picocolors3.green)("\u2713"), "AGENTS.md updated successfully!");
|
|
615
519
|
} else {
|
|
@@ -663,7 +567,7 @@ var import_picocolors5 = require("picocolors");
|
|
|
663
567
|
var import_viem = require("viem");
|
|
664
568
|
var import_accounts = require("viem/accounts");
|
|
665
569
|
var import_relayer_client = require("@towns-labs/relayer-client");
|
|
666
|
-
var
|
|
570
|
+
var import_sdk2 = require("@towns-labs/sdk");
|
|
667
571
|
var import_deployments = require("@towns-labs/contracts/deployments");
|
|
668
572
|
async function create(argv) {
|
|
669
573
|
let ownerPrivateKey = argv.ownerPrivateKey;
|
|
@@ -696,18 +600,18 @@ async function create(argv) {
|
|
|
696
600
|
process.exit(1);
|
|
697
601
|
}
|
|
698
602
|
const imageUrl = argv.imageUrl ?? await promptText("Bot image URL (optional)", true);
|
|
699
|
-
const owner = ownerPrivateKey ? await (0,
|
|
603
|
+
const owner = ownerPrivateKey ? await (0, import_sdk2.makeSignerContextFromViem)(
|
|
700
604
|
(0, import_accounts.privateKeyToAccount)(ownerPrivateKey),
|
|
701
605
|
(0, import_accounts.generatePrivateKey)(),
|
|
702
606
|
{ days: 1 }
|
|
703
|
-
) : await (0,
|
|
704
|
-
const townsConfig = (0,
|
|
607
|
+
) : await (0, import_sdk2.makeSignerContextFromBearerToken)(bearerToken);
|
|
608
|
+
const townsConfig = (0, import_sdk2.townsEnv)().makeTownsConfig();
|
|
705
609
|
const chainId = townsConfig.base.chainConfig.chainId;
|
|
706
610
|
const addresses = (0, import_deployments.getAddressesWithFallback)(townsConfig.environmentId, chainId);
|
|
707
611
|
if (!addresses?.accountProxy) {
|
|
708
612
|
throw new Error(`No accountProxy address found for ${townsConfig.environmentId}/${chainId}`);
|
|
709
613
|
}
|
|
710
|
-
const relayerUrl =
|
|
614
|
+
const relayerUrl = townsConfig.services.relayer.url;
|
|
711
615
|
const relayerClient = (0, import_viem.createPublicClient)({
|
|
712
616
|
chain: {
|
|
713
617
|
id: chainId,
|
|
@@ -717,7 +621,7 @@ async function create(argv) {
|
|
|
717
621
|
},
|
|
718
622
|
transport: (0, import_viem.http)(townsConfig.base.rpcUrl)
|
|
719
623
|
}).extend((0, import_relayer_client.relayerActions)({ relayerUrl }));
|
|
720
|
-
const result = await (0,
|
|
624
|
+
const result = await (0, import_sdk2.createApp)({
|
|
721
625
|
owner,
|
|
722
626
|
metadata: {
|
|
723
627
|
username,
|
|
@@ -749,7 +653,7 @@ async function promptText(message, optional = false) {
|
|
|
749
653
|
var import_prompts4 = __toESM(require("prompts"));
|
|
750
654
|
var import_picocolors6 = require("picocolors");
|
|
751
655
|
var import_accounts2 = require("viem/accounts");
|
|
752
|
-
var
|
|
656
|
+
var import_sdk3 = require("@towns-labs/sdk");
|
|
753
657
|
var import_utils5 = require("@towns-labs/utils");
|
|
754
658
|
var FIELD_DEFS = [
|
|
755
659
|
{ flag: "username", label: "USERNAME", mask: "username", prompt: "Username" },
|
|
@@ -767,21 +671,25 @@ var FIELD_DEFS = [
|
|
|
767
671
|
];
|
|
768
672
|
async function metadata(argv) {
|
|
769
673
|
const subcommand = argv._[1];
|
|
770
|
-
const appAddress = argv._[2];
|
|
674
|
+
const appAddress = resolveAppAddress(argv._[2]);
|
|
771
675
|
if (!subcommand || !["view", "update"].includes(subcommand)) {
|
|
772
|
-
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]"));
|
|
773
677
|
process.exit(1);
|
|
774
678
|
}
|
|
775
679
|
if (!appAddress) {
|
|
776
|
-
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
|
+
);
|
|
777
685
|
process.exit(1);
|
|
778
686
|
}
|
|
779
|
-
const env = (0,
|
|
687
|
+
const env = (0, import_sdk3.townsEnv)();
|
|
780
688
|
const townsConfig = env.makeTownsConfig();
|
|
781
689
|
const appRegistryUrl = env.getAppRegistryUrl(townsConfig.environmentId);
|
|
782
690
|
const appId = (0, import_utils5.bin_fromHexString)(appAddress);
|
|
783
691
|
if (subcommand === "view") {
|
|
784
|
-
const client = (0,
|
|
692
|
+
const client = (0, import_sdk3.makeAppRegistryRpcClient)(appRegistryUrl, "");
|
|
785
693
|
const { metadata: meta } = await client.getAppMetadata({ appId });
|
|
786
694
|
if (!meta) {
|
|
787
695
|
console.error((0, import_picocolors6.red)("No metadata found for this app."));
|
|
@@ -809,12 +717,12 @@ async function metadata(argv) {
|
|
|
809
717
|
bearerToken = auth.value;
|
|
810
718
|
}
|
|
811
719
|
}
|
|
812
|
-
const signerContext = ownerPrivateKey ? await (0,
|
|
720
|
+
const signerContext = ownerPrivateKey ? await (0, import_sdk3.makeSignerContextFromViem)(
|
|
813
721
|
(0, import_accounts2.privateKeyToAccount)(ownerPrivateKey),
|
|
814
722
|
(0, import_accounts2.generatePrivateKey)(),
|
|
815
723
|
{ days: 1 }
|
|
816
|
-
) : await (0,
|
|
817
|
-
const { appRegistryRpcClient } = await
|
|
724
|
+
) : await (0, import_sdk3.makeSignerContextFromBearerToken)(bearerToken);
|
|
725
|
+
const { appRegistryRpcClient } = await import_sdk3.AppRegistryService.authenticate(
|
|
818
726
|
signerContext,
|
|
819
727
|
appRegistryUrl
|
|
820
728
|
);
|
|
@@ -828,7 +736,7 @@ async function metadata(argv) {
|
|
|
828
736
|
}
|
|
829
737
|
}
|
|
830
738
|
} else {
|
|
831
|
-
const unauthClient = (0,
|
|
739
|
+
const unauthClient = (0, import_sdk3.makeAppRegistryRpcClient)(appRegistryUrl, "");
|
|
832
740
|
const { metadata: current } = await unauthClient.getAppMetadata({ appId });
|
|
833
741
|
values = {};
|
|
834
742
|
for (const field of FIELD_DEFS) {
|
|
@@ -876,7 +784,7 @@ async function metadata(argv) {
|
|
|
876
784
|
var import_prompts5 = __toESM(require("prompts"));
|
|
877
785
|
var import_picocolors7 = require("picocolors");
|
|
878
786
|
var import_accounts3 = require("viem/accounts");
|
|
879
|
-
var
|
|
787
|
+
var import_sdk4 = require("@towns-labs/sdk");
|
|
880
788
|
var import_proto = require("@towns-labs/proto");
|
|
881
789
|
var import_utils7 = require("@towns-labs/utils");
|
|
882
790
|
var NOTIFY_MAP = {
|
|
@@ -890,9 +798,13 @@ var NOTIFY_LABELS = {
|
|
|
890
798
|
NONE: "No messages"
|
|
891
799
|
};
|
|
892
800
|
async function setup(argv) {
|
|
893
|
-
const appAddress = argv._[1];
|
|
801
|
+
const appAddress = resolveAppAddress(argv._[1]);
|
|
894
802
|
if (!appAddress) {
|
|
895
|
-
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
|
+
);
|
|
896
808
|
process.exit(1);
|
|
897
809
|
}
|
|
898
810
|
let ownerPrivateKey = argv.ownerPrivateKey;
|
|
@@ -909,11 +821,18 @@ async function setup(argv) {
|
|
|
909
821
|
bearerToken = auth.value;
|
|
910
822
|
}
|
|
911
823
|
}
|
|
912
|
-
|
|
824
|
+
let webhookUrl = argv.webhookUrl ?? await promptWebhookUrl();
|
|
913
825
|
if (!webhookUrl) {
|
|
914
826
|
console.error((0, import_picocolors7.red)("Webhook URL is required."));
|
|
915
827
|
process.exit(1);
|
|
916
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
|
+
}
|
|
917
836
|
let notifyKey;
|
|
918
837
|
if (argv.notify) {
|
|
919
838
|
notifyKey = argv.notify.toUpperCase();
|
|
@@ -929,20 +848,39 @@ async function setup(argv) {
|
|
|
929
848
|
);
|
|
930
849
|
process.exit(1);
|
|
931
850
|
}
|
|
932
|
-
const env = (0,
|
|
851
|
+
const env = (0, import_sdk4.townsEnv)();
|
|
933
852
|
const townsConfig = env.makeTownsConfig();
|
|
934
853
|
const appRegistryUrl = env.getAppRegistryUrl(townsConfig.environmentId);
|
|
935
854
|
const appId = (0, import_utils7.bin_fromHexString)(appAddress);
|
|
936
|
-
const signerContext = ownerPrivateKey ? await (0,
|
|
855
|
+
const signerContext = ownerPrivateKey ? await (0, import_sdk4.makeSignerContextFromViem)(
|
|
937
856
|
(0, import_accounts3.privateKeyToAccount)(ownerPrivateKey),
|
|
938
857
|
(0, import_accounts3.generatePrivateKey)(),
|
|
939
858
|
{ days: 1 }
|
|
940
|
-
) : await (0,
|
|
941
|
-
const { appRegistryRpcClient } = await
|
|
859
|
+
) : await (0, import_sdk4.makeSignerContextFromBearerToken)(bearerToken);
|
|
860
|
+
const { appRegistryRpcClient } = await import_sdk4.AppRegistryService.authenticate(
|
|
942
861
|
signerContext,
|
|
943
862
|
appRegistryUrl
|
|
944
863
|
);
|
|
945
|
-
|
|
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
|
+
}
|
|
946
884
|
await appRegistryRpcClient.setAppSettings({
|
|
947
885
|
appId,
|
|
948
886
|
settings: { forwardSetting }
|
|
@@ -955,12 +893,48 @@ async function setup(argv) {
|
|
|
955
893
|
console.log();
|
|
956
894
|
process.exit(0);
|
|
957
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
|
+
}
|
|
958
932
|
async function promptWebhookUrl() {
|
|
959
933
|
const { value } = await (0, import_prompts5.default)({
|
|
960
934
|
type: "text",
|
|
961
935
|
name: "value",
|
|
962
936
|
message: "Webhook URL",
|
|
963
|
-
validate:
|
|
937
|
+
validate: validateWebhookUrl
|
|
964
938
|
});
|
|
965
939
|
return value;
|
|
966
940
|
}
|
|
@@ -982,6 +956,9 @@ async function promptNotify() {
|
|
|
982
956
|
return value;
|
|
983
957
|
}
|
|
984
958
|
|
|
959
|
+
// src/index.ts
|
|
960
|
+
var import_sdk5 = require("@towns-labs/sdk");
|
|
961
|
+
|
|
985
962
|
// src/parser.ts
|
|
986
963
|
var import_minimist = __toESM(require("minimist"));
|
|
987
964
|
var COMMAND_CONFIGS = {
|
|
@@ -1050,6 +1027,7 @@ var COMMAND_CONFIGS = {
|
|
|
1050
1027
|
function parseArgs(args) {
|
|
1051
1028
|
const initial = (0, import_minimist.default)(args, {
|
|
1052
1029
|
stopEarly: true,
|
|
1030
|
+
string: ["env"],
|
|
1053
1031
|
boolean: ["help"],
|
|
1054
1032
|
alias: { h: "help" }
|
|
1055
1033
|
});
|
|
@@ -1059,8 +1037,10 @@ function parseArgs(args) {
|
|
|
1059
1037
|
}
|
|
1060
1038
|
const commandConfig = COMMAND_CONFIGS[command] || {};
|
|
1061
1039
|
const booleanOptions = Array.isArray(commandConfig.boolean) ? ["help", ...commandConfig.boolean] : ["help"];
|
|
1040
|
+
const stringOptions = Array.isArray(commandConfig.string) ? ["env", ...commandConfig.string] : ["env"];
|
|
1062
1041
|
const parsed = (0, import_minimist.default)(args, {
|
|
1063
1042
|
...commandConfig,
|
|
1043
|
+
string: stringOptions,
|
|
1064
1044
|
boolean: booleanOptions,
|
|
1065
1045
|
alias: {
|
|
1066
1046
|
...commandConfig.alias,
|
|
@@ -1089,8 +1069,6 @@ function isSetupArgs(args) {
|
|
|
1089
1069
|
}
|
|
1090
1070
|
|
|
1091
1071
|
// 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
1072
|
async function main() {
|
|
1095
1073
|
const args = parseArgs(process.argv.slice(2));
|
|
1096
1074
|
const command = args._[0];
|
|
@@ -1098,6 +1076,32 @@ async function main() {
|
|
|
1098
1076
|
showHelp();
|
|
1099
1077
|
return;
|
|
1100
1078
|
}
|
|
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
|
+
}
|
|
1104
|
+
}
|
|
1101
1105
|
try {
|
|
1102
1106
|
switch (command) {
|
|
1103
1107
|
case "init":
|
|
@@ -1150,8 +1154,8 @@ ${(0, import_picocolors8.yellow)("Usage:")}
|
|
|
1150
1154
|
${(0, import_picocolors8.yellow)("Commands:")}
|
|
1151
1155
|
${(0, import_picocolors8.green)("init")} [project-name] Create a new agent project
|
|
1152
1156
|
${(0, import_picocolors8.green)("create")} Create a new bot/app account interactively
|
|
1153
|
-
${(0, import_picocolors8.green)("setup")}
|
|
1154
|
-
${(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
|
|
1155
1159
|
${(0, import_picocolors8.green)("update")} Update @towns-labs dependencies and skills
|
|
1156
1160
|
${(0, import_picocolors8.green)("install-skill")} Install Towns Agent Skills to current project
|
|
1157
1161
|
|
|
@@ -1171,6 +1175,9 @@ ${(0, import_picocolors8.yellow)("Update Commands Options:")}
|
|
|
1171
1175
|
-e, --envFile <path> Path to .env file (default: .env)
|
|
1172
1176
|
--skip-agents-md Skip updating AGENTS.md file
|
|
1173
1177
|
|
|
1178
|
+
${(0, import_picocolors8.yellow)("Environment Options (create, setup, metadata):")}
|
|
1179
|
+
--env <name> Environment to use: local_dev, stage, prod
|
|
1180
|
+
|
|
1174
1181
|
${(0, import_picocolors8.yellow)("Global Options:")}
|
|
1175
1182
|
-h, --help Show this help message
|
|
1176
1183
|
|