struere 0.12.1 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/struere.js +530 -19
- package/dist/cli/commands/add.d.ts.map +1 -1
- package/dist/cli/commands/chat.d.ts.map +1 -1
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/pull.d.ts.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/whatsapp.d.ts +3 -0
- package/dist/cli/commands/whatsapp.d.ts.map +1 -0
- package/dist/cli/index.js +530 -19
- package/dist/cli/templates/index.d.ts +1 -0
- package/dist/cli/templates/index.d.ts.map +1 -1
- package/dist/cli/utils/convex.d.ts +78 -0
- package/dist/cli/utils/convex.d.ts.map +1 -1
- package/dist/cli/utils/extractor.d.ts +27 -0
- package/dist/cli/utils/extractor.d.ts.map +1 -1
- package/dist/cli/utils/generator.d.ts +3 -2
- package/dist/cli/utils/generator.d.ts.map +1 -1
- package/dist/cli/utils/loader.d.ts +3 -1
- package/dist/cli/utils/loader.d.ts.map +1 -1
- package/dist/cli/utils/scaffold.d.ts +1 -0
- package/dist/cli/utils/scaffold.d.ts.map +1 -1
- package/dist/cli/utils/whatsapp.d.ts +20 -0
- package/dist/cli/utils/whatsapp.d.ts.map +1 -1
- package/dist/define/index.d.ts +1 -0
- package/dist/define/index.d.ts.map +1 -1
- package/dist/define/router.d.ts +3 -0
- package/dist/define/router.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -0
- package/dist/types.d.ts +31 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/bin/struere.js
CHANGED
|
@@ -35,6 +35,7 @@ __export(exports_convex, {
|
|
|
35
35
|
getPullState: () => getPullState,
|
|
36
36
|
createOrganization: () => createOrganization,
|
|
37
37
|
compilePrompt: () => compilePrompt,
|
|
38
|
+
chatWithRouter: () => chatWithRouter,
|
|
38
39
|
chatWithAgent: () => chatWithAgent
|
|
39
40
|
});
|
|
40
41
|
async function refreshToken() {
|
|
@@ -184,6 +185,7 @@ async function syncViaHttp(apiKey, payload) {
|
|
|
184
185
|
roles: payload.roles,
|
|
185
186
|
evalSuites: payload.evalSuites,
|
|
186
187
|
triggers: payload.triggers,
|
|
188
|
+
routers: payload.routers,
|
|
187
189
|
fixtures: payload.fixtures
|
|
188
190
|
}),
|
|
189
191
|
signal: AbortSignal.timeout(30000)
|
|
@@ -635,6 +637,65 @@ async function chatWithAgent(options) {
|
|
|
635
637
|
return { error: `Network error: ${err instanceof Error ? err.message : String(err)}` };
|
|
636
638
|
}
|
|
637
639
|
}
|
|
640
|
+
async function chatWithRouter(options) {
|
|
641
|
+
const credentials = loadCredentials();
|
|
642
|
+
const apiKey = getApiKey();
|
|
643
|
+
if (credentials?.sessionId) {
|
|
644
|
+
await refreshToken();
|
|
645
|
+
}
|
|
646
|
+
const freshCredentials = loadCredentials();
|
|
647
|
+
const token = apiKey || freshCredentials?.token;
|
|
648
|
+
if (!token) {
|
|
649
|
+
return { error: "Not authenticated" };
|
|
650
|
+
}
|
|
651
|
+
try {
|
|
652
|
+
const response = await fetch(`${CONVEX_URL}/api/action`, {
|
|
653
|
+
method: "POST",
|
|
654
|
+
headers: {
|
|
655
|
+
"Content-Type": "application/json",
|
|
656
|
+
Authorization: `Bearer ${token}`
|
|
657
|
+
},
|
|
658
|
+
body: JSON.stringify({
|
|
659
|
+
path: "chat:sendByRouterSlug",
|
|
660
|
+
args: {
|
|
661
|
+
routerSlug: options.routerSlug,
|
|
662
|
+
message: options.message,
|
|
663
|
+
threadId: options.threadId,
|
|
664
|
+
phoneNumber: options.phoneNumber,
|
|
665
|
+
environment: options.environment,
|
|
666
|
+
channel: options.channel || "api"
|
|
667
|
+
}
|
|
668
|
+
}),
|
|
669
|
+
signal: options.signal || AbortSignal.timeout(120000)
|
|
670
|
+
});
|
|
671
|
+
const text = await response.text();
|
|
672
|
+
let json;
|
|
673
|
+
try {
|
|
674
|
+
json = JSON.parse(text);
|
|
675
|
+
} catch {
|
|
676
|
+
return { error: text || `HTTP ${response.status}` };
|
|
677
|
+
}
|
|
678
|
+
if (!response.ok) {
|
|
679
|
+
const msg = json.errorData?.message || json.errorMessage || text;
|
|
680
|
+
return { error: msg };
|
|
681
|
+
}
|
|
682
|
+
if (json.status === "success" && json.value) {
|
|
683
|
+
return { result: json.value };
|
|
684
|
+
}
|
|
685
|
+
if (json.status === "success" && json.value === null) {
|
|
686
|
+
return { error: `Router not found or no config for environment: ${options.environment}` };
|
|
687
|
+
}
|
|
688
|
+
if (json.status === "error") {
|
|
689
|
+
return { error: json.errorData?.message || json.errorMessage || "Unknown error from Convex" };
|
|
690
|
+
}
|
|
691
|
+
return { error: `Unexpected response: ${text}` };
|
|
692
|
+
} catch (err) {
|
|
693
|
+
if (err instanceof DOMException && err.name === "TimeoutError") {
|
|
694
|
+
return { error: "Request timed out after 120s" };
|
|
695
|
+
}
|
|
696
|
+
return { error: `Network error: ${err instanceof Error ? err.message : String(err)}` };
|
|
697
|
+
}
|
|
698
|
+
}
|
|
638
699
|
async function getPullState(organizationId, environment = "development") {
|
|
639
700
|
const credentials = loadCredentials();
|
|
640
701
|
const apiKey = getApiKey();
|
|
@@ -1343,6 +1404,26 @@ export default defineTrigger({
|
|
|
1343
1404
|
})
|
|
1344
1405
|
`;
|
|
1345
1406
|
}
|
|
1407
|
+
function getRouterTs(name, slug) {
|
|
1408
|
+
return `import { defineRouter } from 'struere'
|
|
1409
|
+
|
|
1410
|
+
export default defineRouter({
|
|
1411
|
+
name: "${name}",
|
|
1412
|
+
slug: "${slug}",
|
|
1413
|
+
mode: "rules",
|
|
1414
|
+
agents: [
|
|
1415
|
+
{ slug: "agent-one", description: "Handles ..." },
|
|
1416
|
+
],
|
|
1417
|
+
rules: [
|
|
1418
|
+
{
|
|
1419
|
+
conditions: [{ field: "channel", operator: "eq", value: "whatsapp" }],
|
|
1420
|
+
route: "agent-one",
|
|
1421
|
+
},
|
|
1422
|
+
],
|
|
1423
|
+
fallback: "agent-one",
|
|
1424
|
+
})
|
|
1425
|
+
`;
|
|
1426
|
+
}
|
|
1346
1427
|
|
|
1347
1428
|
// src/cli/utils/scaffold.ts
|
|
1348
1429
|
function ensureDir(filePath) {
|
|
@@ -1368,6 +1449,7 @@ function scaffoldProject(cwd, options) {
|
|
|
1368
1449
|
"tools",
|
|
1369
1450
|
"evals",
|
|
1370
1451
|
"triggers",
|
|
1452
|
+
"routers",
|
|
1371
1453
|
"fixtures",
|
|
1372
1454
|
".struere"
|
|
1373
1455
|
];
|
|
@@ -1487,6 +1569,24 @@ function scaffoldTrigger(cwd, name, slug) {
|
|
|
1487
1569
|
result.createdFiles.push(`triggers/${fileName}`);
|
|
1488
1570
|
return result;
|
|
1489
1571
|
}
|
|
1572
|
+
function scaffoldRouter(cwd, name, slug) {
|
|
1573
|
+
const result = {
|
|
1574
|
+
createdFiles: [],
|
|
1575
|
+
updatedFiles: []
|
|
1576
|
+
};
|
|
1577
|
+
const routersDir = join3(cwd, "routers");
|
|
1578
|
+
if (!existsSync3(routersDir)) {
|
|
1579
|
+
mkdirSync2(routersDir, { recursive: true });
|
|
1580
|
+
}
|
|
1581
|
+
const fileName = `${slug}.ts`;
|
|
1582
|
+
const filePath = join3(routersDir, fileName);
|
|
1583
|
+
if (existsSync3(filePath)) {
|
|
1584
|
+
return result;
|
|
1585
|
+
}
|
|
1586
|
+
writeFileSync2(filePath, getRouterTs(name, slug));
|
|
1587
|
+
result.createdFiles.push(`routers/${fileName}`);
|
|
1588
|
+
return result;
|
|
1589
|
+
}
|
|
1490
1590
|
function scaffoldFixture(cwd, name, slug) {
|
|
1491
1591
|
const result = {
|
|
1492
1592
|
createdFiles: [],
|
|
@@ -1916,9 +2016,10 @@ async function loadAllResources(cwd) {
|
|
|
1916
2016
|
const { suites: evalSuites, errors: evalErrors } = loadEvalSuites(join5(cwd, "evals"));
|
|
1917
2017
|
errors.push(...evalErrors);
|
|
1918
2018
|
const triggers = await loadTsDirectory(join5(cwd, "triggers"));
|
|
2019
|
+
const routers = await loadTsDirectory(join5(cwd, "routers"));
|
|
1919
2020
|
const { fixtures, errors: fixtureErrors } = loadFixtures(join5(cwd, "fixtures"));
|
|
1920
2021
|
errors.push(...fixtureErrors);
|
|
1921
|
-
return { agents, entityTypes, roles, customTools, evalSuites, triggers, fixtures, errors };
|
|
2022
|
+
return { agents, entityTypes, roles, customTools, evalSuites, triggers, routers, fixtures, errors };
|
|
1922
2023
|
}
|
|
1923
2024
|
async function loadTsDirectory(dir) {
|
|
1924
2025
|
if (!existsSync5(dir)) {
|
|
@@ -2005,6 +2106,7 @@ function getResourceDirectories(cwd) {
|
|
|
2005
2106
|
tools: join5(cwd, "tools"),
|
|
2006
2107
|
evals: join5(cwd, "evals"),
|
|
2007
2108
|
triggers: join5(cwd, "triggers"),
|
|
2109
|
+
routers: join5(cwd, "routers"),
|
|
2008
2110
|
fixtures: join5(cwd, "fixtures")
|
|
2009
2111
|
};
|
|
2010
2112
|
}
|
|
@@ -2143,6 +2245,7 @@ async function generateDocs(cwd, targets) {
|
|
|
2143
2245
|
customTools: [],
|
|
2144
2246
|
evalSuites: [],
|
|
2145
2247
|
triggers: [],
|
|
2248
|
+
routers: [],
|
|
2146
2249
|
fixtures: [],
|
|
2147
2250
|
errors: []
|
|
2148
2251
|
});
|
|
@@ -2687,6 +2790,29 @@ function extractSyncPayload(resources) {
|
|
|
2687
2790
|
schedule: t.schedule,
|
|
2688
2791
|
retry: t.retry
|
|
2689
2792
|
})) : undefined;
|
|
2793
|
+
const routers = resources.routers.length > 0 ? resources.routers.map((r) => ({
|
|
2794
|
+
name: r.name,
|
|
2795
|
+
slug: r.slug,
|
|
2796
|
+
description: r.description,
|
|
2797
|
+
mode: r.mode,
|
|
2798
|
+
agents: r.agents.map((a) => ({
|
|
2799
|
+
slug: a.slug,
|
|
2800
|
+
description: a.description
|
|
2801
|
+
})),
|
|
2802
|
+
rules: r.rules?.map((rule) => ({
|
|
2803
|
+
conditions: rule.conditions.map((c) => ({
|
|
2804
|
+
field: c.field,
|
|
2805
|
+
operator: c.operator,
|
|
2806
|
+
value: c.value
|
|
2807
|
+
})),
|
|
2808
|
+
route: rule.route
|
|
2809
|
+
})),
|
|
2810
|
+
fallback: r.fallback,
|
|
2811
|
+
classifyModel: r.classifyModel,
|
|
2812
|
+
contextMessages: r.contextMessages,
|
|
2813
|
+
maxTransfers: r.maxTransfers,
|
|
2814
|
+
inactivityResetMs: r.inactivityResetMs
|
|
2815
|
+
})) : undefined;
|
|
2690
2816
|
const fixtures = resources.fixtures.length > 0 ? resources.fixtures.map((f) => ({
|
|
2691
2817
|
name: f.name,
|
|
2692
2818
|
slug: f.slug,
|
|
@@ -2703,7 +2829,7 @@ function extractSyncPayload(resources) {
|
|
|
2703
2829
|
metadata: r.metadata
|
|
2704
2830
|
}))
|
|
2705
2831
|
})) : undefined;
|
|
2706
|
-
return { agents, entityTypes, roles, evalSuites, triggers, fixtures };
|
|
2832
|
+
return { agents, entityTypes, roles, evalSuites, triggers, routers, fixtures };
|
|
2707
2833
|
}
|
|
2708
2834
|
function extractAgentPayload(agent, customToolsMap) {
|
|
2709
2835
|
let systemPrompt;
|
|
@@ -3132,6 +3258,7 @@ ${resources.errors.join(`
|
|
|
3132
3258
|
entityTypes: payload.entityTypes,
|
|
3133
3259
|
roles: payload.roles,
|
|
3134
3260
|
triggers: payload.triggers,
|
|
3261
|
+
routers: payload.routers,
|
|
3135
3262
|
organizationId,
|
|
3136
3263
|
environment: "development"
|
|
3137
3264
|
});
|
|
@@ -3165,7 +3292,8 @@ async function checkForDeletions(resources, organizationId, environment) {
|
|
|
3165
3292
|
entityTypes: new Set(payload.entityTypes.map((et) => et.slug)),
|
|
3166
3293
|
roles: new Set(payload.roles.map((r) => r.name)),
|
|
3167
3294
|
evalSuites: new Set((payload.evalSuites || []).map((es) => es.slug)),
|
|
3168
|
-
triggers: new Set((payload.triggers || []).map((t) => t.slug))
|
|
3295
|
+
triggers: new Set((payload.triggers || []).map((t) => t.slug)),
|
|
3296
|
+
routers: new Set((payload.routers || []).map((r) => r.slug))
|
|
3169
3297
|
};
|
|
3170
3298
|
const deletions = [];
|
|
3171
3299
|
const deletedAgents = remoteState.agents.filter((a) => !localSlugs.agents.has(a.slug)).map((a) => a.name);
|
|
@@ -3185,6 +3313,10 @@ async function checkForDeletions(resources, organizationId, environment) {
|
|
|
3185
3313
|
const deletedTriggers = remoteTriggers.filter((t) => !localSlugs.triggers.has(t.slug)).map((t) => t.name);
|
|
3186
3314
|
if (deletedTriggers.length > 0)
|
|
3187
3315
|
deletions.push({ type: "Triggers", remote: remoteTriggers.length, local: (payload.triggers || []).length, deleted: deletedTriggers });
|
|
3316
|
+
const remoteRouters = remoteState.routers || [];
|
|
3317
|
+
const deletedRouters = remoteRouters.filter((r) => !localSlugs.routers.has(r.slug)).map((r) => r.name);
|
|
3318
|
+
if (deletedRouters.length > 0)
|
|
3319
|
+
deletions.push({ type: "Routers", remote: remoteRouters.length, local: (payload.routers || []).length, deleted: deletedRouters });
|
|
3188
3320
|
return deletions;
|
|
3189
3321
|
}
|
|
3190
3322
|
async function syncToEnvironment(cwd, organizationId, environment) {
|
|
@@ -3304,6 +3436,7 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
|
|
|
3304
3436
|
entityTypes: payload.entityTypes.map((et) => et.slug),
|
|
3305
3437
|
roles: payload.roles.map((r) => r.name),
|
|
3306
3438
|
triggers: (payload.triggers || []).map((t) => t.slug),
|
|
3439
|
+
routers: (payload.routers || []).map((r) => r.slug),
|
|
3307
3440
|
deletions: deletions.map((d) => ({ type: d.type, names: d.deleted }))
|
|
3308
3441
|
}));
|
|
3309
3442
|
} else {
|
|
@@ -3313,6 +3446,7 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
|
|
|
3313
3446
|
console.log(chalk6.gray(" Data types:"), payload.entityTypes.map((et) => et.slug).join(", ") || "none");
|
|
3314
3447
|
console.log(chalk6.gray(" Roles:"), payload.roles.map((r) => r.name).join(", ") || "none");
|
|
3315
3448
|
console.log(chalk6.gray(" Triggers:"), (payload.triggers || []).map((t) => t.slug).join(", ") || "none");
|
|
3449
|
+
console.log(chalk6.gray(" Routers:"), (payload.routers || []).map((r) => r.slug).join(", ") || "none");
|
|
3316
3450
|
if (deletions.length > 0) {
|
|
3317
3451
|
console.log();
|
|
3318
3452
|
console.log(chalk6.yellow.bold(" Would delete:"));
|
|
@@ -3477,7 +3611,7 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
|
|
|
3477
3611
|
spinner.start("Loading resources");
|
|
3478
3612
|
try {
|
|
3479
3613
|
loadedResources = await loadAllResources(cwd);
|
|
3480
|
-
spinner.succeed(`Loaded ${loadedResources.agents.length} agents, ${loadedResources.entityTypes.length} data types, ${loadedResources.roles.length} roles, ${loadedResources.customTools.length} custom tools, ${loadedResources.evalSuites.length} eval suites, ${loadedResources.triggers.length} triggers, ${loadedResources.fixtures.length} fixtures`);
|
|
3614
|
+
spinner.succeed(`Loaded ${loadedResources.agents.length} agents, ${loadedResources.entityTypes.length} data types, ${loadedResources.roles.length} roles, ${loadedResources.customTools.length} custom tools, ${loadedResources.evalSuites.length} eval suites, ${loadedResources.triggers.length} triggers, ${loadedResources.routers.length} routers, ${loadedResources.fixtures.length} fixtures`);
|
|
3481
3615
|
for (const err of loadedResources.errors) {
|
|
3482
3616
|
console.log(chalk7.red(" \u2716"), err);
|
|
3483
3617
|
}
|
|
@@ -3572,6 +3706,7 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
|
|
|
3572
3706
|
dirs.tools,
|
|
3573
3707
|
dirs.evals,
|
|
3574
3708
|
dirs.triggers,
|
|
3709
|
+
dirs.routers,
|
|
3575
3710
|
dirs.fixtures
|
|
3576
3711
|
].filter((p) => existsSync8(p));
|
|
3577
3712
|
const watcher = chokidar.watch(watchPaths, {
|
|
@@ -4069,7 +4204,7 @@ var whoamiCommand = new Command9("whoami").description("Show current logged in u
|
|
|
4069
4204
|
// src/cli/commands/add.ts
|
|
4070
4205
|
import { Command as Command10 } from "commander";
|
|
4071
4206
|
import chalk11 from "chalk";
|
|
4072
|
-
var addCommand = new Command10("add").description("Scaffold a new resource").argument("<type>", "Resource type: agent, data-type, role, eval, trigger, or fixture").argument("<name>", "Resource name").action(async (type, name) => {
|
|
4207
|
+
var addCommand = new Command10("add").description("Scaffold a new resource").argument("<type>", "Resource type: agent, data-type, role, eval, trigger, router, or fixture").argument("<name>", "Resource name").action(async (type, name) => {
|
|
4073
4208
|
const cwd = process.cwd();
|
|
4074
4209
|
console.log();
|
|
4075
4210
|
if (!hasProject(cwd)) {
|
|
@@ -4147,6 +4282,17 @@ var addCommand = new Command10("add").description("Scaffold a new resource").arg
|
|
|
4147
4282
|
console.log(chalk11.yellow("Trigger already exists:"), `triggers/${slug}.ts`);
|
|
4148
4283
|
}
|
|
4149
4284
|
break;
|
|
4285
|
+
case "router":
|
|
4286
|
+
result = scaffoldRouter(cwd, displayName, slug);
|
|
4287
|
+
if (result.createdFiles.length > 0) {
|
|
4288
|
+
console.log(chalk11.green("\u2713"), `Created router "${displayName}"`);
|
|
4289
|
+
for (const file of result.createdFiles) {
|
|
4290
|
+
console.log(chalk11.gray(" \u2192"), file);
|
|
4291
|
+
}
|
|
4292
|
+
} else {
|
|
4293
|
+
console.log(chalk11.yellow("Router already exists:"), `routers/${slug}.ts`);
|
|
4294
|
+
}
|
|
4295
|
+
break;
|
|
4150
4296
|
case "fixture":
|
|
4151
4297
|
result = scaffoldFixture(cwd, displayName, slug);
|
|
4152
4298
|
if (result.createdFiles.length > 0) {
|
|
@@ -4169,6 +4315,7 @@ var addCommand = new Command10("add").description("Scaffold a new resource").arg
|
|
|
4169
4315
|
console.log(chalk11.gray(" -"), chalk11.cyan("role"), "- Create a role with permissions");
|
|
4170
4316
|
console.log(chalk11.gray(" -"), chalk11.cyan("eval"), "- Create an eval suite (YAML)");
|
|
4171
4317
|
console.log(chalk11.gray(" -"), chalk11.cyan("trigger"), "- Create a data trigger");
|
|
4318
|
+
console.log(chalk11.gray(" -"), chalk11.cyan("router"), "- Create a message router");
|
|
4172
4319
|
console.log(chalk11.gray(" -"), chalk11.cyan("fixture"), "- Create a test data fixture (YAML)");
|
|
4173
4320
|
console.log();
|
|
4174
4321
|
process.exit(1);
|
|
@@ -4298,6 +4445,8 @@ var statusCommand = new Command11("status").description("Compare local vs remote
|
|
|
4298
4445
|
const devEntityTypeSlugs = new Set(devState.entityTypes.map((et) => et.slug));
|
|
4299
4446
|
const localRoleNames = new Set(localResources.roles.map((r) => r.name));
|
|
4300
4447
|
const devRoleNames = new Set(devState.roles.map((r) => r.name));
|
|
4448
|
+
const localRouterSlugs = new Set(localResources.routers.map((r) => r.slug));
|
|
4449
|
+
const devRouterSlugs = new Set((devState.routers || []).map((r) => r.slug));
|
|
4301
4450
|
if (opts.json) {
|
|
4302
4451
|
const classify = (localItems, remoteItems, useSlug) => {
|
|
4303
4452
|
const localKeys = new Set(localItems.map((i) => useSlug ? i.slug : i.name));
|
|
@@ -4311,7 +4460,8 @@ var statusCommand = new Command11("status").description("Compare local vs remote
|
|
|
4311
4460
|
console.log(JSON.stringify({
|
|
4312
4461
|
agents: classify(localResources.agents, devState.agents, true),
|
|
4313
4462
|
entityTypes: classify(localResources.entityTypes, devState.entityTypes, true),
|
|
4314
|
-
roles: classify(localResources.roles, devState.roles, false)
|
|
4463
|
+
roles: classify(localResources.roles, devState.roles, false),
|
|
4464
|
+
routers: classify(localResources.routers, devState.routers || [], true)
|
|
4315
4465
|
}));
|
|
4316
4466
|
return;
|
|
4317
4467
|
}
|
|
@@ -4381,6 +4531,26 @@ var statusCommand = new Command11("status").description("Compare local vs remote
|
|
|
4381
4531
|
}
|
|
4382
4532
|
}
|
|
4383
4533
|
console.log();
|
|
4534
|
+
console.log(chalk12.bold("Routers"));
|
|
4535
|
+
console.log(chalk12.gray("\u2500".repeat(60)));
|
|
4536
|
+
if (localResources.routers.length === 0 && (devState.routers || []).length === 0) {
|
|
4537
|
+
console.log(chalk12.gray(" No routers"));
|
|
4538
|
+
} else {
|
|
4539
|
+
for (const router of localResources.routers) {
|
|
4540
|
+
const remote = (devState.routers || []).find((r) => r.slug === router.slug);
|
|
4541
|
+
if (remote) {
|
|
4542
|
+
console.log(` ${chalk12.green("\u25CF")} ${chalk12.cyan(router.name)} (${router.slug}) - ${router.mode}`);
|
|
4543
|
+
} else {
|
|
4544
|
+
console.log(` ${chalk12.blue("+")} ${chalk12.cyan(router.name)} (${router.slug}) - ${chalk12.blue("new")}`);
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
for (const remote of devState.routers || []) {
|
|
4548
|
+
if (!localRouterSlugs.has(remote.slug)) {
|
|
4549
|
+
console.log(` ${chalk12.red("-")} ${remote.name} (${remote.slug}) - ${chalk12.red("will be deleted")}`);
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4552
|
+
}
|
|
4553
|
+
console.log();
|
|
4384
4554
|
console.log(chalk12.gray("Legend:"));
|
|
4385
4555
|
console.log(chalk12.gray(" "), chalk12.green("\u25CF"), "Synced", chalk12.yellow("\u25CB"), "Not in production", chalk12.blue("+"), "New", chalk12.red("-"), "Will be deleted");
|
|
4386
4556
|
console.log();
|
|
@@ -4655,6 +4825,53 @@ ${parts.join(`,
|
|
|
4655
4825
|
})
|
|
4656
4826
|
`;
|
|
4657
4827
|
}
|
|
4828
|
+
function generateRouterFile(router) {
|
|
4829
|
+
const agentLines = router.agents.map((a) => {
|
|
4830
|
+
const aParts = [
|
|
4831
|
+
` slug: "${a.slug}"`,
|
|
4832
|
+
` description: "${a.description}"`
|
|
4833
|
+
];
|
|
4834
|
+
return ` {
|
|
4835
|
+
${aParts.join(`,
|
|
4836
|
+
`)},
|
|
4837
|
+
}`;
|
|
4838
|
+
});
|
|
4839
|
+
const parts = [
|
|
4840
|
+
` name: "${router.name}"`,
|
|
4841
|
+
` slug: "${router.slug}"`
|
|
4842
|
+
];
|
|
4843
|
+
if (router.description) {
|
|
4844
|
+
parts.push(` description: "${router.description}"`);
|
|
4845
|
+
}
|
|
4846
|
+
parts.push(` mode: "${router.mode}"`);
|
|
4847
|
+
parts.push(` agents: [
|
|
4848
|
+
${agentLines.join(`,
|
|
4849
|
+
`)},
|
|
4850
|
+
]`);
|
|
4851
|
+
if (router.rules && router.rules.length > 0) {
|
|
4852
|
+
parts.push(` rules: ${stringifyValue(router.rules, 2)}`);
|
|
4853
|
+
}
|
|
4854
|
+
parts.push(` fallback: "${router.fallback}"`);
|
|
4855
|
+
if (router.classifyModel) {
|
|
4856
|
+
parts.push(` classifyModel: ${stringifyValue(router.classifyModel, 2)}`);
|
|
4857
|
+
}
|
|
4858
|
+
if (router.contextMessages !== undefined) {
|
|
4859
|
+
parts.push(` contextMessages: ${router.contextMessages}`);
|
|
4860
|
+
}
|
|
4861
|
+
if (router.maxTransfers !== undefined) {
|
|
4862
|
+
parts.push(` maxTransfers: ${router.maxTransfers}`);
|
|
4863
|
+
}
|
|
4864
|
+
if (router.inactivityResetMs !== undefined) {
|
|
4865
|
+
parts.push(` inactivityResetMs: ${router.inactivityResetMs}`);
|
|
4866
|
+
}
|
|
4867
|
+
return `import { defineRouter } from 'struere'
|
|
4868
|
+
|
|
4869
|
+
export default defineRouter({
|
|
4870
|
+
${parts.join(`,
|
|
4871
|
+
`)},
|
|
4872
|
+
})
|
|
4873
|
+
`;
|
|
4874
|
+
}
|
|
4658
4875
|
function generateIndexFile(type, slugs) {
|
|
4659
4876
|
if (slugs.length === 0) {
|
|
4660
4877
|
return "";
|
|
@@ -4789,6 +5006,7 @@ var pullCommand = new Command12("pull").description("Pull remote resources to lo
|
|
|
4789
5006
|
ensureDir2(join9(cwd, "entity-types"));
|
|
4790
5007
|
ensureDir2(join9(cwd, "roles"));
|
|
4791
5008
|
ensureDir2(join9(cwd, "triggers"));
|
|
5009
|
+
ensureDir2(join9(cwd, "routers"));
|
|
4792
5010
|
ensureDir2(join9(cwd, "tools"));
|
|
4793
5011
|
const agentSlugs = [];
|
|
4794
5012
|
for (const agent of state.agents) {
|
|
@@ -4816,6 +5034,12 @@ var pullCommand = new Command12("pull").description("Pull remote resources to lo
|
|
|
4816
5034
|
const content = generateTriggerFile(trigger);
|
|
4817
5035
|
writeOrSkip(`triggers/${trigger.slug}.ts`, content);
|
|
4818
5036
|
}
|
|
5037
|
+
const routerSlugs = [];
|
|
5038
|
+
for (const router of state.routers || []) {
|
|
5039
|
+
routerSlugs.push(router.slug);
|
|
5040
|
+
const content = generateRouterFile(router);
|
|
5041
|
+
writeOrSkip(`routers/${router.slug}.ts`, content);
|
|
5042
|
+
}
|
|
4819
5043
|
const customTools = collectCustomTools(state.agents);
|
|
4820
5044
|
if (customTools.length > 0) {
|
|
4821
5045
|
const content = generateToolsFile(customTools);
|
|
@@ -4841,6 +5065,11 @@ var pullCommand = new Command12("pull").description("Pull remote resources to lo
|
|
|
4841
5065
|
if (content)
|
|
4842
5066
|
writeOrSkip("triggers/index.ts", content);
|
|
4843
5067
|
}
|
|
5068
|
+
if (routerSlugs.length > 0) {
|
|
5069
|
+
const content = generateIndexFile("routers", routerSlugs);
|
|
5070
|
+
if (content)
|
|
5071
|
+
writeOrSkip("routers/index.ts", content);
|
|
5072
|
+
}
|
|
4844
5073
|
await installSkill(cwd);
|
|
4845
5074
|
if (options.json) {
|
|
4846
5075
|
console.log(JSON.stringify({
|
|
@@ -6418,6 +6647,53 @@ async function getTemplateStatus(connectionId, name) {
|
|
|
6418
6647
|
name
|
|
6419
6648
|
});
|
|
6420
6649
|
}
|
|
6650
|
+
async function convexMutation3(path, args) {
|
|
6651
|
+
const token = getToken2();
|
|
6652
|
+
if (!token)
|
|
6653
|
+
return { error: "Not authenticated" };
|
|
6654
|
+
const response = await fetch(`${CONVEX_URL}/api/mutation`, {
|
|
6655
|
+
method: "POST",
|
|
6656
|
+
headers: {
|
|
6657
|
+
"Content-Type": "application/json",
|
|
6658
|
+
Authorization: `Bearer ${token}`
|
|
6659
|
+
},
|
|
6660
|
+
body: JSON.stringify({ path, args })
|
|
6661
|
+
});
|
|
6662
|
+
const text = await response.text();
|
|
6663
|
+
let json;
|
|
6664
|
+
try {
|
|
6665
|
+
json = JSON.parse(text);
|
|
6666
|
+
} catch {
|
|
6667
|
+
return { error: text || `HTTP ${response.status}` };
|
|
6668
|
+
}
|
|
6669
|
+
if (!response.ok) {
|
|
6670
|
+
const msg = json.errorData?.message || json.message || json.errorMessage || text;
|
|
6671
|
+
return { error: String(msg) };
|
|
6672
|
+
}
|
|
6673
|
+
if (json.status === "success")
|
|
6674
|
+
return { data: json.value };
|
|
6675
|
+
if (json.status === "error")
|
|
6676
|
+
return { error: String(json.errorMessage || "Unknown error") };
|
|
6677
|
+
return { error: `Unexpected response: ${text}` };
|
|
6678
|
+
}
|
|
6679
|
+
async function listRouters(env) {
|
|
6680
|
+
return convexQuery4("routers:list", { environment: env });
|
|
6681
|
+
}
|
|
6682
|
+
async function setConnectionRouter(connectionId, routerId) {
|
|
6683
|
+
return convexMutation3("whatsapp:setPhoneAgent", {
|
|
6684
|
+
connectionId,
|
|
6685
|
+
routerId
|
|
6686
|
+
});
|
|
6687
|
+
}
|
|
6688
|
+
async function setConnectionAgent(connectionId, agentId) {
|
|
6689
|
+
return convexMutation3("whatsapp:setPhoneAgent", {
|
|
6690
|
+
connectionId,
|
|
6691
|
+
agentId
|
|
6692
|
+
});
|
|
6693
|
+
}
|
|
6694
|
+
async function resolveAgentBySlug(slug) {
|
|
6695
|
+
return convexQuery4("agents:getBySlug", { slug });
|
|
6696
|
+
}
|
|
6421
6697
|
async function updateTemplate(connectionId, templateId, updates) {
|
|
6422
6698
|
if (getApiKey()) {
|
|
6423
6699
|
return httpPost("/v1/templates/update", { connectionId, templateId, ...updates });
|
|
@@ -6791,7 +7067,7 @@ async function convexQuery5(path, args) {
|
|
|
6791
7067
|
return { error: String(json.errorMessage || "Unknown error") };
|
|
6792
7068
|
return { error: `Unexpected response: ${text}` };
|
|
6793
7069
|
}
|
|
6794
|
-
async function
|
|
7070
|
+
async function convexMutation4(path, args) {
|
|
6795
7071
|
const result = await getValidToken();
|
|
6796
7072
|
if ("error" in result)
|
|
6797
7073
|
return { error: result.error };
|
|
@@ -6855,19 +7131,19 @@ async function getIntegrationConfig(provider, env) {
|
|
|
6855
7131
|
return convexQuery5("integrations:getConfig", { provider, environment: env });
|
|
6856
7132
|
}
|
|
6857
7133
|
async function updateIntegrationConfig(provider, env, config) {
|
|
6858
|
-
return
|
|
7134
|
+
return convexMutation4("integrations:updateConfig", { provider, environment: env, config });
|
|
6859
7135
|
}
|
|
6860
7136
|
async function testIntegrationConnection(provider, env) {
|
|
6861
7137
|
return convexAction2("integrations:testConnection", { provider, environment: env });
|
|
6862
7138
|
}
|
|
6863
7139
|
async function deleteIntegrationConfig(provider, env) {
|
|
6864
|
-
return
|
|
7140
|
+
return convexMutation4("integrations:deleteConfig", { provider, environment: env });
|
|
6865
7141
|
}
|
|
6866
7142
|
async function listIntegrationConfigs(env) {
|
|
6867
7143
|
return convexQuery5("integrations:listConfigs", { environment: env });
|
|
6868
7144
|
}
|
|
6869
7145
|
async function setIntegrationStatus(provider, env, status) {
|
|
6870
|
-
return
|
|
7146
|
+
return convexMutation4("integrations:setConfigStatus", { provider, environment: env, status });
|
|
6871
7147
|
}
|
|
6872
7148
|
|
|
6873
7149
|
// src/cli/commands/integration.ts
|
|
@@ -7187,7 +7463,7 @@ async function convexQuery6(path, args) {
|
|
|
7187
7463
|
}
|
|
7188
7464
|
return json.value;
|
|
7189
7465
|
}
|
|
7190
|
-
async function
|
|
7466
|
+
async function convexMutation5(path, args) {
|
|
7191
7467
|
const token = getToken3();
|
|
7192
7468
|
const response = await fetch(`${CONVEX_URL}/api/mutation`, {
|
|
7193
7469
|
method: "POST",
|
|
@@ -7239,14 +7515,14 @@ async function getLastRunStatuses(environment) {
|
|
|
7239
7515
|
return convexQuery6("triggers:getLastRunStatuses", { environment });
|
|
7240
7516
|
}
|
|
7241
7517
|
async function cancelTriggerRun(runId, environment) {
|
|
7242
|
-
return
|
|
7518
|
+
return convexMutation5("triggers:cancelRun", { runId, environment });
|
|
7243
7519
|
}
|
|
7244
7520
|
async function retryTriggerRun(runId, environment) {
|
|
7245
|
-
return
|
|
7521
|
+
return convexMutation5("triggers:retryRun", { runId, environment });
|
|
7246
7522
|
}
|
|
7247
7523
|
async function toggleTrigger(slug, enabled, environment) {
|
|
7248
7524
|
const path = enabled ? "triggers:enable" : "triggers:disable";
|
|
7249
|
-
return
|
|
7525
|
+
return convexMutation5(path, { slug, environment });
|
|
7250
7526
|
}
|
|
7251
7527
|
async function listTriggerExecutions(options) {
|
|
7252
7528
|
return convexQuery6("triggers:listExecutions", {
|
|
@@ -8281,7 +8557,7 @@ import chalk23 from "chalk";
|
|
|
8281
8557
|
import ora17 from "ora";
|
|
8282
8558
|
import readline from "readline";
|
|
8283
8559
|
init_convex();
|
|
8284
|
-
var chatCommand = new Command21("chat").description("Chat with an agent").argument("<
|
|
8560
|
+
var chatCommand = new Command21("chat").description("Chat with an agent or via a router").argument("<slug>", "Agent slug (or router slug when --router is used)").option("--env <environment>", "Environment: development | production | eval", "development").option("--thread <id>", "Continue an existing thread").option("--message <msg>", "Single message mode (send and exit)").option("--json", "Output JSON").option("--channel <channel>", "Channel identifier", "api").option("-v, --verbose", "Show detailed response info").option("--confirm", "Skip production warning prompt").option("--router", "Chat via a router instead of directly with an agent").option("--phone <number>", "Sender phone number for routing rules").action(async (slug, options) => {
|
|
8285
8561
|
const spinner = ora17();
|
|
8286
8562
|
const cwd = process.cwd();
|
|
8287
8563
|
const nonInteractive = !isInteractive();
|
|
@@ -8333,6 +8609,15 @@ var chatCommand = new Command21("chat").description("Chat with an agent").argume
|
|
|
8333
8609
|
console.log();
|
|
8334
8610
|
}
|
|
8335
8611
|
const environment = options.env;
|
|
8612
|
+
const isRouterMode = !!options.router;
|
|
8613
|
+
if (isRouterMode && !options.phone) {
|
|
8614
|
+
if (jsonMode) {
|
|
8615
|
+
console.log(JSON.stringify({ success: false, error: "--phone is required when using --router" }));
|
|
8616
|
+
} else {
|
|
8617
|
+
console.log(chalk23.red("--phone is required when using --router"));
|
|
8618
|
+
}
|
|
8619
|
+
process.exit(1);
|
|
8620
|
+
}
|
|
8336
8621
|
if (environment === "production" && !nonInteractive && !options.confirm) {
|
|
8337
8622
|
const confirmRl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
8338
8623
|
await new Promise((resolve) => {
|
|
@@ -8342,8 +8627,20 @@ var chatCommand = new Command21("chat").description("Chat with an agent").argume
|
|
|
8342
8627
|
confirmRl.close();
|
|
8343
8628
|
}
|
|
8344
8629
|
const doChat = async (message, threadId2, signal) => {
|
|
8630
|
+
if (isRouterMode) {
|
|
8631
|
+
return chatWithRouter({
|
|
8632
|
+
routerSlug: slug,
|
|
8633
|
+
message,
|
|
8634
|
+
threadId: threadId2,
|
|
8635
|
+
phoneNumber: options.phone,
|
|
8636
|
+
environment,
|
|
8637
|
+
organizationId: project?.organization.id,
|
|
8638
|
+
channel: options.channel,
|
|
8639
|
+
signal
|
|
8640
|
+
});
|
|
8641
|
+
}
|
|
8345
8642
|
return chatWithAgent({
|
|
8346
|
-
slug
|
|
8643
|
+
slug,
|
|
8347
8644
|
message,
|
|
8348
8645
|
threadId: threadId2,
|
|
8349
8646
|
environment,
|
|
@@ -8395,12 +8692,22 @@ var chatCommand = new Command21("chat").description("Chat with an agent").argume
|
|
|
8395
8692
|
}
|
|
8396
8693
|
if (!jsonMode)
|
|
8397
8694
|
spinner.succeed("Message sent");
|
|
8695
|
+
const routerResult = result;
|
|
8398
8696
|
if (jsonMode) {
|
|
8399
|
-
|
|
8697
|
+
const output = { message: result.message, threadId: result.threadId, usage: result.usage };
|
|
8698
|
+
if (routerResult.routedToAgent) {
|
|
8699
|
+
output.routedToAgent = routerResult.routedToAgent;
|
|
8700
|
+
output.routedToAgentSlug = routerResult.routedToAgentSlug;
|
|
8701
|
+
}
|
|
8702
|
+
console.log(JSON.stringify(output, null, 2));
|
|
8400
8703
|
} else {
|
|
8401
8704
|
console.log();
|
|
8402
8705
|
console.log("\u2500".repeat(60));
|
|
8403
8706
|
console.log();
|
|
8707
|
+
if (routerResult.routedToAgent) {
|
|
8708
|
+
console.log(chalk23.magenta(`Routed to: ${routerResult.routedToAgent} (${routerResult.routedToAgentSlug})`));
|
|
8709
|
+
console.log();
|
|
8710
|
+
}
|
|
8404
8711
|
console.log(chalk23.green("Agent:"));
|
|
8405
8712
|
console.log(result.message);
|
|
8406
8713
|
console.log();
|
|
@@ -8416,7 +8723,8 @@ var chatCommand = new Command21("chat").description("Chat with an agent").argume
|
|
|
8416
8723
|
}
|
|
8417
8724
|
return;
|
|
8418
8725
|
}
|
|
8419
|
-
|
|
8726
|
+
const headerLabel = isRouterMode ? `router ${chalk23.cyan(slug)}` : chalk23.cyan(slug);
|
|
8727
|
+
console.log(chalk23.bold(`Chat with ${headerLabel} (${environment})`));
|
|
8420
8728
|
console.log(chalk23.dim("Type 'exit' to quit"));
|
|
8421
8729
|
console.log();
|
|
8422
8730
|
let threadId = options.thread;
|
|
@@ -8505,7 +8813,12 @@ var chatCommand = new Command21("chat").description("Chat with an agent").argume
|
|
|
8505
8813
|
}
|
|
8506
8814
|
spinner.stop();
|
|
8507
8815
|
threadId = result.threadId;
|
|
8816
|
+
const interactiveRouterResult = result;
|
|
8508
8817
|
console.log();
|
|
8818
|
+
if (interactiveRouterResult.routedToAgent) {
|
|
8819
|
+
console.log(chalk23.magenta(`Routed to: ${interactiveRouterResult.routedToAgent} (${interactiveRouterResult.routedToAgentSlug})`));
|
|
8820
|
+
console.log();
|
|
8821
|
+
}
|
|
8509
8822
|
console.log(chalk23.green("Agent:"));
|
|
8510
8823
|
console.log(result.message);
|
|
8511
8824
|
console.log();
|
|
@@ -8528,10 +8841,207 @@ var chatCommand = new Command21("chat").description("Chat with an agent").argume
|
|
|
8528
8841
|
process.exit(0);
|
|
8529
8842
|
});
|
|
8530
8843
|
});
|
|
8844
|
+
|
|
8845
|
+
// src/cli/commands/whatsapp.ts
|
|
8846
|
+
init_credentials();
|
|
8847
|
+
import { Command as Command22 } from "commander";
|
|
8848
|
+
import chalk24 from "chalk";
|
|
8849
|
+
async function ensureAuth6() {
|
|
8850
|
+
const cwd = process.cwd();
|
|
8851
|
+
const nonInteractive = !isInteractive();
|
|
8852
|
+
if (!hasProject(cwd)) {
|
|
8853
|
+
if (nonInteractive) {
|
|
8854
|
+
console.error(chalk24.red("No struere.json found. Run struere init first."));
|
|
8855
|
+
process.exit(1);
|
|
8856
|
+
}
|
|
8857
|
+
console.log(chalk24.yellow("No struere.json found - initializing project..."));
|
|
8858
|
+
console.log();
|
|
8859
|
+
const success = await runInit(cwd);
|
|
8860
|
+
if (!success) {
|
|
8861
|
+
process.exit(1);
|
|
8862
|
+
}
|
|
8863
|
+
console.log();
|
|
8864
|
+
}
|
|
8865
|
+
let credentials = loadCredentials();
|
|
8866
|
+
const apiKey = getApiKey();
|
|
8867
|
+
if (!credentials && !apiKey) {
|
|
8868
|
+
if (nonInteractive) {
|
|
8869
|
+
console.error(chalk24.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
|
|
8870
|
+
process.exit(1);
|
|
8871
|
+
}
|
|
8872
|
+
console.log(chalk24.yellow("Not logged in - authenticating..."));
|
|
8873
|
+
console.log();
|
|
8874
|
+
credentials = await performLogin();
|
|
8875
|
+
if (!credentials) {
|
|
8876
|
+
console.log(chalk24.red("Authentication failed"));
|
|
8877
|
+
process.exit(1);
|
|
8878
|
+
}
|
|
8879
|
+
console.log();
|
|
8880
|
+
}
|
|
8881
|
+
return true;
|
|
8882
|
+
}
|
|
8883
|
+
async function resolveConnection(env, identifier, out) {
|
|
8884
|
+
out.start("Fetching WhatsApp connections");
|
|
8885
|
+
const { data, error } = await listWhatsAppConnections(env);
|
|
8886
|
+
if (error || !data) {
|
|
8887
|
+
out.fail("Failed to fetch connections");
|
|
8888
|
+
out.error(error ?? "Unknown error");
|
|
8889
|
+
return null;
|
|
8890
|
+
}
|
|
8891
|
+
const connections = data;
|
|
8892
|
+
const match = connections.find((c) => {
|
|
8893
|
+
if (c._id === identifier)
|
|
8894
|
+
return true;
|
|
8895
|
+
if (c._id.endsWith(identifier))
|
|
8896
|
+
return true;
|
|
8897
|
+
if (c.label && c.label.toLowerCase() === identifier.toLowerCase())
|
|
8898
|
+
return true;
|
|
8899
|
+
if (c.phoneNumber && c.phoneNumber === identifier.replace(/^\+/, ""))
|
|
8900
|
+
return true;
|
|
8901
|
+
if (c.phoneNumber && `+${c.phoneNumber}` === identifier)
|
|
8902
|
+
return true;
|
|
8903
|
+
return false;
|
|
8904
|
+
});
|
|
8905
|
+
if (!match) {
|
|
8906
|
+
out.fail(`Connection not found: ${identifier}`);
|
|
8907
|
+
out.info('Run "struere whatsapp list" to see available connections');
|
|
8908
|
+
return null;
|
|
8909
|
+
}
|
|
8910
|
+
out.succeed(`Found connection: ${match.label || (match.phoneNumber ? `+${match.phoneNumber}` : match._id)}`);
|
|
8911
|
+
return match;
|
|
8912
|
+
}
|
|
8913
|
+
function connectionLabel(conn) {
|
|
8914
|
+
return conn.label || (conn.phoneNumber ? `+${conn.phoneNumber}` : conn._id.slice(-12));
|
|
8915
|
+
}
|
|
8916
|
+
function statusColor5(status) {
|
|
8917
|
+
switch (status) {
|
|
8918
|
+
case "connected":
|
|
8919
|
+
return chalk24.green(status);
|
|
8920
|
+
case "pending_setup":
|
|
8921
|
+
return chalk24.yellow("pending");
|
|
8922
|
+
case "disconnected":
|
|
8923
|
+
return chalk24.red(status);
|
|
8924
|
+
default:
|
|
8925
|
+
return chalk24.gray(status);
|
|
8926
|
+
}
|
|
8927
|
+
}
|
|
8928
|
+
function assignmentLabel(conn) {
|
|
8929
|
+
if (conn.routerName)
|
|
8930
|
+
return `Router: ${conn.routerName}`;
|
|
8931
|
+
if (conn.agentName)
|
|
8932
|
+
return `Agent: ${conn.agentName}`;
|
|
8933
|
+
if (conn.routerId)
|
|
8934
|
+
return `Router: ${conn.routerId.slice(-8)}`;
|
|
8935
|
+
if (conn.agentId)
|
|
8936
|
+
return `Agent: ${conn.agentId.slice(-8)}`;
|
|
8937
|
+
return chalk24.gray("none");
|
|
8938
|
+
}
|
|
8939
|
+
var whatsappCommand = new Command22("whatsapp").description("Manage WhatsApp connections and routing");
|
|
8940
|
+
whatsappCommand.command("list").description("List WhatsApp connections with routing assignments").option("--env <environment>", "Environment (development|production)", "production").option("--json", "Output raw JSON").action(async (opts) => {
|
|
8941
|
+
await ensureAuth6();
|
|
8942
|
+
const env = opts.env;
|
|
8943
|
+
const out = createOutput();
|
|
8944
|
+
out.start("Fetching WhatsApp connections");
|
|
8945
|
+
const { data, error } = await listWhatsAppConnections(env);
|
|
8946
|
+
if (error || !data) {
|
|
8947
|
+
out.fail("Failed to fetch connections");
|
|
8948
|
+
out.error(error ?? "Unknown error");
|
|
8949
|
+
process.exit(1);
|
|
8950
|
+
}
|
|
8951
|
+
const connections = data;
|
|
8952
|
+
out.succeed(`Found ${connections.length} connections`);
|
|
8953
|
+
if (opts.json) {
|
|
8954
|
+
console.log(JSON.stringify(connections, null, 2));
|
|
8955
|
+
return;
|
|
8956
|
+
}
|
|
8957
|
+
console.log();
|
|
8958
|
+
if (connections.length === 0) {
|
|
8959
|
+
console.log(chalk24.gray(" No WhatsApp connections found"));
|
|
8960
|
+
console.log();
|
|
8961
|
+
return;
|
|
8962
|
+
}
|
|
8963
|
+
renderTable([
|
|
8964
|
+
{ key: "label", label: "Label", width: 20 },
|
|
8965
|
+
{ key: "phone", label: "Phone", width: 18 },
|
|
8966
|
+
{ key: "status", label: "Status", width: 14 },
|
|
8967
|
+
{ key: "assignment", label: "Assignment", width: 30 },
|
|
8968
|
+
{ key: "id", label: "ID", width: 16 }
|
|
8969
|
+
], connections.map((c) => ({
|
|
8970
|
+
label: c.label || chalk24.gray("-"),
|
|
8971
|
+
phone: c.phoneNumber ? `+${c.phoneNumber}` : chalk24.gray("-"),
|
|
8972
|
+
status: statusColor5(c.status),
|
|
8973
|
+
assignment: assignmentLabel(c),
|
|
8974
|
+
id: c._id.slice(-12)
|
|
8975
|
+
})));
|
|
8976
|
+
console.log();
|
|
8977
|
+
});
|
|
8978
|
+
whatsappCommand.command("set-router <connection> <router-slug>").description("Assign a router to a WhatsApp connection").option("--env <environment>", "Environment (development|production)", "production").action(async (connection, routerSlug, opts) => {
|
|
8979
|
+
await ensureAuth6();
|
|
8980
|
+
const env = opts.env;
|
|
8981
|
+
const out = createOutput();
|
|
8982
|
+
const conn = await resolveConnection(env, connection, out);
|
|
8983
|
+
if (!conn)
|
|
8984
|
+
process.exit(1);
|
|
8985
|
+
out.start(`Looking up router "${routerSlug}"`);
|
|
8986
|
+
const { data: routersData, error: routersError } = await listRouters(env);
|
|
8987
|
+
if (routersError || !routersData) {
|
|
8988
|
+
out.fail("Failed to fetch routers");
|
|
8989
|
+
out.error(routersError ?? "Unknown error");
|
|
8990
|
+
process.exit(1);
|
|
8991
|
+
}
|
|
8992
|
+
const routers = routersData;
|
|
8993
|
+
const router = routers.find((r) => r.slug === routerSlug);
|
|
8994
|
+
if (!router) {
|
|
8995
|
+
out.fail(`Router not found: ${routerSlug}`);
|
|
8996
|
+
if (routers.length > 0) {
|
|
8997
|
+
out.info(`Available routers: ${routers.map((r) => r.slug).join(", ")}`);
|
|
8998
|
+
} else {
|
|
8999
|
+
out.info("No routers found in this environment");
|
|
9000
|
+
}
|
|
9001
|
+
process.exit(1);
|
|
9002
|
+
}
|
|
9003
|
+
out.succeed(`Found router: ${router.name}`);
|
|
9004
|
+
out.start("Assigning router to connection");
|
|
9005
|
+
const { error: setError } = await setConnectionRouter(conn._id, router._id);
|
|
9006
|
+
if (setError) {
|
|
9007
|
+
out.fail("Failed to assign router");
|
|
9008
|
+
out.error(setError);
|
|
9009
|
+
process.exit(1);
|
|
9010
|
+
}
|
|
9011
|
+
out.succeed(`Connection ${connectionLabel(conn)} now routes via ${router.name} (${routerSlug})`);
|
|
9012
|
+
console.log();
|
|
9013
|
+
});
|
|
9014
|
+
whatsappCommand.command("set-agent <connection> <agent-slug>").description("Assign an agent directly to a WhatsApp connection").option("--env <environment>", "Environment (development|production)", "production").action(async (connection, agentSlug, opts) => {
|
|
9015
|
+
await ensureAuth6();
|
|
9016
|
+
const env = opts.env;
|
|
9017
|
+
const out = createOutput();
|
|
9018
|
+
const conn = await resolveConnection(env, connection, out);
|
|
9019
|
+
if (!conn)
|
|
9020
|
+
process.exit(1);
|
|
9021
|
+
out.start(`Looking up agent "${agentSlug}"`);
|
|
9022
|
+
const { data: agentData, error: agentError } = await resolveAgentBySlug(agentSlug);
|
|
9023
|
+
if (agentError || !agentData) {
|
|
9024
|
+
out.fail(`Agent not found: ${agentSlug}`);
|
|
9025
|
+
if (agentError)
|
|
9026
|
+
out.error(agentError);
|
|
9027
|
+
process.exit(1);
|
|
9028
|
+
}
|
|
9029
|
+
const agent = agentData;
|
|
9030
|
+
out.succeed(`Found agent: ${agent.name}`);
|
|
9031
|
+
out.start("Assigning agent to connection");
|
|
9032
|
+
const { error: setError } = await setConnectionAgent(conn._id, agent._id);
|
|
9033
|
+
if (setError) {
|
|
9034
|
+
out.fail("Failed to assign agent");
|
|
9035
|
+
out.error(setError);
|
|
9036
|
+
process.exit(1);
|
|
9037
|
+
}
|
|
9038
|
+
out.succeed(`Connection ${connectionLabel(conn)} now assigned to agent ${agent.name} (${agentSlug})`);
|
|
9039
|
+
console.log();
|
|
9040
|
+
});
|
|
8531
9041
|
// package.json
|
|
8532
9042
|
var package_default = {
|
|
8533
9043
|
name: "struere",
|
|
8534
|
-
version: "0.12.
|
|
9044
|
+
version: "0.12.3",
|
|
8535
9045
|
description: "Build, test, and deploy AI agents",
|
|
8536
9046
|
keywords: [
|
|
8537
9047
|
"ai",
|
|
@@ -8652,4 +9162,5 @@ program.addCommand(triggersCommand);
|
|
|
8652
9162
|
program.addCommand(compilePromptCommand);
|
|
8653
9163
|
program.addCommand(runToolCommand);
|
|
8654
9164
|
program.addCommand(chatCommand);
|
|
9165
|
+
program.addCommand(whatsappCommand);
|
|
8655
9166
|
program.parse();
|