struere 0.15.0 → 0.16.0
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 +1733 -302
- package/dist/cli/commands/add.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/workflows.d.ts +3 -0
- package/dist/cli/commands/workflows.d.ts.map +1 -0
- package/dist/cli/index.js +1746 -304
- 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 +70 -0
- package/dist/cli/utils/convex.d.ts.map +1 -1
- package/dist/cli/utils/extractor.d.ts +17 -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/plugin.d.ts +2 -2
- package/dist/cli/utils/plugin.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/workflows.d.ts +123 -0
- package/dist/cli/utils/workflows.d.ts.map +1 -0
- package/dist/define/__examples__/vip-payment-workflow.d.ts +3 -0
- package/dist/define/__examples__/vip-payment-workflow.d.ts.map +1 -0
- package/dist/define/workflow.d.ts +132 -0
- package/dist/define/workflow.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +279 -1
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/workflow-core/catalog.d.ts +23 -0
- package/dist/workflow-core/catalog.d.ts.map +1 -0
- package/dist/workflow-core/expression.d.ts +3 -0
- package/dist/workflow-core/expression.d.ts.map +1 -0
- package/dist/workflow-core/index.d.ts +6 -0
- package/dist/workflow-core/index.d.ts.map +1 -0
- package/dist/workflow-core/index.js +168 -0
- package/dist/workflow-core/status.d.ts +5 -0
- package/dist/workflow-core/status.d.ts.map +1 -0
- package/package.json +6 -2
package/dist/bin/struere.js
CHANGED
|
@@ -67,6 +67,7 @@ __export(exports_convex, {
|
|
|
67
67
|
getSyncState: () => getSyncState,
|
|
68
68
|
getSiteUrl: () => getSiteUrl,
|
|
69
69
|
getPullState: () => getPullState,
|
|
70
|
+
fireWorkflow: () => fireWorkflow,
|
|
70
71
|
fireTrigger: () => fireTrigger,
|
|
71
72
|
createOrganization: () => createOrganization,
|
|
72
73
|
compilePrompt: () => compilePrompt,
|
|
@@ -235,6 +236,7 @@ async function syncViaHttp(apiKey, payload) {
|
|
|
235
236
|
evalSuites: payload.evalSuites,
|
|
236
237
|
triggers: payload.triggers,
|
|
237
238
|
routers: payload.routers,
|
|
239
|
+
workflows: payload.workflows,
|
|
238
240
|
fixtures: payload.fixtures
|
|
239
241
|
}),
|
|
240
242
|
signal: AbortSignal.timeout(30000)
|
|
@@ -743,6 +745,92 @@ async function _fireTrigger(options) {
|
|
|
743
745
|
}
|
|
744
746
|
return { error: `Unexpected response: ${text}` };
|
|
745
747
|
}
|
|
748
|
+
async function fireWorkflow(options) {
|
|
749
|
+
return withAuthRetry(() => _fireWorkflow(options));
|
|
750
|
+
}
|
|
751
|
+
async function _fireWorkflow(options) {
|
|
752
|
+
const credentials = loadCredentials();
|
|
753
|
+
const apiKey = getApiKey();
|
|
754
|
+
if (apiKey && !credentials?.token) {
|
|
755
|
+
const siteUrl = getSiteUrl();
|
|
756
|
+
try {
|
|
757
|
+
const response2 = await fetch(`${siteUrl}/v1/fire-workflow`, {
|
|
758
|
+
method: "POST",
|
|
759
|
+
headers: {
|
|
760
|
+
"Content-Type": "application/json",
|
|
761
|
+
Authorization: `Bearer ${apiKey}`
|
|
762
|
+
},
|
|
763
|
+
body: JSON.stringify({
|
|
764
|
+
slug: options.slug,
|
|
765
|
+
data: options.data
|
|
766
|
+
}),
|
|
767
|
+
signal: AbortSignal.timeout(60000)
|
|
768
|
+
});
|
|
769
|
+
const text2 = await response2.text();
|
|
770
|
+
let json2;
|
|
771
|
+
try {
|
|
772
|
+
json2 = JSON.parse(text2);
|
|
773
|
+
} catch {
|
|
774
|
+
return { error: text2 || `HTTP ${response2.status}` };
|
|
775
|
+
}
|
|
776
|
+
if (!response2.ok) {
|
|
777
|
+
return { error: json2.error || text2 };
|
|
778
|
+
}
|
|
779
|
+
return { result: json2 };
|
|
780
|
+
} catch (err) {
|
|
781
|
+
if (err instanceof DOMException && err.name === "TimeoutError") {
|
|
782
|
+
return { error: "Request timed out after 60s" };
|
|
783
|
+
}
|
|
784
|
+
return { error: `Network error: ${err instanceof Error ? err.message : String(err)}` };
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (credentials?.sessionId) {
|
|
788
|
+
await refreshToken();
|
|
789
|
+
}
|
|
790
|
+
const freshCredentials = loadCredentials();
|
|
791
|
+
const token = apiKey || freshCredentials?.token;
|
|
792
|
+
if (!token) {
|
|
793
|
+
return { error: "Not authenticated" };
|
|
794
|
+
}
|
|
795
|
+
const response = await fetch(`${CONVEX_URL}/api/action`, {
|
|
796
|
+
method: "POST",
|
|
797
|
+
headers: {
|
|
798
|
+
"Content-Type": "application/json",
|
|
799
|
+
Authorization: `Bearer ${token}`
|
|
800
|
+
},
|
|
801
|
+
body: JSON.stringify({
|
|
802
|
+
path: "workflows:fire",
|
|
803
|
+
args: {
|
|
804
|
+
slug: options.slug,
|
|
805
|
+
environment: options.environment,
|
|
806
|
+
data: options.data,
|
|
807
|
+
...options.organizationId && { organizationId: options.organizationId }
|
|
808
|
+
}
|
|
809
|
+
}),
|
|
810
|
+
signal: AbortSignal.timeout(60000)
|
|
811
|
+
});
|
|
812
|
+
const text = await response.text();
|
|
813
|
+
let json;
|
|
814
|
+
try {
|
|
815
|
+
json = JSON.parse(text);
|
|
816
|
+
} catch {
|
|
817
|
+
return { error: text || `HTTP ${response.status}` };
|
|
818
|
+
}
|
|
819
|
+
if (!response.ok) {
|
|
820
|
+
const msg = json.errorData?.message || json.errorMessage || text;
|
|
821
|
+
return { error: msg };
|
|
822
|
+
}
|
|
823
|
+
if (json.status === "success" && json.value) {
|
|
824
|
+
return { result: json.value };
|
|
825
|
+
}
|
|
826
|
+
if (json.status === "success" && json.value === null) {
|
|
827
|
+
return { error: `Workflow not found: ${options.slug}` };
|
|
828
|
+
}
|
|
829
|
+
if (json.status === "error") {
|
|
830
|
+
return { error: json.errorData?.message || json.errorMessage || "Unknown error from Convex" };
|
|
831
|
+
}
|
|
832
|
+
return { error: `Unexpected response: ${text}` };
|
|
833
|
+
}
|
|
746
834
|
async function chatWithAgent(options) {
|
|
747
835
|
return withAuthRetry(() => _chatWithAgent(options));
|
|
748
836
|
}
|
|
@@ -63921,6 +64009,29 @@ export default defineTrigger({
|
|
|
63921
64009
|
})
|
|
63922
64010
|
`;
|
|
63923
64011
|
}
|
|
64012
|
+
function getWorkflowTs(name, slug) {
|
|
64013
|
+
const wf = `wf_${slug.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase())}`;
|
|
64014
|
+
return `import { workflow } from 'struere'
|
|
64015
|
+
|
|
64016
|
+
const ${wf} = workflow({
|
|
64017
|
+
name: "${name}",
|
|
64018
|
+
slug: "${slug}",
|
|
64019
|
+
})
|
|
64020
|
+
|
|
64021
|
+
${wf}
|
|
64022
|
+
.onEntity({
|
|
64023
|
+
entityType: "ENTITY_TYPE_HERE",
|
|
64024
|
+
action: "created",
|
|
64025
|
+
})
|
|
64026
|
+
.tool("entity.update", {
|
|
64027
|
+
type: "{{ $trigger.entityType }}",
|
|
64028
|
+
id: "{{ $trigger.entityId }}",
|
|
64029
|
+
data: { handled: true },
|
|
64030
|
+
}, { name: "Mark handled" })
|
|
64031
|
+
|
|
64032
|
+
export default ${wf}.build()
|
|
64033
|
+
`;
|
|
64034
|
+
}
|
|
63924
64035
|
function getRouterTs(name, slug) {
|
|
63925
64036
|
return `import { defineRouter } from 'struere'
|
|
63926
64037
|
|
|
@@ -63968,6 +64079,7 @@ function scaffoldProject(cwd, options) {
|
|
|
63968
64079
|
"evals",
|
|
63969
64080
|
"triggers",
|
|
63970
64081
|
"routers",
|
|
64082
|
+
"workflows",
|
|
63971
64083
|
"fixtures",
|
|
63972
64084
|
".struere"
|
|
63973
64085
|
];
|
|
@@ -64123,6 +64235,24 @@ function scaffoldRouter(cwd, name, slug) {
|
|
|
64123
64235
|
result.createdFiles.push(`routers/${fileName}`);
|
|
64124
64236
|
return result;
|
|
64125
64237
|
}
|
|
64238
|
+
function scaffoldWorkflow(cwd, name, slug) {
|
|
64239
|
+
const result = {
|
|
64240
|
+
createdFiles: [],
|
|
64241
|
+
updatedFiles: []
|
|
64242
|
+
};
|
|
64243
|
+
const workflowsDir = join3(cwd, "workflows");
|
|
64244
|
+
if (!existsSync3(workflowsDir)) {
|
|
64245
|
+
mkdirSync2(workflowsDir, { recursive: true });
|
|
64246
|
+
}
|
|
64247
|
+
const fileName = `${slug}.ts`;
|
|
64248
|
+
const filePath = join3(workflowsDir, fileName);
|
|
64249
|
+
if (existsSync3(filePath)) {
|
|
64250
|
+
return result;
|
|
64251
|
+
}
|
|
64252
|
+
writeFileSync2(filePath, getWorkflowTs(name, slug));
|
|
64253
|
+
result.createdFiles.push(`workflows/${fileName}`);
|
|
64254
|
+
return result;
|
|
64255
|
+
}
|
|
64126
64256
|
function scaffoldFixture(cwd, name, slug) {
|
|
64127
64257
|
const result = {
|
|
64128
64258
|
createdFiles: [],
|
|
@@ -64299,11 +64429,178 @@ function defineView(config) {
|
|
|
64299
64429
|
return config
|
|
64300
64430
|
}
|
|
64301
64431
|
|
|
64432
|
+
function wfNextId(state, type) {
|
|
64433
|
+
state.idCounter.n += 1
|
|
64434
|
+
return type.replace(/[^a-z0-9]+/gi, '_') + '_' + state.idCounter.n
|
|
64435
|
+
}
|
|
64436
|
+
|
|
64437
|
+
function wfRegisterName(state, name) {
|
|
64438
|
+
if (state.names.has(name)) throw new Error('Workflow "' + state.meta.slug + '" has duplicate node name "' + name + '". Node names must be unique.')
|
|
64439
|
+
state.names.add(name)
|
|
64440
|
+
return name
|
|
64441
|
+
}
|
|
64442
|
+
|
|
64443
|
+
function wfAddNode(state, type, name, params) {
|
|
64444
|
+
var node = { id: wfNextId(state, type), name: wfRegisterName(state, name), type: type, params: params }
|
|
64445
|
+
state.nodes.push(node)
|
|
64446
|
+
return node
|
|
64447
|
+
}
|
|
64448
|
+
|
|
64449
|
+
function wfConnect(state, sourceId, port, targetId) {
|
|
64450
|
+
var entry = state.connections[sourceId]
|
|
64451
|
+
if (!entry) { entry = { main: [] }; state.connections[sourceId] = entry }
|
|
64452
|
+
while (entry.main.length <= port) entry.main.push([])
|
|
64453
|
+
entry.main[port].push({ node: targetId, input: 0 })
|
|
64454
|
+
}
|
|
64455
|
+
|
|
64456
|
+
function WfOneHandle(state, nodeId, nodeName, port) {
|
|
64457
|
+
this.state = state; this.nodeId = nodeId; this.nodeName = nodeName; this.port = port
|
|
64458
|
+
}
|
|
64459
|
+
WfOneHandle.prototype.ref = function (path) {
|
|
64460
|
+
var suffix = path ? (path.charAt(0) === '.' ? path : '.' + path) : ''
|
|
64461
|
+
return '{{ $node["' + this.nodeName + '"]' + suffix + ' }}'
|
|
64462
|
+
}
|
|
64463
|
+
WfOneHandle.prototype.tool = function (type, params, opts) {
|
|
64464
|
+
var node = wfAddNode(this.state, 'tool.' + type, (opts && opts.name) || type, params)
|
|
64465
|
+
wfConnect(this.state, this.nodeId, this.port, node.id)
|
|
64466
|
+
return new WfOneHandle(this.state, node.id, node.name, 0)
|
|
64467
|
+
}
|
|
64468
|
+
WfOneHandle.prototype.query = function (type, params, opts) {
|
|
64469
|
+
var node = wfAddNode(this.state, 'tool.' + type, (opts && opts.name) || type, params)
|
|
64470
|
+
wfConnect(this.state, this.nodeId, this.port, node.id)
|
|
64471
|
+
return new WfManyHandle(this.state, node.id, node.name, 0)
|
|
64472
|
+
}
|
|
64473
|
+
WfOneHandle.prototype.agent = function (opts) {
|
|
64474
|
+
var params = { agent: opts.agent, message: opts.message }
|
|
64475
|
+
if (opts.context !== undefined) params.context = opts.context
|
|
64476
|
+
if (opts.output !== undefined) params.output = opts.output
|
|
64477
|
+
var node = wfAddNode(this.state, 'tool.agent.chat', opts.name || 'agent.chat', params)
|
|
64478
|
+
wfConnect(this.state, this.nodeId, this.port, node.id)
|
|
64479
|
+
return new WfOneHandle(this.state, node.id, node.name, 0)
|
|
64480
|
+
}
|
|
64481
|
+
WfOneHandle.prototype.splitOut = function (field, opts) {
|
|
64482
|
+
var node = wfAddNode(this.state, 'flow.splitout', (opts && opts.name) || 'splitout', { field: field })
|
|
64483
|
+
wfConnect(this.state, this.nodeId, this.port, node.id)
|
|
64484
|
+
return new WfManyHandle(this.state, node.id, node.name, 0)
|
|
64485
|
+
}
|
|
64486
|
+
WfOneHandle.prototype.forEach = function (opts, body) {
|
|
64487
|
+
var params = { over: opts.over }
|
|
64488
|
+
if (opts.batchSize !== undefined) params.batchSize = opts.batchSize
|
|
64489
|
+
if (opts.throttleMs !== undefined) params.throttleMs = opts.throttleMs
|
|
64490
|
+
if (opts.continueOnItemError !== undefined) params.continueOnItemError = opts.continueOnItemError
|
|
64491
|
+
var node = wfAddNode(this.state, 'flow.foreach', opts.name || 'foreach', params)
|
|
64492
|
+
wfConnect(this.state, this.nodeId, this.port, node.id)
|
|
64493
|
+
body(new WfOneHandle(this.state, node.id, node.name, 0))
|
|
64494
|
+
return new WfManyHandle(this.state, node.id, node.name, 1)
|
|
64495
|
+
}
|
|
64496
|
+
WfOneHandle.prototype.if = function (opts, branches) {
|
|
64497
|
+
var condition = { left: opts.left, operator: opts.operator }
|
|
64498
|
+
if (opts.right !== undefined) condition.right = opts.right
|
|
64499
|
+
var node = wfAddNode(this.state, 'flow.if', opts.name || 'if', { condition: condition })
|
|
64500
|
+
wfConnect(this.state, this.nodeId, this.port, node.id)
|
|
64501
|
+
branches.whenTrue(new WfOneHandle(this.state, node.id, node.name, 0))
|
|
64502
|
+
if (branches.whenFalse) branches.whenFalse(new WfOneHandle(this.state, node.id, node.name, 1))
|
|
64503
|
+
}
|
|
64504
|
+
WfOneHandle.prototype.switch = function (opts) {
|
|
64505
|
+
var cases = opts.cases.map(function (c, i) { return { value: c.value, output: i } })
|
|
64506
|
+
var fallbackOutput = opts.cases.length
|
|
64507
|
+
var node = wfAddNode(this.state, 'flow.switch', opts.name || 'switch', { value: opts.value, cases: cases, fallbackOutput: fallbackOutput })
|
|
64508
|
+
wfConnect(this.state, this.nodeId, this.port, node.id)
|
|
64509
|
+
var self = this
|
|
64510
|
+
opts.cases.forEach(function (c, i) { c.then(new WfOneHandle(self.state, node.id, node.name, i)) })
|
|
64511
|
+
if (opts.fallback) opts.fallback(new WfOneHandle(this.state, node.id, node.name, fallbackOutput))
|
|
64512
|
+
}
|
|
64513
|
+
WfOneHandle.prototype.fanOut = function () {
|
|
64514
|
+
var branches = Array.prototype.slice.call(arguments)
|
|
64515
|
+
for (var i = 0; i < branches.length; i++) {
|
|
64516
|
+
branches[i](new WfOneHandle(this.state, this.nodeId, this.nodeName, this.port))
|
|
64517
|
+
}
|
|
64518
|
+
}
|
|
64519
|
+
|
|
64520
|
+
function WfManyHandle(state, nodeId, nodeName, port) {
|
|
64521
|
+
this.state = state; this.nodeId = nodeId; this.nodeName = nodeName; this.port = port
|
|
64522
|
+
}
|
|
64523
|
+
WfManyHandle.prototype.ref = WfOneHandle.prototype.ref
|
|
64524
|
+
WfManyHandle.prototype.forEach = function (opts, body) {
|
|
64525
|
+
var params = {}
|
|
64526
|
+
if (opts.batchSize !== undefined) params.batchSize = opts.batchSize
|
|
64527
|
+
if (opts.throttleMs !== undefined) params.throttleMs = opts.throttleMs
|
|
64528
|
+
if (opts.continueOnItemError !== undefined) params.continueOnItemError = opts.continueOnItemError
|
|
64529
|
+
var node = wfAddNode(this.state, 'flow.foreach', opts.name || 'foreach', params)
|
|
64530
|
+
wfConnect(this.state, this.nodeId, this.port, node.id)
|
|
64531
|
+
body(new WfOneHandle(this.state, node.id, node.name, 0))
|
|
64532
|
+
return new WfManyHandle(this.state, node.id, node.name, 1)
|
|
64533
|
+
}
|
|
64534
|
+
WfManyHandle.prototype.aggregate = function (opts) {
|
|
64535
|
+
var params = { mode: opts.mode }
|
|
64536
|
+
if (opts.field !== undefined) params.field = opts.field
|
|
64537
|
+
var node = wfAddNode(this.state, 'flow.aggregate', opts.name || 'aggregate', params)
|
|
64538
|
+
wfConnect(this.state, this.nodeId, this.port, node.id)
|
|
64539
|
+
return new WfOneHandle(this.state, node.id, node.name, 0)
|
|
64540
|
+
}
|
|
64541
|
+
|
|
64542
|
+
function WorkflowBuilder(meta) {
|
|
64543
|
+
if (!meta.name) throw new Error('Workflow name is required')
|
|
64544
|
+
if (!meta.slug) throw new Error('Workflow slug is required')
|
|
64545
|
+
this.state = { meta: meta, nodes: [], connections: {}, idCounter: { n: 0 }, names: new Set(), hasTrigger: false }
|
|
64546
|
+
}
|
|
64547
|
+
WorkflowBuilder.prototype._addTrigger = function (type, name, params) {
|
|
64548
|
+
if (this.state.hasTrigger) throw new Error('Workflow "' + this.state.meta.slug + '" can only have one trigger node')
|
|
64549
|
+
this.state.hasTrigger = true
|
|
64550
|
+
var node = wfAddNode(this.state, type, name, params)
|
|
64551
|
+
return new WfOneHandle(this.state, node.id, node.name, 0)
|
|
64552
|
+
}
|
|
64553
|
+
WorkflowBuilder.prototype.onEntity = function (opts) {
|
|
64554
|
+
if (opts.transitions && opts.action !== 'updated') throw new Error('Workflow "' + this.state.meta.slug + '" trigger transitions are only valid when action is "updated"')
|
|
64555
|
+
var params = { entityType: opts.entityType, action: opts.action, concurrencyMode: opts.concurrencyMode || 'parallel' }
|
|
64556
|
+
if (opts.match !== undefined) params.match = opts.match
|
|
64557
|
+
if (opts.transitions !== undefined) params.transitions = opts.transitions
|
|
64558
|
+
if (opts.dedupeKey !== undefined) params.dedupeKey = opts.dedupeKey
|
|
64559
|
+
return this._addTrigger('trigger.entity', opts.entityType + '.' + opts.action, params)
|
|
64560
|
+
}
|
|
64561
|
+
WorkflowBuilder.prototype.onCron = function (opts) {
|
|
64562
|
+
var params = { cronSchedule: opts.schedule, concurrencyMode: opts.concurrencyMode || 'parallel' }
|
|
64563
|
+
if (opts.timezone !== undefined) params.cronTimezone = opts.timezone
|
|
64564
|
+
if (opts.dedupeKey !== undefined) params.dedupeKey = opts.dedupeKey
|
|
64565
|
+
return this._addTrigger('trigger.cron', 'cron', params)
|
|
64566
|
+
}
|
|
64567
|
+
WorkflowBuilder.prototype.onManual = function (opts) {
|
|
64568
|
+
var params = { concurrencyMode: (opts && opts.concurrencyMode) || 'parallel' }
|
|
64569
|
+
if (opts && opts.inputSchema !== undefined) params.inputSchema = opts.inputSchema
|
|
64570
|
+
if (opts && opts.dedupeKey !== undefined) params.dedupeKey = opts.dedupeKey
|
|
64571
|
+
return this._addTrigger('trigger.manual', 'manual', params)
|
|
64572
|
+
}
|
|
64573
|
+
WorkflowBuilder.prototype.build = function () {
|
|
64574
|
+
if (!this.state.hasTrigger) throw new Error('Workflow "' + this.state.meta.slug + '" must declare exactly one trigger node')
|
|
64575
|
+
var out = { name: this.state.meta.name, slug: this.state.meta.slug, nodes: this.state.nodes, connections: this.state.connections }
|
|
64576
|
+
if (this.state.meta.description !== undefined) out.description = this.state.meta.description
|
|
64577
|
+
return out
|
|
64578
|
+
}
|
|
64579
|
+
|
|
64580
|
+
function workflow(meta) {
|
|
64581
|
+
return new WorkflowBuilder(meta)
|
|
64582
|
+
}
|
|
64583
|
+
|
|
64584
|
+
function defineWorkflow(config) {
|
|
64585
|
+
if (!config.name) throw new Error('Workflow name is required')
|
|
64586
|
+
if (!config.slug) throw new Error('Workflow slug is required')
|
|
64587
|
+
if (!config.nodes || config.nodes.length === 0) throw new Error('Workflow must have at least one node')
|
|
64588
|
+
var triggerNodes = config.nodes.filter(function (n) { return n.type.indexOf('trigger.') === 0 })
|
|
64589
|
+
if (triggerNodes.length !== 1) throw new Error('Workflow "' + config.slug + '" must have exactly one trigger node, found ' + triggerNodes.length)
|
|
64590
|
+
var names = new Set()
|
|
64591
|
+
for (var i = 0; i < config.nodes.length; i++) {
|
|
64592
|
+
var node = config.nodes[i]
|
|
64593
|
+
if (names.has(node.name)) throw new Error('Workflow "' + config.slug + '" has duplicate node name "' + node.name + '". Node names must be unique.')
|
|
64594
|
+
names.add(node.name)
|
|
64595
|
+
}
|
|
64596
|
+
return config
|
|
64597
|
+
}
|
|
64598
|
+
|
|
64302
64599
|
function defineEntityType() {
|
|
64303
64600
|
throw new Error('defineEntityType has been renamed to defineData. Please update your imports: import { defineData } from "struere"')
|
|
64304
64601
|
}
|
|
64305
64602
|
|
|
64306
|
-
export { defineAgent, defineRole, defineData, defineEntityType, defineTrigger, defineTools, defineRouter, defineView }
|
|
64603
|
+
export { defineAgent, defineRole, defineData, defineEntityType, defineTrigger, defineTools, defineRouter, defineView, defineWorkflow, workflow, WorkflowBuilder }
|
|
64307
64604
|
`;
|
|
64308
64605
|
var TYPE_DECLARATIONS = `export interface ModelConfig {
|
|
64309
64606
|
model: string
|
|
@@ -64575,6 +64872,64 @@ export interface RouterConfig {
|
|
|
64575
64872
|
inactivityResetMs?: number
|
|
64576
64873
|
}
|
|
64577
64874
|
|
|
64875
|
+
export interface WorkflowNodeConfig {
|
|
64876
|
+
id: string
|
|
64877
|
+
name: string
|
|
64878
|
+
type: string
|
|
64879
|
+
params: Record<string, unknown>
|
|
64880
|
+
}
|
|
64881
|
+
|
|
64882
|
+
export type WorkflowConnections = Record<
|
|
64883
|
+
string,
|
|
64884
|
+
{ main: Array<Array<{ node: string; input: number }>> }
|
|
64885
|
+
>
|
|
64886
|
+
|
|
64887
|
+
export interface WorkflowConfig {
|
|
64888
|
+
name: string
|
|
64889
|
+
slug: string
|
|
64890
|
+
description?: string
|
|
64891
|
+
nodes: WorkflowNodeConfig[]
|
|
64892
|
+
connections: WorkflowConnections
|
|
64893
|
+
}
|
|
64894
|
+
|
|
64895
|
+
export type ConcurrencyMode = 'parallel' | 'dropIfRunning' | 'cancelPrevious'
|
|
64896
|
+
|
|
64897
|
+
export type ManyTool =
|
|
64898
|
+
| 'entity.query'
|
|
64899
|
+
| 'event.query'
|
|
64900
|
+
| 'web.search'
|
|
64901
|
+
| 'calendar.list'
|
|
64902
|
+
| 'calendar.freeBusy'
|
|
64903
|
+
| 'airtable.listRecords'
|
|
64904
|
+
| 'whatsapp.listTemplates'
|
|
64905
|
+
|
|
64906
|
+
export type OneTool = string & { __notMany?: never }
|
|
64907
|
+
|
|
64908
|
+
export interface OneHandle {
|
|
64909
|
+
tool(type: OneTool, params: Record<string, unknown>, opts?: { name?: string }): OneHandle
|
|
64910
|
+
query(type: ManyTool, params: Record<string, unknown>, opts?: { name?: string }): ManyHandle
|
|
64911
|
+
agent(opts: { agent: string; message: string; context?: Record<string, unknown>; output?: { schema: JSONSchema }; name?: string }): OneHandle
|
|
64912
|
+
splitOut(field: string, opts?: { name?: string }): ManyHandle
|
|
64913
|
+
forEach(opts: { over: string; batchSize?: number; throttleMs?: number; continueOnItemError?: boolean; name?: string }, body: (loop: OneHandle) => void): ManyHandle
|
|
64914
|
+
if(opts: { left: string; operator: string; right?: unknown; name?: string }, branches: { whenTrue: (h: OneHandle) => void; whenFalse?: (h: OneHandle) => void }): void
|
|
64915
|
+
switch(opts: { value: string; cases: { value: unknown; then: (h: OneHandle) => void }[]; fallback?: (h: OneHandle) => void; name?: string }): void
|
|
64916
|
+
fanOut(...branches: Array<(h: OneHandle) => void>): void
|
|
64917
|
+
ref(path?: string): string
|
|
64918
|
+
}
|
|
64919
|
+
|
|
64920
|
+
export interface ManyHandle {
|
|
64921
|
+
forEach(opts: { batchSize?: number; throttleMs?: number; continueOnItemError?: boolean; name?: string }, body: (item: OneHandle) => void): ManyHandle
|
|
64922
|
+
aggregate(opts: { mode: 'collect' | 'merge'; field?: string; name?: string }): OneHandle
|
|
64923
|
+
ref(path?: string): string
|
|
64924
|
+
}
|
|
64925
|
+
|
|
64926
|
+
export interface WorkflowBuilder {
|
|
64927
|
+
onEntity(opts: { entityType: string; action: 'created' | 'updated' | 'deleted'; match?: Record<string, unknown>; transitions?: { field: string; from?: unknown | unknown[]; to?: unknown | unknown[] }[]; dedupeKey?: string; concurrencyMode?: ConcurrencyMode }): OneHandle
|
|
64928
|
+
onCron(opts: { schedule: string; timezone?: string; dedupeKey?: string; concurrencyMode?: ConcurrencyMode }): OneHandle
|
|
64929
|
+
onManual(opts?: { inputSchema?: JSONSchema; dedupeKey?: string; concurrencyMode?: ConcurrencyMode }): OneHandle
|
|
64930
|
+
build(): WorkflowConfig
|
|
64931
|
+
}
|
|
64932
|
+
|
|
64578
64933
|
export function defineAgent(config: AgentConfig): AgentConfig
|
|
64579
64934
|
export function defineRole(config: RoleConfig): RoleConfig
|
|
64580
64935
|
export function defineData(config: EntityTypeConfig): EntityTypeConfig
|
|
@@ -64583,6 +64938,8 @@ export function defineTrigger(config: TriggerConfig): TriggerConfig
|
|
|
64583
64938
|
export function defineTools(tools: ToolDefinition[]): ToolReference[]
|
|
64584
64939
|
export function defineRouter(config: RouterConfig): RouterConfig
|
|
64585
64940
|
export function defineView(config: ViewSourceConfig): ViewSourceConfig
|
|
64941
|
+
export function defineWorkflow(config: WorkflowConfig): WorkflowConfig
|
|
64942
|
+
export function workflow(meta: { name: string; slug: string; description?: string }): WorkflowBuilder
|
|
64586
64943
|
`;
|
|
64587
64944
|
var STRUERE_VIEW_TYPE_DECLARATIONS = `declare module "struere/view" {
|
|
64588
64945
|
export interface ViewQuery {
|
|
@@ -64795,6 +65152,8 @@ function formatResourceSummary(resources) {
|
|
|
64795
65152
|
parts.push(`${resources.customTools.length} custom tools`);
|
|
64796
65153
|
if (resources.routers.length > 0)
|
|
64797
65154
|
parts.push(`${resources.routers.length} routers`);
|
|
65155
|
+
if (resources.workflows.length > 0)
|
|
65156
|
+
parts.push(`${resources.workflows.length} workflows`);
|
|
64798
65157
|
if (resources.triggers.length > 0)
|
|
64799
65158
|
parts.push(`${resources.triggers.length} triggers`);
|
|
64800
65159
|
if (resources.evalSuites.length > 0)
|
|
@@ -64818,9 +65177,32 @@ async function loadAllResources(cwd) {
|
|
|
64818
65177
|
errors.push(...evalErrors);
|
|
64819
65178
|
const triggers = await loadTsDirectory(join5(cwd, "triggers"));
|
|
64820
65179
|
const routers = await loadTsDirectory(join5(cwd, "routers"));
|
|
65180
|
+
const workflows = await loadWorkflows(join5(cwd, "workflows"));
|
|
64821
65181
|
const { fixtures, errors: fixtureErrors } = loadFixtures(join5(cwd, "fixtures"));
|
|
64822
65182
|
errors.push(...fixtureErrors);
|
|
64823
|
-
return { agents, entityTypes, customViewSources, roles, customTools, evalSuites, triggers, routers, fixtures, errors };
|
|
65183
|
+
return { agents, entityTypes, customViewSources, roles, customTools, evalSuites, triggers, routers, workflows, fixtures, errors };
|
|
65184
|
+
}
|
|
65185
|
+
async function loadWorkflows(dir) {
|
|
65186
|
+
if (!existsSync5(dir)) {
|
|
65187
|
+
return [];
|
|
65188
|
+
}
|
|
65189
|
+
const files = readdirSync(dir).filter((f) => f.endsWith(".ts") && f !== "index.ts" && !f.endsWith(".d.ts"));
|
|
65190
|
+
const items = [];
|
|
65191
|
+
for (const file of files) {
|
|
65192
|
+
const filePath = join5(dir, file);
|
|
65193
|
+
try {
|
|
65194
|
+
const module = await importUserFile(filePath);
|
|
65195
|
+
if (!Object.prototype.hasOwnProperty.call(module, "default") || !module.default) {
|
|
65196
|
+
continue;
|
|
65197
|
+
}
|
|
65198
|
+
const value = module.default;
|
|
65199
|
+
const config = typeof value.build === "function" ? value.build() : value;
|
|
65200
|
+
items.push(config);
|
|
65201
|
+
} catch (error) {
|
|
65202
|
+
throw new Error(`Failed to load ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
65203
|
+
}
|
|
65204
|
+
}
|
|
65205
|
+
return items;
|
|
64824
65206
|
}
|
|
64825
65207
|
async function loadTsDirectory(dir) {
|
|
64826
65208
|
if (!existsSync5(dir)) {
|
|
@@ -64946,6 +65328,7 @@ function getResourceDirectories(cwd) {
|
|
|
64946
65328
|
evals: join5(cwd, "evals"),
|
|
64947
65329
|
triggers: join5(cwd, "triggers"),
|
|
64948
65330
|
routers: join5(cwd, "routers"),
|
|
65331
|
+
workflows: join5(cwd, "workflows"),
|
|
64949
65332
|
fixtures: join5(cwd, "fixtures")
|
|
64950
65333
|
};
|
|
64951
65334
|
}
|
|
@@ -65094,6 +65477,7 @@ async function generateDocs(cwd, targets) {
|
|
|
65094
65477
|
evalSuites: [],
|
|
65095
65478
|
triggers: [],
|
|
65096
65479
|
routers: [],
|
|
65480
|
+
workflows: [],
|
|
65097
65481
|
fixtures: [],
|
|
65098
65482
|
errors: []
|
|
65099
65483
|
});
|
|
@@ -66328,6 +66712,18 @@ ${messages.join(`
|
|
|
66328
66712
|
inactivityResetMs: r.inactivityResetMs,
|
|
66329
66713
|
voiceConfig: r.voiceConfig
|
|
66330
66714
|
})) : undefined;
|
|
66715
|
+
const workflows = resources.workflows.length > 0 ? resources.workflows.map((w) => ({
|
|
66716
|
+
name: w.name,
|
|
66717
|
+
slug: w.slug,
|
|
66718
|
+
description: w.description,
|
|
66719
|
+
nodes: w.nodes.map((n) => ({
|
|
66720
|
+
id: n.id,
|
|
66721
|
+
name: n.name,
|
|
66722
|
+
type: n.type,
|
|
66723
|
+
params: n.params
|
|
66724
|
+
})),
|
|
66725
|
+
connections: w.connections
|
|
66726
|
+
})) : undefined;
|
|
66331
66727
|
const fixtures = resources.fixtures.length > 0 ? resources.fixtures.map((f) => ({
|
|
66332
66728
|
name: f.name,
|
|
66333
66729
|
slug: f.slug,
|
|
@@ -66344,7 +66740,7 @@ ${messages.join(`
|
|
|
66344
66740
|
metadata: r.metadata
|
|
66345
66741
|
}))
|
|
66346
66742
|
})) : undefined;
|
|
66347
|
-
return { agents, entityTypes, customViews, roles, tools, evalSuites, triggers, routers, fixtures };
|
|
66743
|
+
return { agents, entityTypes, customViews, roles, tools, evalSuites, triggers, routers, workflows, fixtures };
|
|
66348
66744
|
}
|
|
66349
66745
|
function extractAgentPayload(agent, customToolsMap) {
|
|
66350
66746
|
let systemPrompt;
|
|
@@ -66532,9 +66928,10 @@ function renderValidationErrors(errors) {
|
|
|
66532
66928
|
triggers: "trigger",
|
|
66533
66929
|
fixtures: "fixture",
|
|
66534
66930
|
routers: "router",
|
|
66931
|
+
workflows: "workflow",
|
|
66535
66932
|
tools: "tool"
|
|
66536
66933
|
};
|
|
66537
|
-
const order = ["agents", "entityTypes", "customViews", "roles", "evalSuites", "triggers", "fixtures", "routers", "tools"];
|
|
66934
|
+
const order = ["agents", "entityTypes", "customViews", "roles", "evalSuites", "triggers", "fixtures", "routers", "workflows", "tools"];
|
|
66538
66935
|
for (const key of order) {
|
|
66539
66936
|
const items = errors[key];
|
|
66540
66937
|
if (!items || items.length === 0)
|
|
@@ -66575,6 +66972,7 @@ ${resources.errors.join(`
|
|
|
66575
66972
|
roles: payload.roles,
|
|
66576
66973
|
triggers: payload.triggers,
|
|
66577
66974
|
routers: payload.routers,
|
|
66975
|
+
workflows: payload.workflows,
|
|
66578
66976
|
organizationId,
|
|
66579
66977
|
environment: "development"
|
|
66580
66978
|
});
|
|
@@ -66616,7 +67014,8 @@ async function checkForDeletions(resources, organizationId, environment) {
|
|
|
66616
67014
|
roles: new Set(payload.roles.map((r) => r.name)),
|
|
66617
67015
|
evalSuites: new Set((payload.evalSuites || []).map((es) => es.slug)),
|
|
66618
67016
|
triggers: new Set((payload.triggers || []).map((t2) => t2.slug)),
|
|
66619
|
-
routers: new Set((payload.routers || []).map((r) => r.slug))
|
|
67017
|
+
routers: new Set((payload.routers || []).map((r) => r.slug)),
|
|
67018
|
+
workflows: new Set((payload.workflows || []).map((w) => w.slug))
|
|
66620
67019
|
};
|
|
66621
67020
|
const deletions = [];
|
|
66622
67021
|
const deletedAgents = remoteState.agents.filter((a) => !localSlugs.agents.has(a.slug)).map((a) => a.name);
|
|
@@ -66644,6 +67043,10 @@ async function checkForDeletions(resources, organizationId, environment) {
|
|
|
66644
67043
|
const deletedRouters = remoteRouters.filter((r) => !localSlugs.routers.has(r.slug)).map((r) => r.name);
|
|
66645
67044
|
if (deletedRouters.length > 0)
|
|
66646
67045
|
deletions.push({ type: "Routers", remote: remoteRouters.length, local: (payload.routers || []).length, deleted: deletedRouters });
|
|
67046
|
+
const remoteWorkflows = remoteState.workflows || [];
|
|
67047
|
+
const deletedWorkflows = remoteWorkflows.filter((w) => !localSlugs.workflows.has(w.slug)).map((w) => w.name);
|
|
67048
|
+
if (deletedWorkflows.length > 0)
|
|
67049
|
+
deletions.push({ type: "Workflows", remote: remoteWorkflows.length, local: (payload.workflows || []).length, deleted: deletedWorkflows });
|
|
66647
67050
|
return deletions;
|
|
66648
67051
|
}
|
|
66649
67052
|
async function syncToEnvironment(cwd, organizationId, environment) {
|
|
@@ -66785,6 +67188,7 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
|
|
|
66785
67188
|
roles: payload.roles.map((r) => r.name),
|
|
66786
67189
|
triggers: (payload.triggers || []).map((t2) => t2.slug),
|
|
66787
67190
|
routers: (payload.routers || []).map((r) => r.slug),
|
|
67191
|
+
workflows: (payload.workflows || []).map((w) => w.slug),
|
|
66788
67192
|
deletions: deletions.map((d) => ({ type: d.type, names: d.deleted }))
|
|
66789
67193
|
}));
|
|
66790
67194
|
} else {
|
|
@@ -66796,6 +67200,7 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
|
|
|
66796
67200
|
console.log(chalk6.gray(" Roles:"), payload.roles.map((r) => r.name).join(", ") || "none");
|
|
66797
67201
|
console.log(chalk6.gray(" Triggers:"), (payload.triggers || []).map((t2) => t2.slug).join(", ") || "none");
|
|
66798
67202
|
console.log(chalk6.gray(" Routers:"), (payload.routers || []).map((r) => r.slug).join(", ") || "none");
|
|
67203
|
+
console.log(chalk6.gray(" Workflows:"), (payload.workflows || []).map((w) => w.slug).join(", ") || "none");
|
|
66799
67204
|
if (deletions.length > 0) {
|
|
66800
67205
|
console.log();
|
|
66801
67206
|
console.log(chalk6.yellow.bold(" Would delete:"));
|
|
@@ -66854,6 +67259,7 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
|
|
|
66854
67259
|
{ label: "role", data: result.roles },
|
|
66855
67260
|
{ label: "trigger", data: result.triggers },
|
|
66856
67261
|
{ label: "router", data: result.routers },
|
|
67262
|
+
{ label: "workflow", data: result.workflows },
|
|
66857
67263
|
{ label: "eval suite", data: result.evalSuites }
|
|
66858
67264
|
];
|
|
66859
67265
|
let hasChanges = false;
|
|
@@ -67072,6 +67478,7 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
|
|
|
67072
67478
|
dirs.evals,
|
|
67073
67479
|
dirs.triggers,
|
|
67074
67480
|
dirs.routers,
|
|
67481
|
+
dirs.workflows,
|
|
67075
67482
|
dirs.fixtures
|
|
67076
67483
|
].filter((p) => existsSync8(p));
|
|
67077
67484
|
const watcher = chokidar.watch(watchPaths, {
|
|
@@ -67649,7 +68056,7 @@ var whoamiCommand = new Command9("whoami").description("Show current logged in u
|
|
|
67649
68056
|
// src/cli/commands/add.ts
|
|
67650
68057
|
import { Command as Command10 } from "commander";
|
|
67651
68058
|
import chalk11 from "chalk";
|
|
67652
|
-
var addCommand = new Command10("add").description("Scaffold a new resource").argument("<type>", "Resource type: agent, data-type, view, role, eval, trigger, router, or fixture").argument("<name>", "Resource name").action(async (type, name) => {
|
|
68059
|
+
var addCommand = new Command10("add").description("Scaffold a new resource").argument("<type>", "Resource type: agent, data-type, view, role, eval, trigger, router, workflow, or fixture").argument("<name>", "Resource name").action(async (type, name) => {
|
|
67653
68060
|
const cwd = process.cwd();
|
|
67654
68061
|
console.log();
|
|
67655
68062
|
if (!hasProject(cwd)) {
|
|
@@ -67751,6 +68158,17 @@ var addCommand = new Command10("add").description("Scaffold a new resource").arg
|
|
|
67751
68158
|
console.log(chalk11.yellow("Router already exists:"), `routers/${slug}.ts`);
|
|
67752
68159
|
}
|
|
67753
68160
|
break;
|
|
68161
|
+
case "workflow":
|
|
68162
|
+
result = scaffoldWorkflow(cwd, displayName, slug);
|
|
68163
|
+
if (result.createdFiles.length > 0) {
|
|
68164
|
+
console.log(chalk11.green("✓"), `Created workflow "${displayName}"`);
|
|
68165
|
+
for (const file of result.createdFiles) {
|
|
68166
|
+
console.log(chalk11.gray(" →"), file);
|
|
68167
|
+
}
|
|
68168
|
+
} else {
|
|
68169
|
+
console.log(chalk11.yellow("Workflow already exists:"), `workflows/${slug}.ts`);
|
|
68170
|
+
}
|
|
68171
|
+
break;
|
|
67754
68172
|
case "fixture":
|
|
67755
68173
|
result = scaffoldFixture(cwd, displayName, slug);
|
|
67756
68174
|
if (result.createdFiles.length > 0) {
|
|
@@ -67775,6 +68193,7 @@ var addCommand = new Command10("add").description("Scaffold a new resource").arg
|
|
|
67775
68193
|
console.log(chalk11.gray(" -"), chalk11.cyan("eval"), "- Create an eval suite (YAML)");
|
|
67776
68194
|
console.log(chalk11.gray(" -"), chalk11.cyan("trigger"), "- Create a data trigger");
|
|
67777
68195
|
console.log(chalk11.gray(" -"), chalk11.cyan("router"), "- Create a message router");
|
|
68196
|
+
console.log(chalk11.gray(" -"), chalk11.cyan("workflow"), "- Create a graph-based workflow");
|
|
67778
68197
|
console.log(chalk11.gray(" -"), chalk11.cyan("fixture"), "- Create a test data fixture (YAML)");
|
|
67779
68198
|
console.log();
|
|
67780
68199
|
process.exit(1);
|
|
@@ -67922,6 +68341,7 @@ var statusCommand = new Command11("status").description("Compare local vs remote
|
|
|
67922
68341
|
const devToolNames = new Set((devState.tools || []).map((t2) => t2.name));
|
|
67923
68342
|
const localRouterSlugs = new Set(localResources.routers.map((r) => r.slug));
|
|
67924
68343
|
const devRouterSlugs = new Set((devState.routers || []).map((r) => r.slug));
|
|
68344
|
+
const localWorkflowSlugs = new Set(localResources.workflows.map((w) => w.slug));
|
|
67925
68345
|
if (opts.json) {
|
|
67926
68346
|
const classify = (localItems, remoteItems, useSlug) => {
|
|
67927
68347
|
const localKeys = new Set(localItems.map((i) => useSlug ? i.slug : i.name));
|
|
@@ -67938,7 +68358,8 @@ var statusCommand = new Command11("status").description("Compare local vs remote
|
|
|
67938
68358
|
entityTypes: classify(localResources.entityTypes, devState.entityTypes, true),
|
|
67939
68359
|
customViews: classify(localViews, devState.customViews || [], true),
|
|
67940
68360
|
roles: classify(localResources.roles, devState.roles, false),
|
|
67941
|
-
routers: classify(localResources.routers, devState.routers || [], true)
|
|
68361
|
+
routers: classify(localResources.routers, devState.routers || [], true),
|
|
68362
|
+
workflows: classify(localResources.workflows, devState.workflows || [], true)
|
|
67942
68363
|
}));
|
|
67943
68364
|
return;
|
|
67944
68365
|
}
|
|
@@ -68055,6 +68476,26 @@ var statusCommand = new Command11("status").description("Compare local vs remote
|
|
|
68055
68476
|
}
|
|
68056
68477
|
}
|
|
68057
68478
|
console.log();
|
|
68479
|
+
console.log(chalk12.bold("Workflows"));
|
|
68480
|
+
console.log(chalk12.gray("─".repeat(60)));
|
|
68481
|
+
if (localResources.workflows.length === 0 && (devState.workflows || []).length === 0) {
|
|
68482
|
+
console.log(chalk12.gray(" No workflows"));
|
|
68483
|
+
} else {
|
|
68484
|
+
for (const wf of localResources.workflows) {
|
|
68485
|
+
const remote = (devState.workflows || []).find((r) => r.slug === wf.slug);
|
|
68486
|
+
if (remote) {
|
|
68487
|
+
console.log(` ${chalk12.green("●")} ${chalk12.cyan(wf.name)} (${wf.slug}) - ${wf.nodes.length} nodes`);
|
|
68488
|
+
} else {
|
|
68489
|
+
console.log(` ${chalk12.blue("+")} ${chalk12.cyan(wf.name)} (${wf.slug}) - ${chalk12.blue("new")}`);
|
|
68490
|
+
}
|
|
68491
|
+
}
|
|
68492
|
+
for (const remote of devState.workflows || []) {
|
|
68493
|
+
if (!localWorkflowSlugs.has(remote.slug)) {
|
|
68494
|
+
console.log(` ${chalk12.red("-")} ${remote.name} (${remote.slug}) - ${chalk12.red("will be deleted")}`);
|
|
68495
|
+
}
|
|
68496
|
+
}
|
|
68497
|
+
}
|
|
68498
|
+
console.log();
|
|
68058
68499
|
console.log(chalk12.bold("Custom Tools"));
|
|
68059
68500
|
console.log(chalk12.gray("─".repeat(60)));
|
|
68060
68501
|
if (localResources.customTools.length === 0 && (devState.tools || []).length === 0) {
|
|
@@ -68396,6 +68837,24 @@ ${parts.join(`,
|
|
|
68396
68837
|
})
|
|
68397
68838
|
`;
|
|
68398
68839
|
}
|
|
68840
|
+
function generateWorkflowFile(wf) {
|
|
68841
|
+
const parts = [
|
|
68842
|
+
` name: ${JSON.stringify(wf.name)}`,
|
|
68843
|
+
` slug: ${JSON.stringify(wf.slug)}`
|
|
68844
|
+
];
|
|
68845
|
+
if (wf.description) {
|
|
68846
|
+
parts.push(` description: ${JSON.stringify(wf.description)}`);
|
|
68847
|
+
}
|
|
68848
|
+
parts.push(` nodes: ${stringifyValue(wf.nodes, 2)}`);
|
|
68849
|
+
parts.push(` connections: ${stringifyValue(wf.connections, 2)}`);
|
|
68850
|
+
return `import { defineWorkflow } from 'struere'
|
|
68851
|
+
|
|
68852
|
+
export default defineWorkflow({
|
|
68853
|
+
${parts.join(`,
|
|
68854
|
+
`)},
|
|
68855
|
+
})
|
|
68856
|
+
`;
|
|
68857
|
+
}
|
|
68399
68858
|
function generateIndexFile(type, slugs) {
|
|
68400
68859
|
if (slugs.length === 0) {
|
|
68401
68860
|
return "";
|
|
@@ -68516,6 +68975,7 @@ var pullCommand = new Command12("pull").description("Pull remote resources to lo
|
|
|
68516
68975
|
ensureDir2(join9(cwd, "roles"));
|
|
68517
68976
|
ensureDir2(join9(cwd, "triggers"));
|
|
68518
68977
|
ensureDir2(join9(cwd, "routers"));
|
|
68978
|
+
ensureDir2(join9(cwd, "workflows"));
|
|
68519
68979
|
ensureDir2(join9(cwd, "tools"));
|
|
68520
68980
|
const agentSlugs = [];
|
|
68521
68981
|
for (const agent of state.agents) {
|
|
@@ -68560,6 +69020,12 @@ var pullCommand = new Command12("pull").description("Pull remote resources to lo
|
|
|
68560
69020
|
const content = generateRouterFile(router);
|
|
68561
69021
|
writeOrSkip(`routers/${router.slug}.ts`, content);
|
|
68562
69022
|
}
|
|
69023
|
+
const workflowSlugs = [];
|
|
69024
|
+
for (const wf of state.workflows || []) {
|
|
69025
|
+
workflowSlugs.push(wf.slug);
|
|
69026
|
+
const content = generateWorkflowFile(wf);
|
|
69027
|
+
writeOrSkip(`workflows/${wf.slug}.ts`, content);
|
|
69028
|
+
}
|
|
68563
69029
|
const customTools = state.tools || [];
|
|
68564
69030
|
if (customTools.length > 0) {
|
|
68565
69031
|
const content = generateToolsFile(customTools);
|
|
@@ -68590,6 +69056,11 @@ var pullCommand = new Command12("pull").description("Pull remote resources to lo
|
|
|
68590
69056
|
if (content)
|
|
68591
69057
|
writeOrSkip("routers/index.ts", content);
|
|
68592
69058
|
}
|
|
69059
|
+
if (workflowSlugs.length > 0) {
|
|
69060
|
+
const content = generateIndexFile("workflows", workflowSlugs);
|
|
69061
|
+
if (content)
|
|
69062
|
+
writeOrSkip("workflows/index.ts", content);
|
|
69063
|
+
}
|
|
68593
69064
|
await installSkill(cwd);
|
|
68594
69065
|
if (options.json) {
|
|
68595
69066
|
console.log(JSON.stringify({
|
|
@@ -72000,13 +72471,16 @@ triggersCommand.command("fire <slug>").description("Manually fire a trigger").op
|
|
|
72000
72471
|
}
|
|
72001
72472
|
});
|
|
72002
72473
|
|
|
72003
|
-
// src/cli/commands/
|
|
72474
|
+
// src/cli/commands/workflows.ts
|
|
72004
72475
|
init_credentials();
|
|
72005
72476
|
import { Command as Command19 } from "commander";
|
|
72006
72477
|
import chalk21 from "chalk";
|
|
72007
72478
|
import ora15 from "ora";
|
|
72479
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
72480
|
+
import { join as join11 } from "path";
|
|
72481
|
+
init_convex();
|
|
72008
72482
|
|
|
72009
|
-
// src/cli/utils/
|
|
72483
|
+
// src/cli/utils/workflows.ts
|
|
72010
72484
|
init_credentials();
|
|
72011
72485
|
init_config();
|
|
72012
72486
|
init_convex();
|
|
@@ -72018,49 +72492,15 @@ function getToken4() {
|
|
|
72018
72492
|
throw new Error("Not authenticated");
|
|
72019
72493
|
return token;
|
|
72020
72494
|
}
|
|
72021
|
-
async function
|
|
72022
|
-
const
|
|
72023
|
-
const
|
|
72024
|
-
if (apiKey && !credentials?.token) {
|
|
72025
|
-
const siteUrl2 = getSiteUrl();
|
|
72026
|
-
const response2 = await fetch(`${siteUrl2}${path}`, {
|
|
72027
|
-
method: "POST",
|
|
72028
|
-
headers: {
|
|
72029
|
-
"Content-Type": "application/json",
|
|
72030
|
-
Authorization: `Bearer ${apiKey}`
|
|
72031
|
-
},
|
|
72032
|
-
body: JSON.stringify(body),
|
|
72033
|
-
signal: AbortSignal.timeout(30000)
|
|
72034
|
-
});
|
|
72035
|
-
const text2 = await response2.text();
|
|
72036
|
-
let json2;
|
|
72037
|
-
try {
|
|
72038
|
-
json2 = JSON.parse(text2);
|
|
72039
|
-
} catch {
|
|
72040
|
-
throw new Error(text2 || `HTTP ${response2.status}`);
|
|
72041
|
-
}
|
|
72042
|
-
if (!response2.ok) {
|
|
72043
|
-
throw new Error(json2.error || text2);
|
|
72044
|
-
}
|
|
72045
|
-
return json2;
|
|
72046
|
-
}
|
|
72047
|
-
if (credentials?.sessionId) {
|
|
72048
|
-
await refreshToken();
|
|
72049
|
-
}
|
|
72050
|
-
const freshCredentials = loadCredentials();
|
|
72051
|
-
const token = apiKey || freshCredentials?.token;
|
|
72052
|
-
if (!token) {
|
|
72053
|
-
throw new Error("Not authenticated");
|
|
72054
|
-
}
|
|
72055
|
-
const siteUrl = getSiteUrl();
|
|
72056
|
-
const response = await fetch(`${siteUrl}${path}`, {
|
|
72495
|
+
async function convexQuery7(path, args) {
|
|
72496
|
+
const token = getToken4();
|
|
72497
|
+
const response = await fetch(`${CONVEX_URL}/api/query`, {
|
|
72057
72498
|
method: "POST",
|
|
72058
72499
|
headers: {
|
|
72059
72500
|
"Content-Type": "application/json",
|
|
72060
72501
|
Authorization: `Bearer ${token}`
|
|
72061
72502
|
},
|
|
72062
|
-
body: JSON.stringify(
|
|
72063
|
-
signal: AbortSignal.timeout(30000)
|
|
72503
|
+
body: JSON.stringify({ path, args })
|
|
72064
72504
|
});
|
|
72065
72505
|
const text = await response.text();
|
|
72066
72506
|
let json;
|
|
@@ -72070,13 +72510,999 @@ async function threadsApiCall(path, body) {
|
|
|
72070
72510
|
throw new Error(text || `HTTP ${response.status}`);
|
|
72071
72511
|
}
|
|
72072
72512
|
if (!response.ok) {
|
|
72073
|
-
throw new Error(json.
|
|
72513
|
+
throw new Error(json.errorData?.message || json.errorMessage || text);
|
|
72074
72514
|
}
|
|
72075
|
-
|
|
72515
|
+
if (json.status === "error") {
|
|
72516
|
+
throw new Error(json.errorMessage || "Unknown error from Convex");
|
|
72517
|
+
}
|
|
72518
|
+
return json.value;
|
|
72076
72519
|
}
|
|
72077
|
-
async function
|
|
72520
|
+
async function convexMutation6(path, args) {
|
|
72078
72521
|
const token = getToken4();
|
|
72079
|
-
const response = await fetch(`${CONVEX_URL}/api/
|
|
72522
|
+
const response = await fetch(`${CONVEX_URL}/api/mutation`, {
|
|
72523
|
+
method: "POST",
|
|
72524
|
+
headers: {
|
|
72525
|
+
"Content-Type": "application/json",
|
|
72526
|
+
Authorization: `Bearer ${token}`
|
|
72527
|
+
},
|
|
72528
|
+
body: JSON.stringify({ path, args })
|
|
72529
|
+
});
|
|
72530
|
+
const text = await response.text();
|
|
72531
|
+
let json;
|
|
72532
|
+
try {
|
|
72533
|
+
json = JSON.parse(text);
|
|
72534
|
+
} catch {
|
|
72535
|
+
throw new Error(text || `HTTP ${response.status}`);
|
|
72536
|
+
}
|
|
72537
|
+
if (!response.ok) {
|
|
72538
|
+
throw new Error(json.errorData?.message || json.errorMessage || text);
|
|
72539
|
+
}
|
|
72540
|
+
if (json.status === "error") {
|
|
72541
|
+
throw new Error(json.errorMessage || "Unknown error from Convex");
|
|
72542
|
+
}
|
|
72543
|
+
return json.value;
|
|
72544
|
+
}
|
|
72545
|
+
async function listWorkflows(environment, organizationId) {
|
|
72546
|
+
return convexQuery7("workflows:list", { environment, ...organizationId && { organizationId } });
|
|
72547
|
+
}
|
|
72548
|
+
async function getWorkflow(slug, environment, organizationId) {
|
|
72549
|
+
return convexQuery7("workflows:getBySlug", { slug, environment, ...organizationId && { organizationId } });
|
|
72550
|
+
}
|
|
72551
|
+
async function listWorkflowRuns(options) {
|
|
72552
|
+
return convexQuery7("workflows:listRuns", {
|
|
72553
|
+
environment: options.environment,
|
|
72554
|
+
...options.status && { status: options.status },
|
|
72555
|
+
...options.workflowSlug && { workflowSlug: options.workflowSlug },
|
|
72556
|
+
...options.limit && { limit: options.limit },
|
|
72557
|
+
...options.organizationId && { organizationId: options.organizationId }
|
|
72558
|
+
});
|
|
72559
|
+
}
|
|
72560
|
+
async function getWorkflowRunDetail(runId, environment, organizationId) {
|
|
72561
|
+
return convexQuery7("workflows:getRunDetail", {
|
|
72562
|
+
runId,
|
|
72563
|
+
...environment && { environment },
|
|
72564
|
+
...organizationId && { organizationId }
|
|
72565
|
+
});
|
|
72566
|
+
}
|
|
72567
|
+
async function getWorkflowRunStats(environment, organizationId) {
|
|
72568
|
+
return convexQuery7("workflows:getRunStats", { environment, ...organizationId && { organizationId } });
|
|
72569
|
+
}
|
|
72570
|
+
async function getLastRunStatuses2(environment, organizationId) {
|
|
72571
|
+
return convexQuery7("workflows:getLastRunStatuses", { environment, ...organizationId && { organizationId } });
|
|
72572
|
+
}
|
|
72573
|
+
async function cancelWorkflowRun(runId, environment, organizationId) {
|
|
72574
|
+
return convexMutation6("workflows:cancelRun", { runId, environment, ...organizationId && { organizationId } });
|
|
72575
|
+
}
|
|
72576
|
+
async function retryWorkflowRun(runId, environment, organizationId) {
|
|
72577
|
+
return convexMutation6("workflows:retryRun", { runId, environment, ...organizationId && { organizationId } });
|
|
72578
|
+
}
|
|
72579
|
+
async function toggleWorkflow(slug, enabled, environment, organizationId) {
|
|
72580
|
+
const path = enabled ? "workflows:enable" : "workflows:disable";
|
|
72581
|
+
return convexMutation6(path, { slug, environment, ...organizationId && { organizationId } });
|
|
72582
|
+
}
|
|
72583
|
+
async function listWorkflowExecutions(options) {
|
|
72584
|
+
return convexQuery7("workflows:listExecutions", {
|
|
72585
|
+
environment: options.environment,
|
|
72586
|
+
...options.workflowSlug && { workflowSlug: options.workflowSlug },
|
|
72587
|
+
...options.limit && { limit: options.limit },
|
|
72588
|
+
...options.organizationId && { organizationId: options.organizationId }
|
|
72589
|
+
});
|
|
72590
|
+
}
|
|
72591
|
+
async function getWorkflowExecutionDetail(runId, environment, organizationId) {
|
|
72592
|
+
return convexQuery7("workflows:getExecutionDetail", { runId, ...environment && { environment }, ...organizationId && { organizationId } });
|
|
72593
|
+
}
|
|
72594
|
+
function rewriteExpression(value) {
|
|
72595
|
+
return value.replace(/\{\{\s*steps\.([a-zA-Z0-9_-]+)\.([^}]+?)\s*\}\}/g, (_m, as, path) => `{{ $node["${as}"].json.${String(path).trim()} }}`).replace(/\{\{\s*trigger\.data\.([^}]+?)\s*\}\}/g, (_m, path) => `{{ $trigger.data.${String(path).trim()} }}`).replace(/\{\{\s*trigger\.entityId\s*\}\}/g, "{{ $trigger.entityId }}").replace(/\{\{\s*trigger\.entityType\s*\}\}/g, "{{ $trigger.entityType }}");
|
|
72596
|
+
}
|
|
72597
|
+
function rewriteValue(value) {
|
|
72598
|
+
if (typeof value === "string")
|
|
72599
|
+
return rewriteExpression(value);
|
|
72600
|
+
if (Array.isArray(value))
|
|
72601
|
+
return value.map(rewriteValue);
|
|
72602
|
+
if (value && typeof value === "object") {
|
|
72603
|
+
const out = {};
|
|
72604
|
+
for (const [k, v] of Object.entries(value)) {
|
|
72605
|
+
out[k] = rewriteValue(v);
|
|
72606
|
+
}
|
|
72607
|
+
return out;
|
|
72608
|
+
}
|
|
72609
|
+
return value;
|
|
72610
|
+
}
|
|
72611
|
+
function stringifyArgs(value, indent) {
|
|
72612
|
+
if (value === null || value === undefined)
|
|
72613
|
+
return "undefined";
|
|
72614
|
+
if (typeof value === "string")
|
|
72615
|
+
return JSON.stringify(value);
|
|
72616
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
72617
|
+
return String(value);
|
|
72618
|
+
if (Array.isArray(value)) {
|
|
72619
|
+
if (value.length === 0)
|
|
72620
|
+
return "[]";
|
|
72621
|
+
const items = value.map((item) => `${" ".repeat(indent + 2)}${stringifyArgs(item, indent + 2)}`);
|
|
72622
|
+
return `[
|
|
72623
|
+
${items.join(`,
|
|
72624
|
+
`)},
|
|
72625
|
+
${" ".repeat(indent)}]`;
|
|
72626
|
+
}
|
|
72627
|
+
if (typeof value === "object") {
|
|
72628
|
+
const entries = Object.entries(value).filter(([, v]) => v !== undefined);
|
|
72629
|
+
if (entries.length === 0)
|
|
72630
|
+
return "{}";
|
|
72631
|
+
const lines = entries.map(([key, val]) => {
|
|
72632
|
+
const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
72633
|
+
return `${" ".repeat(indent + 2)}${safeKey}: ${stringifyArgs(val, indent + 2)}`;
|
|
72634
|
+
});
|
|
72635
|
+
return `{
|
|
72636
|
+
${lines.join(`,
|
|
72637
|
+
`)},
|
|
72638
|
+
${" ".repeat(indent)}}`;
|
|
72639
|
+
}
|
|
72640
|
+
return JSON.stringify(value);
|
|
72641
|
+
}
|
|
72642
|
+
function convertTriggerToWorkflowSource(trigger) {
|
|
72643
|
+
const varName = `wf_${trigger.slug.replace(/-([a-z0-9])/g, (_m, c) => c.toUpperCase())}`;
|
|
72644
|
+
const triggerLines = [];
|
|
72645
|
+
if (trigger.cronSchedule) {
|
|
72646
|
+
const cronArgs = [` schedule: ${JSON.stringify(trigger.cronSchedule)}`];
|
|
72647
|
+
if (trigger.cronTimezone)
|
|
72648
|
+
cronArgs.push(` timezone: ${JSON.stringify(trigger.cronTimezone)}`);
|
|
72649
|
+
triggerLines.push(` .onCron({
|
|
72650
|
+
${cronArgs.join(`,
|
|
72651
|
+
`)},
|
|
72652
|
+
})`);
|
|
72653
|
+
} else {
|
|
72654
|
+
const entityArgs = [
|
|
72655
|
+
` entityType: ${JSON.stringify(trigger.entityType ?? "")}`,
|
|
72656
|
+
` action: ${JSON.stringify(trigger.action ?? "created")}`
|
|
72657
|
+
];
|
|
72658
|
+
if (trigger.condition && Object.keys(trigger.condition).length > 0) {
|
|
72659
|
+
entityArgs.push(` match: ${stringifyArgs(rewriteValue(trigger.condition), 4)}`);
|
|
72660
|
+
}
|
|
72661
|
+
triggerLines.push(` .onEntity({
|
|
72662
|
+
${entityArgs.join(`,
|
|
72663
|
+
`)},
|
|
72664
|
+
})`);
|
|
72665
|
+
}
|
|
72666
|
+
const stepLines = [];
|
|
72667
|
+
for (const action of trigger.actions) {
|
|
72668
|
+
const name = action.as ?? action.tool;
|
|
72669
|
+
const args = stringifyArgs(rewriteValue(action.args), 4);
|
|
72670
|
+
if (action.tool === "agent.chat") {
|
|
72671
|
+
const agentArgs = action.args ?? {};
|
|
72672
|
+
const agentSlug = agentArgs.agent ?? agentArgs.agentSlug ?? "";
|
|
72673
|
+
const message = typeof agentArgs.message === "string" ? rewriteExpression(agentArgs.message) : "";
|
|
72674
|
+
stepLines.push(` .agent({
|
|
72675
|
+
agent: ${JSON.stringify(agentSlug)},
|
|
72676
|
+
message: ${JSON.stringify(message)},
|
|
72677
|
+
name: ${JSON.stringify(name)},
|
|
72678
|
+
})`);
|
|
72679
|
+
} else {
|
|
72680
|
+
stepLines.push(` .tool(${JSON.stringify(action.tool)}, ${args}, { name: ${JSON.stringify(name)} })`);
|
|
72681
|
+
}
|
|
72682
|
+
}
|
|
72683
|
+
const metaLines = [
|
|
72684
|
+
` name: ${JSON.stringify(trigger.name)}`,
|
|
72685
|
+
` slug: ${JSON.stringify(trigger.slug)}`
|
|
72686
|
+
];
|
|
72687
|
+
if (trigger.description)
|
|
72688
|
+
metaLines.push(` description: ${JSON.stringify(trigger.description)}`);
|
|
72689
|
+
return `import { workflow } from 'struere'
|
|
72690
|
+
|
|
72691
|
+
const ${varName} = workflow({
|
|
72692
|
+
${metaLines.join(`,
|
|
72693
|
+
`)},
|
|
72694
|
+
})
|
|
72695
|
+
|
|
72696
|
+
${varName}
|
|
72697
|
+
${[...triggerLines, ...stepLines].join(`
|
|
72698
|
+
`)}
|
|
72699
|
+
|
|
72700
|
+
export default ${varName}.build()
|
|
72701
|
+
`;
|
|
72702
|
+
}
|
|
72703
|
+
|
|
72704
|
+
// src/cli/commands/workflows.ts
|
|
72705
|
+
function getOrgId4() {
|
|
72706
|
+
const project = loadProject(process.cwd());
|
|
72707
|
+
return project?.organization.id;
|
|
72708
|
+
}
|
|
72709
|
+
async function withWorkflowAuthRetry(fn) {
|
|
72710
|
+
try {
|
|
72711
|
+
return await fn();
|
|
72712
|
+
} catch (err) {
|
|
72713
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
72714
|
+
if (msg.includes("Unauthenticated") || msg.includes("OIDC") || msg.includes("token") || msg.includes("expired")) {
|
|
72715
|
+
const refreshed = await refreshToken();
|
|
72716
|
+
if (!refreshed)
|
|
72717
|
+
throw err;
|
|
72718
|
+
return fn();
|
|
72719
|
+
}
|
|
72720
|
+
throw err;
|
|
72721
|
+
}
|
|
72722
|
+
}
|
|
72723
|
+
async function ensureAuth6() {
|
|
72724
|
+
const cwd = process.cwd();
|
|
72725
|
+
const nonInteractive = !isInteractive();
|
|
72726
|
+
if (!hasProject(cwd)) {
|
|
72727
|
+
if (nonInteractive) {
|
|
72728
|
+
console.error(chalk21.red("No struere.json found. Run struere init first."));
|
|
72729
|
+
process.exit(1);
|
|
72730
|
+
}
|
|
72731
|
+
console.log(chalk21.yellow("No struere.json found - initializing project..."));
|
|
72732
|
+
console.log();
|
|
72733
|
+
const success = await runInit(cwd);
|
|
72734
|
+
if (!success) {
|
|
72735
|
+
process.exit(1);
|
|
72736
|
+
}
|
|
72737
|
+
console.log();
|
|
72738
|
+
}
|
|
72739
|
+
let credentials = loadCredentials();
|
|
72740
|
+
const apiKey = getApiKey();
|
|
72741
|
+
if (!credentials && !apiKey) {
|
|
72742
|
+
if (nonInteractive) {
|
|
72743
|
+
console.error(chalk21.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
|
|
72744
|
+
process.exit(1);
|
|
72745
|
+
}
|
|
72746
|
+
console.log(chalk21.yellow("Not logged in - authenticating..."));
|
|
72747
|
+
console.log();
|
|
72748
|
+
credentials = await performLogin();
|
|
72749
|
+
if (!credentials) {
|
|
72750
|
+
console.log(chalk21.red("Authentication failed"));
|
|
72751
|
+
process.exit(1);
|
|
72752
|
+
}
|
|
72753
|
+
console.log();
|
|
72754
|
+
}
|
|
72755
|
+
return true;
|
|
72756
|
+
}
|
|
72757
|
+
function relativeTime3(ts) {
|
|
72758
|
+
const diff = Date.now() - ts;
|
|
72759
|
+
const seconds = Math.floor(diff / 1000);
|
|
72760
|
+
if (seconds < 60)
|
|
72761
|
+
return `${seconds}s ago`;
|
|
72762
|
+
const minutes = Math.floor(seconds / 60);
|
|
72763
|
+
if (minutes < 60)
|
|
72764
|
+
return `${minutes}m ago`;
|
|
72765
|
+
const hours = Math.floor(minutes / 60);
|
|
72766
|
+
if (hours < 24)
|
|
72767
|
+
return `${hours}h ago`;
|
|
72768
|
+
const days = Math.floor(hours / 24);
|
|
72769
|
+
return `${days}d ago`;
|
|
72770
|
+
}
|
|
72771
|
+
function statusColor5(status) {
|
|
72772
|
+
switch (status) {
|
|
72773
|
+
case "completed":
|
|
72774
|
+
case "success":
|
|
72775
|
+
return chalk21.green(status);
|
|
72776
|
+
case "completed_with_errors":
|
|
72777
|
+
return chalk21.yellow(status);
|
|
72778
|
+
case "pending":
|
|
72779
|
+
return chalk21.yellow(status);
|
|
72780
|
+
case "running":
|
|
72781
|
+
return chalk21.cyan(status);
|
|
72782
|
+
case "failed":
|
|
72783
|
+
case "dead":
|
|
72784
|
+
return chalk21.red(status);
|
|
72785
|
+
default:
|
|
72786
|
+
return chalk21.gray(status);
|
|
72787
|
+
}
|
|
72788
|
+
}
|
|
72789
|
+
function triggerLabel(wf) {
|
|
72790
|
+
if (wf.cronSchedule)
|
|
72791
|
+
return `cron ${wf.cronSchedule}`;
|
|
72792
|
+
if (wf.triggerType === "trigger.manual")
|
|
72793
|
+
return "manual";
|
|
72794
|
+
if (wf.triggerEntityType)
|
|
72795
|
+
return `${wf.triggerEntityType}.${wf.triggerAction ?? ""}`;
|
|
72796
|
+
return wf.triggerType ?? "-";
|
|
72797
|
+
}
|
|
72798
|
+
function renderNodeRuns(nodeRuns, verbose) {
|
|
72799
|
+
console.log();
|
|
72800
|
+
console.log(chalk21.bold("Node Runs"));
|
|
72801
|
+
console.log(chalk21.gray("─".repeat(60)));
|
|
72802
|
+
nodeRuns.forEach((node, i) => {
|
|
72803
|
+
const status = node.status === "success" || node.status === "completed" ? chalk21.green(node.status) : statusColor5(node.status);
|
|
72804
|
+
const label = node.nodeName ?? node.nodeId;
|
|
72805
|
+
const type = node.nodeType ? chalk21.gray(` (${node.nodeType})`) : "";
|
|
72806
|
+
const out = node.outputItemCount ? chalk21.gray(` → ${node.outputItemCount.join("/")} items`) : "";
|
|
72807
|
+
const ms = node.durationMs !== undefined ? ` (${node.durationMs}ms)` : "";
|
|
72808
|
+
console.log(` Node ${i + 1}: ${chalk21.cyan(label)}${type} → ${status}${out}${ms}`);
|
|
72809
|
+
if (node.attempts !== undefined && node.attempts > 1) {
|
|
72810
|
+
console.log(` ${chalk21.gray("Attempts:")} ${node.attempts}`);
|
|
72811
|
+
}
|
|
72812
|
+
if (node.errorMessage) {
|
|
72813
|
+
const msg = verbose ? node.errorMessage : node.errorMessage.slice(0, 120);
|
|
72814
|
+
console.log(` ${chalk21.red("Error:")} ${node.errorType ? chalk21.red(`[${node.errorType}] `) : ""}${msg}`);
|
|
72815
|
+
}
|
|
72816
|
+
console.log();
|
|
72817
|
+
});
|
|
72818
|
+
}
|
|
72819
|
+
var workflowsCommand = new Command19("workflows").description("Manage workflows and workflow runs");
|
|
72820
|
+
workflowsCommand.command("list", { isDefault: true }).description("List all workflows").option("--env <environment>", "Environment (development|production|eval)", "development").option("--json", "Output raw JSON").option("--failed", "Show only workflows with recent failures").action(async (opts) => {
|
|
72821
|
+
await ensureAuth6();
|
|
72822
|
+
const spinner = opts.json ? null : ora15();
|
|
72823
|
+
try {
|
|
72824
|
+
const orgId = getOrgId4();
|
|
72825
|
+
spinner?.start("Fetching workflows");
|
|
72826
|
+
const [workflows, statuses] = await Promise.all([
|
|
72827
|
+
listWorkflows(opts.env, orgId),
|
|
72828
|
+
getLastRunStatuses2(opts.env, orgId)
|
|
72829
|
+
]);
|
|
72830
|
+
let filtered = workflows;
|
|
72831
|
+
if (opts.failed) {
|
|
72832
|
+
filtered = workflows.filter((w) => {
|
|
72833
|
+
const s = statuses[w.slug]?.status;
|
|
72834
|
+
return s === "failed" || s === "dead" || s === "completed_with_errors";
|
|
72835
|
+
});
|
|
72836
|
+
}
|
|
72837
|
+
spinner?.succeed(`Found ${filtered.length} workflows${opts.failed ? " (failed only)" : ""}`);
|
|
72838
|
+
if (opts.json) {
|
|
72839
|
+
console.log(JSON.stringify(filtered, null, 2));
|
|
72840
|
+
return;
|
|
72841
|
+
}
|
|
72842
|
+
console.log();
|
|
72843
|
+
renderTable([
|
|
72844
|
+
{ key: "name", label: "Name", width: 20 },
|
|
72845
|
+
{ key: "slug", label: "Slug", width: 18 },
|
|
72846
|
+
{ key: "trigger", label: "Trigger", width: 18 },
|
|
72847
|
+
{ key: "nodes", label: "Nodes", width: 6 },
|
|
72848
|
+
{ key: "enabled", label: "Enabled", width: 8 },
|
|
72849
|
+
{ key: "lastRun", label: "Last Run", width: 18 },
|
|
72850
|
+
{ key: "updated", label: "Updated", width: 10 }
|
|
72851
|
+
], filtered.map((w) => ({
|
|
72852
|
+
name: w.name ?? "",
|
|
72853
|
+
slug: w.slug ?? "",
|
|
72854
|
+
trigger: triggerLabel(w),
|
|
72855
|
+
nodes: String(w.nodes?.length ?? 0),
|
|
72856
|
+
enabled: w.enabled ? chalk21.green("✓") : chalk21.red("✗"),
|
|
72857
|
+
lastRun: statuses[w.slug] ? statusColor5(statuses[w.slug].status) : chalk21.gray("-"),
|
|
72858
|
+
updated: relativeTime3(w.updatedAt ?? Date.now())
|
|
72859
|
+
})));
|
|
72860
|
+
console.log();
|
|
72861
|
+
} catch (err) {
|
|
72862
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
72863
|
+
spinner?.fail("Failed to fetch workflows");
|
|
72864
|
+
if (opts.json) {
|
|
72865
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
72866
|
+
} else {
|
|
72867
|
+
console.log(chalk21.red("Error:"), message);
|
|
72868
|
+
}
|
|
72869
|
+
process.exit(1);
|
|
72870
|
+
}
|
|
72871
|
+
});
|
|
72872
|
+
workflowsCommand.command("get <slug>").description("View workflow details").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").action(async (slug, opts) => {
|
|
72873
|
+
await ensureAuth6();
|
|
72874
|
+
const spinner = opts.json ? null : ora15();
|
|
72875
|
+
try {
|
|
72876
|
+
spinner?.start("Fetching workflow");
|
|
72877
|
+
const workflow = await getWorkflow(slug, opts.env, getOrgId4());
|
|
72878
|
+
if (!workflow) {
|
|
72879
|
+
spinner?.fail("Workflow not found");
|
|
72880
|
+
if (opts.json) {
|
|
72881
|
+
console.log(JSON.stringify({ success: false, error: "Workflow not found" }));
|
|
72882
|
+
}
|
|
72883
|
+
process.exit(1);
|
|
72884
|
+
}
|
|
72885
|
+
spinner?.succeed("Workflow loaded");
|
|
72886
|
+
if (opts.json) {
|
|
72887
|
+
console.log(JSON.stringify(workflow, null, 2));
|
|
72888
|
+
return;
|
|
72889
|
+
}
|
|
72890
|
+
console.log();
|
|
72891
|
+
console.log(` ${chalk21.gray("Name:")} ${chalk21.cyan(workflow.name)}`);
|
|
72892
|
+
console.log(` ${chalk21.gray("Slug:")} ${chalk21.cyan(workflow.slug)}`);
|
|
72893
|
+
console.log(` ${chalk21.gray("Description:")} ${chalk21.cyan(workflow.description || "-")}`);
|
|
72894
|
+
console.log(` ${chalk21.gray("Trigger:")} ${chalk21.cyan(triggerLabel(workflow))}`);
|
|
72895
|
+
if (workflow.cronTimezone) {
|
|
72896
|
+
console.log(` ${chalk21.gray("Timezone:")} ${chalk21.cyan(workflow.cronTimezone)}`);
|
|
72897
|
+
}
|
|
72898
|
+
console.log(` ${chalk21.gray("Enabled:")} ${workflow.enabled ? chalk21.green("✓") : chalk21.red("✗")}`);
|
|
72899
|
+
console.log(` ${chalk21.gray("Created:")} ${chalk21.cyan(relativeTime3(workflow.createdAt ?? Date.now()))}`);
|
|
72900
|
+
console.log(` ${chalk21.gray("Updated:")} ${chalk21.cyan(relativeTime3(workflow.updatedAt ?? Date.now()))}`);
|
|
72901
|
+
if (workflow.nodes?.length) {
|
|
72902
|
+
console.log();
|
|
72903
|
+
console.log(` ${chalk21.gray("Nodes:")}`);
|
|
72904
|
+
for (const node of workflow.nodes) {
|
|
72905
|
+
const targets = workflow.connections?.[node.id]?.main ?? [];
|
|
72906
|
+
const edges = targets.map((port, i) => port.length ? `port${i}→${port.map((t2) => {
|
|
72907
|
+
const target = workflow.nodes.find((n) => n.id === t2.node);
|
|
72908
|
+
return target?.name ?? t2.node;
|
|
72909
|
+
}).join(", ")}` : null).filter(Boolean).join(" | ");
|
|
72910
|
+
console.log(` ${chalk21.cyan(node.name)} ${chalk21.gray(`[${node.type}]`)}${edges ? chalk21.gray(` ${edges}`) : ""}`);
|
|
72911
|
+
}
|
|
72912
|
+
}
|
|
72913
|
+
console.log();
|
|
72914
|
+
} catch (err) {
|
|
72915
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
72916
|
+
spinner?.fail("Failed to fetch workflow");
|
|
72917
|
+
if (opts.json) {
|
|
72918
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
72919
|
+
} else {
|
|
72920
|
+
console.log(chalk21.red("Error:"), message);
|
|
72921
|
+
}
|
|
72922
|
+
process.exit(1);
|
|
72923
|
+
}
|
|
72924
|
+
});
|
|
72925
|
+
workflowsCommand.command("runs [slug]").description("List workflow runs").option("--env <environment>", "Environment", "development").option("--status <status>", "Filter by status (pending|running|completed|completed_with_errors|failed|dead)").option("--limit <n>", "Maximum results", "20").option("--json", "Output raw JSON").action(async (slug, opts) => {
|
|
72926
|
+
await ensureAuth6();
|
|
72927
|
+
const spinner = opts.json ? null : ora15();
|
|
72928
|
+
try {
|
|
72929
|
+
spinner?.start("Fetching runs");
|
|
72930
|
+
const runs = await listWorkflowRuns({
|
|
72931
|
+
environment: opts.env,
|
|
72932
|
+
status: opts.status,
|
|
72933
|
+
workflowSlug: slug,
|
|
72934
|
+
limit: parseInt(opts.limit, 10),
|
|
72935
|
+
organizationId: getOrgId4()
|
|
72936
|
+
});
|
|
72937
|
+
spinner?.succeed(`Found ${runs.length} runs`);
|
|
72938
|
+
if (opts.json) {
|
|
72939
|
+
console.log(JSON.stringify(runs, null, 2));
|
|
72940
|
+
return;
|
|
72941
|
+
}
|
|
72942
|
+
console.log();
|
|
72943
|
+
renderTable([
|
|
72944
|
+
{ key: "id", label: "ID", width: 40 },
|
|
72945
|
+
{ key: "workflow", label: "Workflow", width: 16 },
|
|
72946
|
+
{ key: "status", label: "Status", width: 20 },
|
|
72947
|
+
{ key: "dedupe", label: "Dedupe", width: 14 },
|
|
72948
|
+
{ key: "error", label: "Error", width: 30 },
|
|
72949
|
+
{ key: "time", label: "Time", width: 10 }
|
|
72950
|
+
], runs.map((r) => ({
|
|
72951
|
+
id: r._id ?? "",
|
|
72952
|
+
workflow: r.workflowSlug ?? "",
|
|
72953
|
+
status: statusColor5(r.status),
|
|
72954
|
+
dedupe: r.dedupeKey ? r.dedupeKey.slice(-12) : "",
|
|
72955
|
+
error: r.errorMessage ?? "",
|
|
72956
|
+
time: relativeTime3(r.createdAt ?? Date.now())
|
|
72957
|
+
})));
|
|
72958
|
+
console.log();
|
|
72959
|
+
} catch (err) {
|
|
72960
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
72961
|
+
spinner?.fail("Failed to fetch runs");
|
|
72962
|
+
if (opts.json) {
|
|
72963
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
72964
|
+
} else {
|
|
72965
|
+
console.log(chalk21.red("Error:"), message);
|
|
72966
|
+
}
|
|
72967
|
+
process.exit(1);
|
|
72968
|
+
}
|
|
72969
|
+
});
|
|
72970
|
+
workflowsCommand.command("run <run-id>").description("View workflow run details").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("-v, --verbose", "Show full error messages").action(async (runId, opts) => {
|
|
72971
|
+
await ensureAuth6();
|
|
72972
|
+
const spinner = opts.json ? null : ora15();
|
|
72973
|
+
try {
|
|
72974
|
+
spinner?.start("Fetching run");
|
|
72975
|
+
const run = await getWorkflowRunDetail(runId, opts.env, getOrgId4());
|
|
72976
|
+
if (!run) {
|
|
72977
|
+
spinner?.fail("Run not found");
|
|
72978
|
+
if (opts.json) {
|
|
72979
|
+
console.log(JSON.stringify({ success: false, error: "Run not found" }));
|
|
72980
|
+
}
|
|
72981
|
+
process.exit(1);
|
|
72982
|
+
}
|
|
72983
|
+
spinner?.succeed("Run loaded");
|
|
72984
|
+
if (opts.json) {
|
|
72985
|
+
console.log(JSON.stringify(run, null, 2));
|
|
72986
|
+
return;
|
|
72987
|
+
}
|
|
72988
|
+
console.log();
|
|
72989
|
+
console.log(` ${chalk21.gray("Workflow:")} ${chalk21.cyan(run.workflowSlug)}`);
|
|
72990
|
+
console.log(` ${chalk21.gray("Status:")} ${statusColor5(run.status)}`);
|
|
72991
|
+
if (run.dedupeKey)
|
|
72992
|
+
console.log(` ${chalk21.gray("Dedupe:")} ${chalk21.cyan(run.dedupeKey)}`);
|
|
72993
|
+
console.log(` ${chalk21.gray("Started:")} ${chalk21.cyan(run.startedAt ? new Date(run.startedAt).toISOString() : "-")}`);
|
|
72994
|
+
console.log(` ${chalk21.gray("Completed:")} ${chalk21.cyan(run.completedAt ? new Date(run.completedAt).toISOString() : "-")}`);
|
|
72995
|
+
if (run.errorMessage) {
|
|
72996
|
+
console.log(` ${chalk21.gray("Error:")} ${chalk21.red(run.errorMessage)}`);
|
|
72997
|
+
}
|
|
72998
|
+
if (run.nodeRuns?.length) {
|
|
72999
|
+
renderNodeRuns(run.nodeRuns, opts.verbose);
|
|
73000
|
+
}
|
|
73001
|
+
console.log();
|
|
73002
|
+
} catch (err) {
|
|
73003
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73004
|
+
spinner?.fail("Failed to fetch run");
|
|
73005
|
+
if (opts.json) {
|
|
73006
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
73007
|
+
} else {
|
|
73008
|
+
console.log(chalk21.red("Error:"), message);
|
|
73009
|
+
}
|
|
73010
|
+
process.exit(1);
|
|
73011
|
+
}
|
|
73012
|
+
});
|
|
73013
|
+
workflowsCommand.command("stats").description("Show workflow run statistics").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").action(async (opts) => {
|
|
73014
|
+
await ensureAuth6();
|
|
73015
|
+
const spinner = opts.json ? null : ora15();
|
|
73016
|
+
try {
|
|
73017
|
+
const orgId = getOrgId4();
|
|
73018
|
+
spinner?.start("Fetching statistics");
|
|
73019
|
+
const stats = await getWorkflowRunStats(opts.env, orgId);
|
|
73020
|
+
spinner?.succeed("Run statistics");
|
|
73021
|
+
if (opts.json) {
|
|
73022
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
73023
|
+
return;
|
|
73024
|
+
}
|
|
73025
|
+
console.log();
|
|
73026
|
+
console.log(chalk21.bold(` Run Statistics (${opts.env})`));
|
|
73027
|
+
console.log();
|
|
73028
|
+
const statuses = [
|
|
73029
|
+
{ key: "pending", color: chalk21.yellow },
|
|
73030
|
+
{ key: "running", color: chalk21.cyan },
|
|
73031
|
+
{ key: "completed", color: chalk21.green },
|
|
73032
|
+
{ key: "completed_with_errors", color: chalk21.yellow },
|
|
73033
|
+
{ key: "failed", color: chalk21.red },
|
|
73034
|
+
{ key: "dead", color: chalk21.gray }
|
|
73035
|
+
];
|
|
73036
|
+
let total = 0;
|
|
73037
|
+
for (const { key, color } of statuses) {
|
|
73038
|
+
const count = stats[key] ?? 0;
|
|
73039
|
+
total += count;
|
|
73040
|
+
console.log(` ${color(key.padEnd(22))} ${String(count).padStart(6)}`);
|
|
73041
|
+
}
|
|
73042
|
+
console.log(` ${chalk21.gray("─".repeat(28))}`);
|
|
73043
|
+
console.log(` ${chalk21.bold("Total".padEnd(22))} ${chalk21.bold(String(total).padStart(6))}`);
|
|
73044
|
+
console.log();
|
|
73045
|
+
} catch (err) {
|
|
73046
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73047
|
+
spinner?.fail("Failed to fetch statistics");
|
|
73048
|
+
if (opts.json) {
|
|
73049
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
73050
|
+
} else {
|
|
73051
|
+
console.log(chalk21.red("Error:"), message);
|
|
73052
|
+
}
|
|
73053
|
+
process.exit(1);
|
|
73054
|
+
}
|
|
73055
|
+
});
|
|
73056
|
+
workflowsCommand.command("logs [slug]").description("View workflow execution history").option("--env <environment>", "Environment", "development").option("--limit <n>", "Maximum results", "10").option("--json", "Output raw JSON").option("-v, --verbose", "Show full error messages").action(async (slug, opts) => {
|
|
73057
|
+
await ensureAuth6();
|
|
73058
|
+
const spinner = opts.json ? null : ora15();
|
|
73059
|
+
try {
|
|
73060
|
+
spinner?.start("Fetching execution logs");
|
|
73061
|
+
const executions = await withWorkflowAuthRetry(() => listWorkflowExecutions({
|
|
73062
|
+
environment: opts.env,
|
|
73063
|
+
workflowSlug: slug,
|
|
73064
|
+
limit: parseInt(opts.limit, 10),
|
|
73065
|
+
organizationId: getOrgId4()
|
|
73066
|
+
}));
|
|
73067
|
+
spinner?.succeed(`Found ${executions.length} executions`);
|
|
73068
|
+
if (opts.json) {
|
|
73069
|
+
console.log(JSON.stringify(executions, null, 2));
|
|
73070
|
+
return;
|
|
73071
|
+
}
|
|
73072
|
+
console.log();
|
|
73073
|
+
renderTable([
|
|
73074
|
+
{ key: "id", label: "ID", width: 40 },
|
|
73075
|
+
{ key: "workflow", label: "Workflow", width: 16 },
|
|
73076
|
+
{ key: "status", label: "Status", width: 20 },
|
|
73077
|
+
{ key: "time", label: "Time", width: 10 }
|
|
73078
|
+
], executions.map((e) => ({
|
|
73079
|
+
id: e._id ?? "",
|
|
73080
|
+
workflow: e.workflowSlug ?? "",
|
|
73081
|
+
status: statusColor5(e.status ?? "pending"),
|
|
73082
|
+
time: relativeTime3(e.startedAt ?? e._creationTime ?? Date.now())
|
|
73083
|
+
})));
|
|
73084
|
+
console.log();
|
|
73085
|
+
} catch (err) {
|
|
73086
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73087
|
+
spinner?.fail("Failed to fetch execution logs");
|
|
73088
|
+
if (opts.json) {
|
|
73089
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
73090
|
+
} else {
|
|
73091
|
+
console.log(chalk21.red("Error:"), message);
|
|
73092
|
+
}
|
|
73093
|
+
process.exit(1);
|
|
73094
|
+
}
|
|
73095
|
+
});
|
|
73096
|
+
workflowsCommand.command("log <identifier>").description("View detailed workflow execution log (by event ID or workflow slug)").option("--env <environment>", "Environment", "development").option("--nth <n>", "Show nth most recent execution (when using slug)", "1").option("--json", "Output raw JSON").option("-v, --verbose", "Show full error messages").action(async (identifier, opts) => {
|
|
73097
|
+
await ensureAuth6();
|
|
73098
|
+
const spinner = opts.json ? null : ora15();
|
|
73099
|
+
try {
|
|
73100
|
+
const orgId = getOrgId4();
|
|
73101
|
+
const nth = parseInt(opts.nth, 10);
|
|
73102
|
+
if (isNaN(nth) || nth < 1) {
|
|
73103
|
+
console.error(chalk21.red("Error:"), "--nth must be a positive integer (e.g., --nth 1 for most recent)");
|
|
73104
|
+
process.exit(1);
|
|
73105
|
+
}
|
|
73106
|
+
let eventId = identifier;
|
|
73107
|
+
const isConvexId = /^[0-9a-zA-Z]{20,}$/.test(identifier);
|
|
73108
|
+
if (!isConvexId) {
|
|
73109
|
+
spinner?.start("Resolving workflow slug to latest execution");
|
|
73110
|
+
const executions = await withWorkflowAuthRetry(() => listWorkflowExecutions({
|
|
73111
|
+
environment: opts.env,
|
|
73112
|
+
workflowSlug: identifier,
|
|
73113
|
+
limit: nth,
|
|
73114
|
+
organizationId: orgId
|
|
73115
|
+
}));
|
|
73116
|
+
if (!executions.length) {
|
|
73117
|
+
spinner?.fail(`No executions found for workflow "${identifier}" in ${opts.env}`);
|
|
73118
|
+
if (opts.json) {
|
|
73119
|
+
console.log(JSON.stringify({ success: false, error: `No executions found for workflow "${identifier}"` }));
|
|
73120
|
+
}
|
|
73121
|
+
process.exit(1);
|
|
73122
|
+
}
|
|
73123
|
+
const idx = nth - 1;
|
|
73124
|
+
if (idx >= executions.length) {
|
|
73125
|
+
spinner?.fail(`Only ${executions.length} executions found, cannot get #${nth}`);
|
|
73126
|
+
process.exit(1);
|
|
73127
|
+
}
|
|
73128
|
+
eventId = executions[idx]._id;
|
|
73129
|
+
spinner?.succeed(`Found execution for "${identifier}"`);
|
|
73130
|
+
}
|
|
73131
|
+
spinner?.start("Fetching execution detail");
|
|
73132
|
+
const event = await withWorkflowAuthRetry(() => getWorkflowExecutionDetail(eventId, opts.env, orgId));
|
|
73133
|
+
if (!event) {
|
|
73134
|
+
spinner?.fail("Execution not found");
|
|
73135
|
+
if (opts.json) {
|
|
73136
|
+
console.log(JSON.stringify({ success: false, error: "Execution not found" }));
|
|
73137
|
+
}
|
|
73138
|
+
process.exit(1);
|
|
73139
|
+
}
|
|
73140
|
+
spinner?.succeed("Execution loaded");
|
|
73141
|
+
if (opts.json) {
|
|
73142
|
+
console.log(JSON.stringify(event, null, 2));
|
|
73143
|
+
return;
|
|
73144
|
+
}
|
|
73145
|
+
console.log();
|
|
73146
|
+
console.log(` ${chalk21.gray("Workflow:")} ${chalk21.cyan(event.workflowSlug ?? "")}`);
|
|
73147
|
+
console.log(` ${chalk21.gray("Status:")} ${statusColor5(event.status ?? "pending")}`);
|
|
73148
|
+
console.log(` ${chalk21.gray("Timestamp:")} ${chalk21.cyan(new Date(event.startedAt ?? event._creationTime).toISOString())}`);
|
|
73149
|
+
if (event.nodeRuns?.length) {
|
|
73150
|
+
renderNodeRuns(event.nodeRuns, opts.verbose);
|
|
73151
|
+
}
|
|
73152
|
+
const triggerData = event.triggerItem?.json;
|
|
73153
|
+
if (triggerData) {
|
|
73154
|
+
console.log(chalk21.bold("Trigger Data"));
|
|
73155
|
+
console.log(chalk21.gray("─".repeat(60)));
|
|
73156
|
+
console.log(` ${JSON.stringify(triggerData, null, 2).split(`
|
|
73157
|
+
`).join(`
|
|
73158
|
+
`)}`);
|
|
73159
|
+
console.log();
|
|
73160
|
+
}
|
|
73161
|
+
} catch (err) {
|
|
73162
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73163
|
+
spinner?.fail("Failed to fetch execution detail");
|
|
73164
|
+
if (opts.json) {
|
|
73165
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
73166
|
+
} else {
|
|
73167
|
+
console.log(chalk21.red("Error:"), message);
|
|
73168
|
+
}
|
|
73169
|
+
process.exit(1);
|
|
73170
|
+
}
|
|
73171
|
+
});
|
|
73172
|
+
workflowsCommand.command("retry <run-id>").description("Retry a failed or dead run").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (runId, opts) => {
|
|
73173
|
+
await ensureAuth6();
|
|
73174
|
+
const spinner = opts.json ? null : ora15();
|
|
73175
|
+
const environment = opts.env;
|
|
73176
|
+
if (environment === "production" && !opts.confirm && isInteractive()) {
|
|
73177
|
+
const readline = await import("readline");
|
|
73178
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
73179
|
+
await new Promise((resolve) => {
|
|
73180
|
+
rl.question(chalk21.yellow(`WARNING: Retrying run in PRODUCTION environment.
|
|
73181
|
+
Press Enter to continue or Ctrl+C to cancel: `), resolve);
|
|
73182
|
+
});
|
|
73183
|
+
rl.close();
|
|
73184
|
+
}
|
|
73185
|
+
try {
|
|
73186
|
+
spinner?.start("Retrying run...");
|
|
73187
|
+
await retryWorkflowRun(runId, environment, getOrgId4());
|
|
73188
|
+
spinner?.succeed(chalk21.green(`Run ${runId} queued for retry`));
|
|
73189
|
+
if (opts.json) {
|
|
73190
|
+
console.log(JSON.stringify({ success: true, runId }));
|
|
73191
|
+
}
|
|
73192
|
+
} catch (err) {
|
|
73193
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73194
|
+
spinner?.fail("Failed to retry run");
|
|
73195
|
+
if (opts.json) {
|
|
73196
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
73197
|
+
} else {
|
|
73198
|
+
console.log(chalk21.red("Error:"), message);
|
|
73199
|
+
}
|
|
73200
|
+
process.exit(1);
|
|
73201
|
+
}
|
|
73202
|
+
});
|
|
73203
|
+
workflowsCommand.command("cancel <run-id>").description("Cancel a pending or running run").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (runId, opts) => {
|
|
73204
|
+
await ensureAuth6();
|
|
73205
|
+
const spinner = opts.json ? null : ora15();
|
|
73206
|
+
const environment = opts.env;
|
|
73207
|
+
if (environment === "production" && !opts.confirm && isInteractive()) {
|
|
73208
|
+
const readline = await import("readline");
|
|
73209
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
73210
|
+
await new Promise((resolve) => {
|
|
73211
|
+
rl.question(chalk21.yellow(`WARNING: Cancelling run in PRODUCTION environment.
|
|
73212
|
+
Press Enter to continue or Ctrl+C to cancel: `), resolve);
|
|
73213
|
+
});
|
|
73214
|
+
rl.close();
|
|
73215
|
+
}
|
|
73216
|
+
try {
|
|
73217
|
+
spinner?.start("Cancelling run...");
|
|
73218
|
+
await cancelWorkflowRun(runId, environment, getOrgId4());
|
|
73219
|
+
spinner?.succeed(chalk21.green(`Run ${runId} cancelled`));
|
|
73220
|
+
if (opts.json) {
|
|
73221
|
+
console.log(JSON.stringify({ success: true, runId }));
|
|
73222
|
+
}
|
|
73223
|
+
} catch (err) {
|
|
73224
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73225
|
+
spinner?.fail("Failed to cancel run");
|
|
73226
|
+
if (opts.json) {
|
|
73227
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
73228
|
+
} else {
|
|
73229
|
+
console.log(chalk21.red("Error:"), message);
|
|
73230
|
+
}
|
|
73231
|
+
process.exit(1);
|
|
73232
|
+
}
|
|
73233
|
+
});
|
|
73234
|
+
workflowsCommand.command("enable <slug>").description("Enable a workflow").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (slug, opts) => {
|
|
73235
|
+
await ensureAuth6();
|
|
73236
|
+
const spinner = opts.json ? null : ora15();
|
|
73237
|
+
const environment = opts.env;
|
|
73238
|
+
if (environment === "production" && !opts.confirm && isInteractive()) {
|
|
73239
|
+
const readline = await import("readline");
|
|
73240
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
73241
|
+
await new Promise((resolve) => {
|
|
73242
|
+
rl.question(chalk21.yellow(`WARNING: Enabling workflow in PRODUCTION environment.
|
|
73243
|
+
Press Enter to continue or Ctrl+C to cancel: `), resolve);
|
|
73244
|
+
});
|
|
73245
|
+
rl.close();
|
|
73246
|
+
}
|
|
73247
|
+
try {
|
|
73248
|
+
spinner?.start("Enabling workflow...");
|
|
73249
|
+
await toggleWorkflow(slug, true, environment, getOrgId4());
|
|
73250
|
+
spinner?.succeed(chalk21.green(`Workflow ${slug} enabled`));
|
|
73251
|
+
if (opts.json) {
|
|
73252
|
+
console.log(JSON.stringify({ success: true, slug }));
|
|
73253
|
+
}
|
|
73254
|
+
} catch (err) {
|
|
73255
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73256
|
+
spinner?.fail("Failed to enable workflow");
|
|
73257
|
+
if (opts.json) {
|
|
73258
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
73259
|
+
} else {
|
|
73260
|
+
console.log(chalk21.red("Error:"), message);
|
|
73261
|
+
}
|
|
73262
|
+
process.exit(1);
|
|
73263
|
+
}
|
|
73264
|
+
});
|
|
73265
|
+
workflowsCommand.command("disable <slug>").description("Disable a workflow").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (slug, opts) => {
|
|
73266
|
+
await ensureAuth6();
|
|
73267
|
+
const spinner = opts.json ? null : ora15();
|
|
73268
|
+
const environment = opts.env;
|
|
73269
|
+
if (environment === "production" && !opts.confirm && isInteractive()) {
|
|
73270
|
+
const readline = await import("readline");
|
|
73271
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
73272
|
+
await new Promise((resolve) => {
|
|
73273
|
+
rl.question(chalk21.yellow(`WARNING: Disabling workflow in PRODUCTION environment.
|
|
73274
|
+
Press Enter to continue or Ctrl+C to cancel: `), resolve);
|
|
73275
|
+
});
|
|
73276
|
+
rl.close();
|
|
73277
|
+
}
|
|
73278
|
+
try {
|
|
73279
|
+
spinner?.start("Disabling workflow...");
|
|
73280
|
+
await toggleWorkflow(slug, false, environment, getOrgId4());
|
|
73281
|
+
spinner?.succeed(chalk21.green(`Workflow ${slug} disabled`));
|
|
73282
|
+
if (opts.json) {
|
|
73283
|
+
console.log(JSON.stringify({ success: true, slug }));
|
|
73284
|
+
}
|
|
73285
|
+
} catch (err) {
|
|
73286
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73287
|
+
spinner?.fail("Failed to disable workflow");
|
|
73288
|
+
if (opts.json) {
|
|
73289
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
73290
|
+
} else {
|
|
73291
|
+
console.log(chalk21.red("Error:"), message);
|
|
73292
|
+
}
|
|
73293
|
+
process.exit(1);
|
|
73294
|
+
}
|
|
73295
|
+
});
|
|
73296
|
+
workflowsCommand.command("fire <slug>").description("Manually fire a workflow (starts a real run)").option("--env <environment>", "Environment (development|production|eval)", "development").option("--data <json>", "JSON data for the trigger item").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (slug, opts) => {
|
|
73297
|
+
await ensureAuth6();
|
|
73298
|
+
const spinner = opts.json ? null : ora15();
|
|
73299
|
+
const environment = opts.env;
|
|
73300
|
+
if (environment === "production" && !opts.confirm && isInteractive()) {
|
|
73301
|
+
const readline = await import("readline");
|
|
73302
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
73303
|
+
await new Promise((resolve) => {
|
|
73304
|
+
rl.question(chalk21.yellow(`WARNING: Firing workflow in PRODUCTION environment.
|
|
73305
|
+
Press Enter to continue or Ctrl+C to cancel: `), resolve);
|
|
73306
|
+
});
|
|
73307
|
+
rl.close();
|
|
73308
|
+
}
|
|
73309
|
+
let data;
|
|
73310
|
+
if (opts.data) {
|
|
73311
|
+
try {
|
|
73312
|
+
data = JSON.parse(opts.data);
|
|
73313
|
+
} catch {
|
|
73314
|
+
console.log(chalk21.red("Error: --data must be valid JSON"));
|
|
73315
|
+
process.exit(1);
|
|
73316
|
+
}
|
|
73317
|
+
}
|
|
73318
|
+
try {
|
|
73319
|
+
spinner?.start(`Firing workflow ${chalk21.cyan(slug)}...`);
|
|
73320
|
+
const { result, error } = await fireWorkflow({
|
|
73321
|
+
slug,
|
|
73322
|
+
environment,
|
|
73323
|
+
data,
|
|
73324
|
+
organizationId: getOrgId4()
|
|
73325
|
+
});
|
|
73326
|
+
if (error) {
|
|
73327
|
+
spinner?.fail("Workflow fire failed");
|
|
73328
|
+
if (opts.json) {
|
|
73329
|
+
console.log(JSON.stringify({ success: false, error }));
|
|
73330
|
+
} else {
|
|
73331
|
+
console.log(chalk21.red("Error:"), error);
|
|
73332
|
+
}
|
|
73333
|
+
process.exit(1);
|
|
73334
|
+
}
|
|
73335
|
+
if (!result) {
|
|
73336
|
+
spinner?.fail("No result returned");
|
|
73337
|
+
process.exit(1);
|
|
73338
|
+
}
|
|
73339
|
+
if (result.success) {
|
|
73340
|
+
spinner?.succeed(chalk21.green(`Workflow ${chalk21.cyan(slug)} run started`));
|
|
73341
|
+
} else {
|
|
73342
|
+
spinner?.fail(chalk21.red(`Workflow ${chalk21.cyan(slug)} fire failed`));
|
|
73343
|
+
}
|
|
73344
|
+
if (opts.json) {
|
|
73345
|
+
console.log(JSON.stringify(result, null, 2));
|
|
73346
|
+
return;
|
|
73347
|
+
}
|
|
73348
|
+
console.log();
|
|
73349
|
+
console.log(` ${chalk21.gray("Workflow:")} ${chalk21.cyan(result.workflow.name)} (${result.workflow.slug})`);
|
|
73350
|
+
console.log(` ${chalk21.gray("Environment:")} ${chalk21.cyan(result.environment)}`);
|
|
73351
|
+
if (result.runId)
|
|
73352
|
+
console.log(` ${chalk21.gray("Run ID:")} ${chalk21.cyan(result.runId)}`);
|
|
73353
|
+
if (result.status)
|
|
73354
|
+
console.log(` ${chalk21.gray("Status:")} ${statusColor5(result.status)}`);
|
|
73355
|
+
if (result.error)
|
|
73356
|
+
console.log(` ${chalk21.gray("Error:")} ${chalk21.red(result.error)}`);
|
|
73357
|
+
if (result.runId) {
|
|
73358
|
+
console.log();
|
|
73359
|
+
console.log(chalk21.gray(" Inspect with"), chalk21.cyan(`struere workflows run ${result.runId} --env ${result.environment}`));
|
|
73360
|
+
}
|
|
73361
|
+
console.log();
|
|
73362
|
+
if (!result.success) {
|
|
73363
|
+
process.exit(1);
|
|
73364
|
+
}
|
|
73365
|
+
} catch (err) {
|
|
73366
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73367
|
+
spinner?.fail("Failed to fire workflow");
|
|
73368
|
+
if (opts.json) {
|
|
73369
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
73370
|
+
} else {
|
|
73371
|
+
console.log(chalk21.red("Error:"), message);
|
|
73372
|
+
}
|
|
73373
|
+
process.exit(1);
|
|
73374
|
+
}
|
|
73375
|
+
});
|
|
73376
|
+
workflowsCommand.command("import-trigger <slug>").description("Generate a workflow file from a legacy trigger").option("--env <environment>", "Environment to read the trigger from", "development").option("--force", "Overwrite an existing workflow file").option("--json", "Output raw JSON").action(async (slug, opts) => {
|
|
73377
|
+
await ensureAuth6();
|
|
73378
|
+
const spinner = opts.json ? null : ora15();
|
|
73379
|
+
const cwd = process.cwd();
|
|
73380
|
+
try {
|
|
73381
|
+
spinner?.start(`Fetching trigger ${chalk21.cyan(slug)}`);
|
|
73382
|
+
const trigger = await withWorkflowAuthRetry(() => getTrigger(slug, opts.env, getOrgId4()));
|
|
73383
|
+
if (!trigger) {
|
|
73384
|
+
spinner?.fail("Trigger not found");
|
|
73385
|
+
if (opts.json) {
|
|
73386
|
+
console.log(JSON.stringify({ success: false, error: `Trigger not found: ${slug}` }));
|
|
73387
|
+
}
|
|
73388
|
+
process.exit(1);
|
|
73389
|
+
}
|
|
73390
|
+
const source = convertTriggerToWorkflowSource(trigger);
|
|
73391
|
+
const workflowsDir = join11(cwd, "workflows");
|
|
73392
|
+
if (!existsSync10(workflowsDir)) {
|
|
73393
|
+
mkdirSync8(workflowsDir, { recursive: true });
|
|
73394
|
+
}
|
|
73395
|
+
const relativePath = `workflows/${trigger.slug}.ts`;
|
|
73396
|
+
const filePath = join11(cwd, relativePath);
|
|
73397
|
+
if (existsSync10(filePath) && !opts.force) {
|
|
73398
|
+
spinner?.fail("Workflow file already exists");
|
|
73399
|
+
if (opts.json) {
|
|
73400
|
+
console.log(JSON.stringify({ success: false, error: `${relativePath} already exists (use --force to overwrite)` }));
|
|
73401
|
+
} else {
|
|
73402
|
+
console.log(chalk21.yellow(`File already exists:`), relativePath);
|
|
73403
|
+
console.log(chalk21.gray("Use --force to overwrite."));
|
|
73404
|
+
}
|
|
73405
|
+
process.exit(1);
|
|
73406
|
+
}
|
|
73407
|
+
writeFileSync9(filePath, source);
|
|
73408
|
+
spinner?.succeed(`Created ${relativePath}`);
|
|
73409
|
+
if (opts.json) {
|
|
73410
|
+
console.log(JSON.stringify({ success: true, file: relativePath }));
|
|
73411
|
+
return;
|
|
73412
|
+
}
|
|
73413
|
+
console.log();
|
|
73414
|
+
console.log(chalk21.gray(" Review the generated file, then run"), chalk21.cyan("struere sync"), chalk21.gray("to sync it."));
|
|
73415
|
+
console.log(chalk21.gray(" The source trigger is left intact and can be disabled with"), chalk21.cyan(`struere triggers disable ${trigger.slug}`));
|
|
73416
|
+
console.log();
|
|
73417
|
+
} catch (err) {
|
|
73418
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73419
|
+
spinner?.fail("Failed to import trigger");
|
|
73420
|
+
if (opts.json) {
|
|
73421
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
73422
|
+
} else {
|
|
73423
|
+
console.log(chalk21.red("Error:"), message);
|
|
73424
|
+
}
|
|
73425
|
+
process.exit(1);
|
|
73426
|
+
}
|
|
73427
|
+
});
|
|
73428
|
+
|
|
73429
|
+
// src/cli/commands/threads.ts
|
|
73430
|
+
init_credentials();
|
|
73431
|
+
import { Command as Command20 } from "commander";
|
|
73432
|
+
import chalk22 from "chalk";
|
|
73433
|
+
import ora16 from "ora";
|
|
73434
|
+
|
|
73435
|
+
// src/cli/utils/threads.ts
|
|
73436
|
+
init_credentials();
|
|
73437
|
+
init_config();
|
|
73438
|
+
init_convex();
|
|
73439
|
+
function getToken5() {
|
|
73440
|
+
const credentials = loadCredentials();
|
|
73441
|
+
const apiKey = getApiKey();
|
|
73442
|
+
const token = apiKey || credentials?.token;
|
|
73443
|
+
if (!token)
|
|
73444
|
+
throw new Error("Not authenticated");
|
|
73445
|
+
return token;
|
|
73446
|
+
}
|
|
73447
|
+
async function threadsApiCall(path, body) {
|
|
73448
|
+
const credentials = loadCredentials();
|
|
73449
|
+
const apiKey = getApiKey();
|
|
73450
|
+
if (apiKey && !credentials?.token) {
|
|
73451
|
+
const siteUrl2 = getSiteUrl();
|
|
73452
|
+
const response2 = await fetch(`${siteUrl2}${path}`, {
|
|
73453
|
+
method: "POST",
|
|
73454
|
+
headers: {
|
|
73455
|
+
"Content-Type": "application/json",
|
|
73456
|
+
Authorization: `Bearer ${apiKey}`
|
|
73457
|
+
},
|
|
73458
|
+
body: JSON.stringify(body),
|
|
73459
|
+
signal: AbortSignal.timeout(30000)
|
|
73460
|
+
});
|
|
73461
|
+
const text2 = await response2.text();
|
|
73462
|
+
let json2;
|
|
73463
|
+
try {
|
|
73464
|
+
json2 = JSON.parse(text2);
|
|
73465
|
+
} catch {
|
|
73466
|
+
throw new Error(text2 || `HTTP ${response2.status}`);
|
|
73467
|
+
}
|
|
73468
|
+
if (!response2.ok) {
|
|
73469
|
+
throw new Error(json2.error || text2);
|
|
73470
|
+
}
|
|
73471
|
+
return json2;
|
|
73472
|
+
}
|
|
73473
|
+
if (credentials?.sessionId) {
|
|
73474
|
+
await refreshToken();
|
|
73475
|
+
}
|
|
73476
|
+
const freshCredentials = loadCredentials();
|
|
73477
|
+
const token = apiKey || freshCredentials?.token;
|
|
73478
|
+
if (!token) {
|
|
73479
|
+
throw new Error("Not authenticated");
|
|
73480
|
+
}
|
|
73481
|
+
const siteUrl = getSiteUrl();
|
|
73482
|
+
const response = await fetch(`${siteUrl}${path}`, {
|
|
73483
|
+
method: "POST",
|
|
73484
|
+
headers: {
|
|
73485
|
+
"Content-Type": "application/json",
|
|
73486
|
+
Authorization: `Bearer ${token}`
|
|
73487
|
+
},
|
|
73488
|
+
body: JSON.stringify(body),
|
|
73489
|
+
signal: AbortSignal.timeout(30000)
|
|
73490
|
+
});
|
|
73491
|
+
const text = await response.text();
|
|
73492
|
+
let json;
|
|
73493
|
+
try {
|
|
73494
|
+
json = JSON.parse(text);
|
|
73495
|
+
} catch {
|
|
73496
|
+
throw new Error(text || `HTTP ${response.status}`);
|
|
73497
|
+
}
|
|
73498
|
+
if (!response.ok) {
|
|
73499
|
+
throw new Error(json.error || text);
|
|
73500
|
+
}
|
|
73501
|
+
return json;
|
|
73502
|
+
}
|
|
73503
|
+
async function convexQuery8(path, args) {
|
|
73504
|
+
const token = getToken5();
|
|
73505
|
+
const response = await fetch(`${CONVEX_URL}/api/query`, {
|
|
72080
73506
|
method: "POST",
|
|
72081
73507
|
headers: {
|
|
72082
73508
|
"Content-Type": "application/json",
|
|
@@ -72109,7 +73535,7 @@ async function listThreads(options) {
|
|
|
72109
73535
|
});
|
|
72110
73536
|
return result.data;
|
|
72111
73537
|
}
|
|
72112
|
-
return
|
|
73538
|
+
return convexQuery8("threads:listWithPreviews", {
|
|
72113
73539
|
environment: options.environment,
|
|
72114
73540
|
channel: options.channel,
|
|
72115
73541
|
limit: options.limit,
|
|
@@ -72117,7 +73543,7 @@ async function listThreads(options) {
|
|
|
72117
73543
|
});
|
|
72118
73544
|
}
|
|
72119
73545
|
async function getThreadWithMessages(options) {
|
|
72120
|
-
return
|
|
73546
|
+
return convexQuery8("threads:getWithMessages", {
|
|
72121
73547
|
id: options.threadId,
|
|
72122
73548
|
...options.organizationId && { organizationId: options.organizationId }
|
|
72123
73549
|
});
|
|
@@ -72130,7 +73556,7 @@ async function archiveThread(options) {
|
|
|
72130
73556
|
threadId: options.threadId
|
|
72131
73557
|
});
|
|
72132
73558
|
}
|
|
72133
|
-
const token =
|
|
73559
|
+
const token = getToken5();
|
|
72134
73560
|
const siteUrl = getSiteUrl();
|
|
72135
73561
|
const response = await fetch(`${siteUrl}/v1/threads/archive`, {
|
|
72136
73562
|
method: "POST",
|
|
@@ -72172,15 +73598,15 @@ async function findThreadByPhone(options) {
|
|
|
72172
73598
|
}
|
|
72173
73599
|
|
|
72174
73600
|
// src/cli/commands/threads.ts
|
|
72175
|
-
async function
|
|
73601
|
+
async function ensureAuth7() {
|
|
72176
73602
|
const cwd = process.cwd();
|
|
72177
73603
|
const nonInteractive = !isInteractive();
|
|
72178
73604
|
if (!hasProject(cwd)) {
|
|
72179
73605
|
if (nonInteractive) {
|
|
72180
|
-
console.error(
|
|
73606
|
+
console.error(chalk22.red("No struere.json found. Run struere init first."));
|
|
72181
73607
|
process.exit(1);
|
|
72182
73608
|
}
|
|
72183
|
-
console.log(
|
|
73609
|
+
console.log(chalk22.yellow("No struere.json found - initializing project..."));
|
|
72184
73610
|
console.log();
|
|
72185
73611
|
const success = await runInit(cwd);
|
|
72186
73612
|
if (!success) {
|
|
@@ -72192,21 +73618,21 @@ async function ensureAuth6() {
|
|
|
72192
73618
|
const apiKey = getApiKey();
|
|
72193
73619
|
if (!credentials && !apiKey) {
|
|
72194
73620
|
if (nonInteractive) {
|
|
72195
|
-
console.error(
|
|
73621
|
+
console.error(chalk22.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
|
|
72196
73622
|
process.exit(1);
|
|
72197
73623
|
}
|
|
72198
|
-
console.log(
|
|
73624
|
+
console.log(chalk22.yellow("Not logged in - authenticating..."));
|
|
72199
73625
|
console.log();
|
|
72200
73626
|
credentials = await performLogin();
|
|
72201
73627
|
if (!credentials) {
|
|
72202
|
-
console.log(
|
|
73628
|
+
console.log(chalk22.red("Authentication failed"));
|
|
72203
73629
|
process.exit(1);
|
|
72204
73630
|
}
|
|
72205
73631
|
console.log();
|
|
72206
73632
|
}
|
|
72207
73633
|
return true;
|
|
72208
73634
|
}
|
|
72209
|
-
function
|
|
73635
|
+
function relativeTime4(ts) {
|
|
72210
73636
|
const diff = Date.now() - ts;
|
|
72211
73637
|
const seconds = Math.floor(diff / 1000);
|
|
72212
73638
|
if (seconds < 60)
|
|
@@ -72223,37 +73649,37 @@ function relativeTime3(ts) {
|
|
|
72223
73649
|
function channelColor(channel) {
|
|
72224
73650
|
switch (channel) {
|
|
72225
73651
|
case "whatsapp":
|
|
72226
|
-
return
|
|
73652
|
+
return chalk22.green(channel);
|
|
72227
73653
|
case "api":
|
|
72228
|
-
return
|
|
73654
|
+
return chalk22.blue(channel);
|
|
72229
73655
|
case "widget":
|
|
72230
|
-
return
|
|
73656
|
+
return chalk22.magenta(channel);
|
|
72231
73657
|
case "dashboard":
|
|
72232
|
-
return
|
|
73658
|
+
return chalk22.cyan(channel);
|
|
72233
73659
|
case "voice":
|
|
72234
|
-
return
|
|
73660
|
+
return chalk22.yellow(channel);
|
|
72235
73661
|
default:
|
|
72236
|
-
return
|
|
73662
|
+
return chalk22.gray(channel ?? "-");
|
|
72237
73663
|
}
|
|
72238
73664
|
}
|
|
72239
73665
|
function roleColor(role) {
|
|
72240
73666
|
switch (role) {
|
|
72241
73667
|
case "user":
|
|
72242
|
-
return
|
|
73668
|
+
return chalk22.cyan(role);
|
|
72243
73669
|
case "assistant":
|
|
72244
|
-
return
|
|
73670
|
+
return chalk22.green(role);
|
|
72245
73671
|
case "system":
|
|
72246
|
-
return
|
|
73672
|
+
return chalk22.yellow(role);
|
|
72247
73673
|
case "tool":
|
|
72248
|
-
return
|
|
73674
|
+
return chalk22.magenta(role);
|
|
72249
73675
|
default:
|
|
72250
|
-
return
|
|
73676
|
+
return chalk22.gray(role);
|
|
72251
73677
|
}
|
|
72252
73678
|
}
|
|
72253
|
-
var threadsCommand = new
|
|
73679
|
+
var threadsCommand = new Command20("threads").description("Manage conversation threads");
|
|
72254
73680
|
threadsCommand.command("list", { isDefault: true }).description("List conversation threads").option("--env <environment>", "Environment (development|production|eval)", "development").option("--channel <channel>", "Filter by channel (whatsapp|api|widget|dashboard|voice)").option("--limit <n>", "Maximum results", "25").option("--json", "Output raw JSON").action(async (opts) => {
|
|
72255
|
-
await
|
|
72256
|
-
const spinner = opts.json ? null :
|
|
73681
|
+
await ensureAuth7();
|
|
73682
|
+
const spinner = opts.json ? null : ora16();
|
|
72257
73683
|
try {
|
|
72258
73684
|
spinner?.start("Fetching threads");
|
|
72259
73685
|
const threads = await listThreads({
|
|
@@ -72279,8 +73705,8 @@ threadsCommand.command("list", { isDefault: true }).description("List conversati
|
|
|
72279
73705
|
channel: channelColor(t2.channel),
|
|
72280
73706
|
participant: t2.participantName ?? "Unknown",
|
|
72281
73707
|
agent: t2.agentName ?? "Unknown",
|
|
72282
|
-
lastMessage: t2.lastMessage ? t2.lastMessage.content.slice(0, 40) :
|
|
72283
|
-
time:
|
|
73708
|
+
lastMessage: t2.lastMessage ? t2.lastMessage.content.slice(0, 40) : chalk22.gray("-"),
|
|
73709
|
+
time: relativeTime4(t2.updatedAt ?? t2.createdAt)
|
|
72284
73710
|
})));
|
|
72285
73711
|
console.log();
|
|
72286
73712
|
} catch (err) {
|
|
@@ -72289,14 +73715,14 @@ threadsCommand.command("list", { isDefault: true }).description("List conversati
|
|
|
72289
73715
|
if (opts.json) {
|
|
72290
73716
|
console.log(JSON.stringify({ success: false, error: message }));
|
|
72291
73717
|
} else {
|
|
72292
|
-
console.log(
|
|
73718
|
+
console.log(chalk22.red("Error:"), message);
|
|
72293
73719
|
}
|
|
72294
73720
|
process.exit(1);
|
|
72295
73721
|
}
|
|
72296
73722
|
});
|
|
72297
73723
|
threadsCommand.command("view <id>").description("View thread details and messages").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").action(async (id, opts) => {
|
|
72298
|
-
await
|
|
72299
|
-
const spinner = opts.json ? null :
|
|
73724
|
+
await ensureAuth7();
|
|
73725
|
+
const spinner = opts.json ? null : ora16();
|
|
72300
73726
|
try {
|
|
72301
73727
|
spinner?.start("Fetching thread");
|
|
72302
73728
|
const thread = await getThreadWithMessages({
|
|
@@ -72316,28 +73742,28 @@ threadsCommand.command("view <id>").description("View thread details and message
|
|
|
72316
73742
|
return;
|
|
72317
73743
|
}
|
|
72318
73744
|
console.log();
|
|
72319
|
-
console.log(` ${
|
|
72320
|
-
console.log(` ${
|
|
73745
|
+
console.log(` ${chalk22.gray("ID:")} ${chalk22.cyan(thread._id)}`);
|
|
73746
|
+
console.log(` ${chalk22.gray("Channel:")} ${channelColor(thread.channel)}`);
|
|
72321
73747
|
if (thread.externalId) {
|
|
72322
|
-
console.log(` ${
|
|
73748
|
+
console.log(` ${chalk22.gray("External:")} ${chalk22.cyan(thread.externalId)}`);
|
|
72323
73749
|
}
|
|
72324
|
-
console.log(` ${
|
|
73750
|
+
console.log(` ${chalk22.gray("Agent:")} ${chalk22.cyan(thread.agentId)}`);
|
|
72325
73751
|
if (thread.currentAgentId) {
|
|
72326
|
-
console.log(` ${
|
|
73752
|
+
console.log(` ${chalk22.gray("Current:")} ${chalk22.cyan(thread.currentAgentId)}`);
|
|
72327
73753
|
}
|
|
72328
|
-
console.log(` ${
|
|
72329
|
-
console.log(` ${
|
|
73754
|
+
console.log(` ${chalk22.gray("Created:")} ${chalk22.cyan(new Date(thread.createdAt).toISOString())}`);
|
|
73755
|
+
console.log(` ${chalk22.gray("Updated:")} ${chalk22.cyan(new Date(thread.updatedAt).toISOString())}`);
|
|
72330
73756
|
if (thread.messages?.length) {
|
|
72331
73757
|
console.log();
|
|
72332
|
-
console.log(
|
|
72333
|
-
console.log(
|
|
73758
|
+
console.log(chalk22.bold("Messages"));
|
|
73759
|
+
console.log(chalk22.gray("─".repeat(60)));
|
|
72334
73760
|
for (const msg of thread.messages) {
|
|
72335
73761
|
if (msg.toolCalls?.length)
|
|
72336
73762
|
continue;
|
|
72337
73763
|
const time = new Date(msg.createdAt).toLocaleTimeString();
|
|
72338
73764
|
const role = roleColor(msg.role);
|
|
72339
73765
|
const content = msg.content.length > 200 ? msg.content.slice(0, 200) + "..." : msg.content;
|
|
72340
|
-
console.log(` ${
|
|
73766
|
+
console.log(` ${chalk22.gray(time)} ${role}: ${content}`);
|
|
72341
73767
|
}
|
|
72342
73768
|
}
|
|
72343
73769
|
console.log();
|
|
@@ -72347,20 +73773,20 @@ threadsCommand.command("view <id>").description("View thread details and message
|
|
|
72347
73773
|
if (opts.json) {
|
|
72348
73774
|
console.log(JSON.stringify({ success: false, error: message }));
|
|
72349
73775
|
} else {
|
|
72350
|
-
console.log(
|
|
73776
|
+
console.log(chalk22.red("Error:"), message);
|
|
72351
73777
|
}
|
|
72352
73778
|
process.exit(1);
|
|
72353
73779
|
}
|
|
72354
73780
|
});
|
|
72355
73781
|
threadsCommand.command("archive <id>").description("Archive a thread (frees its externalId)").option("--env <environment>", "Environment", "development").option("--confirm", "Skip production confirmation").option("--json", "Output raw JSON").action(async (id, opts) => {
|
|
72356
|
-
await
|
|
72357
|
-
const spinner = opts.json ? null :
|
|
73782
|
+
await ensureAuth7();
|
|
73783
|
+
const spinner = opts.json ? null : ora16();
|
|
72358
73784
|
const environment = opts.env;
|
|
72359
73785
|
if (environment === "production" && !opts.confirm && isInteractive()) {
|
|
72360
73786
|
const readline = await import("readline");
|
|
72361
73787
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
72362
73788
|
await new Promise((resolve) => {
|
|
72363
|
-
rl.question(
|
|
73789
|
+
rl.question(chalk22.yellow(`WARNING: Archiving thread in PRODUCTION environment.
|
|
72364
73790
|
Press Enter to continue or Ctrl+C to cancel: `), resolve);
|
|
72365
73791
|
});
|
|
72366
73792
|
rl.close();
|
|
@@ -72371,7 +73797,7 @@ threadsCommand.command("archive <id>").description("Archive a thread (frees its
|
|
|
72371
73797
|
threadId: id,
|
|
72372
73798
|
environment
|
|
72373
73799
|
});
|
|
72374
|
-
spinner?.succeed(
|
|
73800
|
+
spinner?.succeed(chalk22.green(`Thread ${id} archived`));
|
|
72375
73801
|
if (opts.json) {
|
|
72376
73802
|
console.log(JSON.stringify({ success: true, threadId: id }));
|
|
72377
73803
|
}
|
|
@@ -72381,20 +73807,20 @@ threadsCommand.command("archive <id>").description("Archive a thread (frees its
|
|
|
72381
73807
|
if (opts.json) {
|
|
72382
73808
|
console.log(JSON.stringify({ success: false, error: message }));
|
|
72383
73809
|
} else {
|
|
72384
|
-
console.log(
|
|
73810
|
+
console.log(chalk22.red("Error:"), message);
|
|
72385
73811
|
}
|
|
72386
73812
|
process.exit(1);
|
|
72387
73813
|
}
|
|
72388
73814
|
});
|
|
72389
73815
|
threadsCommand.command("reset").description("Archive a thread by phone number").requiredOption("--phone <number>", "Phone number to find and archive").option("--env <environment>", "Environment", "production").option("--confirm", "Skip production confirmation").option("--json", "Output raw JSON").action(async (opts) => {
|
|
72390
|
-
await
|
|
72391
|
-
const spinner = opts.json ? null :
|
|
73816
|
+
await ensureAuth7();
|
|
73817
|
+
const spinner = opts.json ? null : ora16();
|
|
72392
73818
|
const environment = opts.env;
|
|
72393
73819
|
if (environment === "production" && !opts.confirm && isInteractive()) {
|
|
72394
73820
|
const readline = await import("readline");
|
|
72395
73821
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
72396
73822
|
await new Promise((resolve) => {
|
|
72397
|
-
rl.question(
|
|
73823
|
+
rl.question(chalk22.yellow(`WARNING: Resetting thread in PRODUCTION environment.
|
|
72398
73824
|
Press Enter to continue or Ctrl+C to cancel: `), resolve);
|
|
72399
73825
|
});
|
|
72400
73826
|
rl.close();
|
|
@@ -72418,7 +73844,7 @@ threadsCommand.command("reset").description("Archive a thread by phone number").
|
|
|
72418
73844
|
threadId: thread._id,
|
|
72419
73845
|
environment
|
|
72420
73846
|
});
|
|
72421
|
-
spinner?.succeed(
|
|
73847
|
+
spinner?.succeed(chalk22.green(`Thread ${thread._id} archived (phone: ${opts.phone})`));
|
|
72422
73848
|
if (opts.json) {
|
|
72423
73849
|
console.log(JSON.stringify({ success: true, threadId: thread._id, phone: opts.phone }));
|
|
72424
73850
|
}
|
|
@@ -72428,7 +73854,7 @@ threadsCommand.command("reset").description("Archive a thread by phone number").
|
|
|
72428
73854
|
if (opts.json) {
|
|
72429
73855
|
console.log(JSON.stringify({ success: false, error: message }));
|
|
72430
73856
|
} else {
|
|
72431
|
-
console.log(
|
|
73857
|
+
console.log(chalk22.red("Error:"), message);
|
|
72432
73858
|
}
|
|
72433
73859
|
process.exit(1);
|
|
72434
73860
|
}
|
|
@@ -72436,15 +73862,15 @@ threadsCommand.command("reset").description("Archive a thread by phone number").
|
|
|
72436
73862
|
|
|
72437
73863
|
// src/cli/commands/compile-prompt.ts
|
|
72438
73864
|
init_credentials();
|
|
72439
|
-
import { Command as
|
|
72440
|
-
import
|
|
72441
|
-
import
|
|
73865
|
+
import { Command as Command21 } from "commander";
|
|
73866
|
+
import chalk23 from "chalk";
|
|
73867
|
+
import ora17 from "ora";
|
|
72442
73868
|
init_convex();
|
|
72443
|
-
var compilePromptCommand = new
|
|
73869
|
+
var compilePromptCommand = new Command21("compile-prompt").description("Compile and preview an agent's system prompt after template processing").argument("<agent-slug>", "Agent slug to compile prompt for").option("--env <env>", "Environment: development | production | eval", "development").option("--message <msg>", "Sample message for template context").option("--channel <channel>", "Sample channel (whatsapp, widget, api, dashboard)").option("--param <key=value...>", "Custom thread param (repeatable)", (val, acc) => {
|
|
72444
73870
|
acc.push(val);
|
|
72445
73871
|
return acc;
|
|
72446
73872
|
}, []).option("--phone <number>", "Shorthand for --param phoneNumber=<number>").option("--json", "Output full JSON (raw + compiled + context)").option("--raw", "Show raw uncompiled template instead of compiled").action(async (agentSlug, options) => {
|
|
72447
|
-
const spinner =
|
|
73873
|
+
const spinner = ora17();
|
|
72448
73874
|
const cwd = process.cwd();
|
|
72449
73875
|
const nonInteractive = !isInteractive();
|
|
72450
73876
|
const jsonMode = !!options.json;
|
|
@@ -72453,11 +73879,11 @@ var compilePromptCommand = new Command20("compile-prompt").description("Compile
|
|
|
72453
73879
|
if (jsonMode) {
|
|
72454
73880
|
console.log(JSON.stringify({ success: false, error: "No struere.json found" }));
|
|
72455
73881
|
} else {
|
|
72456
|
-
console.log(
|
|
73882
|
+
console.log(chalk23.red("No struere.json found. Run struere init first."));
|
|
72457
73883
|
}
|
|
72458
73884
|
process.exit(1);
|
|
72459
73885
|
}
|
|
72460
|
-
console.log(
|
|
73886
|
+
console.log(chalk23.yellow("No struere.json found - initializing project..."));
|
|
72461
73887
|
console.log();
|
|
72462
73888
|
const success = await runInit(cwd);
|
|
72463
73889
|
if (!success) {
|
|
@@ -72470,7 +73896,7 @@ var compilePromptCommand = new Command20("compile-prompt").description("Compile
|
|
|
72470
73896
|
if (jsonMode) {
|
|
72471
73897
|
console.log(JSON.stringify({ success: false, error: "Failed to load struere.json" }));
|
|
72472
73898
|
} else {
|
|
72473
|
-
console.log(
|
|
73899
|
+
console.log(chalk23.red("Failed to load struere.json"));
|
|
72474
73900
|
}
|
|
72475
73901
|
process.exit(1);
|
|
72476
73902
|
}
|
|
@@ -72481,15 +73907,15 @@ var compilePromptCommand = new Command20("compile-prompt").description("Compile
|
|
|
72481
73907
|
if (jsonMode) {
|
|
72482
73908
|
console.log(JSON.stringify({ success: false, error: "Not authenticated. Set STRUERE_API_KEY or run struere login." }));
|
|
72483
73909
|
} else {
|
|
72484
|
-
console.log(
|
|
73910
|
+
console.log(chalk23.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
|
|
72485
73911
|
}
|
|
72486
73912
|
process.exit(1);
|
|
72487
73913
|
}
|
|
72488
|
-
console.log(
|
|
73914
|
+
console.log(chalk23.yellow("Not logged in - authenticating..."));
|
|
72489
73915
|
console.log();
|
|
72490
73916
|
credentials = await performLogin();
|
|
72491
73917
|
if (!credentials) {
|
|
72492
|
-
console.log(
|
|
73918
|
+
console.log(chalk23.red("Authentication failed"));
|
|
72493
73919
|
process.exit(1);
|
|
72494
73920
|
}
|
|
72495
73921
|
console.log();
|
|
@@ -72501,7 +73927,7 @@ var compilePromptCommand = new Command20("compile-prompt").description("Compile
|
|
|
72501
73927
|
if (jsonMode) {
|
|
72502
73928
|
console.log(JSON.stringify({ success: false, error: `Invalid param format: ${param}. Use key=value.` }));
|
|
72503
73929
|
} else {
|
|
72504
|
-
console.log(
|
|
73930
|
+
console.log(chalk23.red(`Invalid param format: ${param}. Use key=value.`));
|
|
72505
73931
|
}
|
|
72506
73932
|
process.exit(1);
|
|
72507
73933
|
}
|
|
@@ -72520,7 +73946,7 @@ var compilePromptCommand = new Command20("compile-prompt").description("Compile
|
|
|
72520
73946
|
}
|
|
72521
73947
|
const environment = options.env;
|
|
72522
73948
|
if (!jsonMode) {
|
|
72523
|
-
spinner.start(`Compiling prompt for ${
|
|
73949
|
+
spinner.start(`Compiling prompt for ${chalk23.cyan(agentSlug)} (${environment})`);
|
|
72524
73950
|
}
|
|
72525
73951
|
const doCompile = async () => {
|
|
72526
73952
|
return compilePrompt({
|
|
@@ -72538,7 +73964,7 @@ var compilePromptCommand = new Command20("compile-prompt").description("Compile
|
|
|
72538
73964
|
console.log(JSON.stringify({ success: false, error }));
|
|
72539
73965
|
} else {
|
|
72540
73966
|
spinner.fail("Failed to compile prompt");
|
|
72541
|
-
console.log(
|
|
73967
|
+
console.log(chalk23.red("Error:"), error);
|
|
72542
73968
|
}
|
|
72543
73969
|
process.exit(1);
|
|
72544
73970
|
}
|
|
@@ -72561,33 +73987,33 @@ var compilePromptCommand = new Command20("compile-prompt").description("Compile
|
|
|
72561
73987
|
}, null, 2));
|
|
72562
73988
|
} else if (options.raw) {
|
|
72563
73989
|
console.log();
|
|
72564
|
-
console.log(
|
|
72565
|
-
console.log(
|
|
73990
|
+
console.log(chalk23.bold("Raw System Prompt"));
|
|
73991
|
+
console.log(chalk23.gray("─".repeat(60)));
|
|
72566
73992
|
console.log(result.raw);
|
|
72567
|
-
console.log(
|
|
73993
|
+
console.log(chalk23.gray("─".repeat(60)));
|
|
72568
73994
|
if (Object.keys(threadMetadata).length > 0) {
|
|
72569
|
-
console.log(
|
|
73995
|
+
console.log(chalk23.gray("Params:"), Object.entries(threadMetadata).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", "));
|
|
72570
73996
|
}
|
|
72571
73997
|
} else {
|
|
72572
73998
|
console.log();
|
|
72573
|
-
console.log(
|
|
72574
|
-
console.log(
|
|
73999
|
+
console.log(chalk23.bold("Compiled System Prompt"));
|
|
74000
|
+
console.log(chalk23.gray("─".repeat(60)));
|
|
72575
74001
|
console.log(result.compiled);
|
|
72576
|
-
console.log(
|
|
74002
|
+
console.log(chalk23.gray("─".repeat(60)));
|
|
72577
74003
|
if (Object.keys(threadMetadata).length > 0) {
|
|
72578
|
-
console.log(
|
|
74004
|
+
console.log(chalk23.gray("Params:"), Object.entries(threadMetadata).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", "));
|
|
72579
74005
|
}
|
|
72580
74006
|
}
|
|
72581
74007
|
});
|
|
72582
74008
|
|
|
72583
74009
|
// src/cli/commands/run-tool.ts
|
|
72584
74010
|
init_credentials();
|
|
72585
|
-
import { Command as
|
|
72586
|
-
import
|
|
72587
|
-
import
|
|
74011
|
+
import { Command as Command22 } from "commander";
|
|
74012
|
+
import chalk24 from "chalk";
|
|
74013
|
+
import ora18 from "ora";
|
|
72588
74014
|
init_convex();
|
|
72589
|
-
var runToolCommand = new
|
|
72590
|
-
const spinner =
|
|
74015
|
+
var runToolCommand = new Command22("run-tool").description("Run a tool as it would execute during a real agent conversation").argument("<tool-name>", "Tool name (e.g., entity.query, run_scrapers)").option("--agent <slug>", "Agent slug (optional — omit to run without agent context)").option("--env <environment>", "Environment: development | production | eval", "development").option("--args <json>", "Tool arguments as JSON string", "{}").option("--args-file <path>", "Read tool arguments from a JSON file").option("--json", "Output full JSON result").option("--confirm", "Skip production confirmation prompt").action(async (toolName, options) => {
|
|
74016
|
+
const spinner = ora18();
|
|
72591
74017
|
const cwd = process.cwd();
|
|
72592
74018
|
const nonInteractive = !isInteractive();
|
|
72593
74019
|
const jsonMode = !!options.json;
|
|
@@ -72596,11 +74022,11 @@ var runToolCommand = new Command21("run-tool").description("Run a tool as it wou
|
|
|
72596
74022
|
if (jsonMode) {
|
|
72597
74023
|
console.log(JSON.stringify({ success: false, error: "No struere.json found" }));
|
|
72598
74024
|
} else {
|
|
72599
|
-
console.log(
|
|
74025
|
+
console.log(chalk24.red("No struere.json found. Run struere init first."));
|
|
72600
74026
|
}
|
|
72601
74027
|
process.exit(1);
|
|
72602
74028
|
}
|
|
72603
|
-
console.log(
|
|
74029
|
+
console.log(chalk24.yellow("No struere.json found - initializing project..."));
|
|
72604
74030
|
console.log();
|
|
72605
74031
|
const success = await runInit(cwd);
|
|
72606
74032
|
if (!success) {
|
|
@@ -72613,7 +74039,7 @@ var runToolCommand = new Command21("run-tool").description("Run a tool as it wou
|
|
|
72613
74039
|
if (jsonMode) {
|
|
72614
74040
|
console.log(JSON.stringify({ success: false, error: "Failed to load struere.json" }));
|
|
72615
74041
|
} else {
|
|
72616
|
-
console.log(
|
|
74042
|
+
console.log(chalk24.red("Failed to load struere.json"));
|
|
72617
74043
|
}
|
|
72618
74044
|
process.exit(1);
|
|
72619
74045
|
}
|
|
@@ -72624,15 +74050,15 @@ var runToolCommand = new Command21("run-tool").description("Run a tool as it wou
|
|
|
72624
74050
|
if (jsonMode) {
|
|
72625
74051
|
console.log(JSON.stringify({ success: false, error: "Not authenticated. Set STRUERE_API_KEY or run struere login." }));
|
|
72626
74052
|
} else {
|
|
72627
|
-
console.log(
|
|
74053
|
+
console.log(chalk24.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
|
|
72628
74054
|
}
|
|
72629
74055
|
process.exit(1);
|
|
72630
74056
|
}
|
|
72631
|
-
console.log(
|
|
74057
|
+
console.log(chalk24.yellow("Not logged in - authenticating..."));
|
|
72632
74058
|
console.log();
|
|
72633
74059
|
credentials = await performLogin();
|
|
72634
74060
|
if (!credentials) {
|
|
72635
|
-
console.log(
|
|
74061
|
+
console.log(chalk24.red("Authentication failed"));
|
|
72636
74062
|
process.exit(1);
|
|
72637
74063
|
}
|
|
72638
74064
|
console.log();
|
|
@@ -72650,26 +74076,26 @@ var runToolCommand = new Command21("run-tool").description("Run a tool as it wou
|
|
|
72650
74076
|
if (jsonMode) {
|
|
72651
74077
|
console.log(JSON.stringify({ success: false, error: `Invalid JSON: ${err instanceof Error ? err.message : String(err)}` }));
|
|
72652
74078
|
} else {
|
|
72653
|
-
console.log(
|
|
74079
|
+
console.log(chalk24.red(`Invalid JSON: ${err instanceof Error ? err.message : String(err)}`));
|
|
72654
74080
|
}
|
|
72655
74081
|
process.exit(1);
|
|
72656
74082
|
}
|
|
72657
74083
|
const environment = options.env;
|
|
72658
74084
|
if (!options.agent && !toolName.includes(".") && !jsonMode) {
|
|
72659
|
-
console.log(
|
|
74085
|
+
console.log(chalk24.dim(`Tip: Running without agent context. Use --agent <slug> to run with a specific agent.`));
|
|
72660
74086
|
}
|
|
72661
74087
|
if (environment === "production" && !options.confirm && !nonInteractive) {
|
|
72662
74088
|
const readline = await import("readline");
|
|
72663
74089
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
72664
74090
|
await new Promise((resolve) => {
|
|
72665
|
-
rl.question(
|
|
74091
|
+
rl.question(chalk24.yellow(`WARNING: Running tool against PRODUCTION environment.
|
|
72666
74092
|
This will execute real operations with real data.
|
|
72667
74093
|
Press Enter to continue or Ctrl+C to cancel: `), resolve);
|
|
72668
74094
|
});
|
|
72669
74095
|
rl.close();
|
|
72670
74096
|
}
|
|
72671
74097
|
if (!jsonMode) {
|
|
72672
|
-
spinner.start(`Running ${
|
|
74098
|
+
spinner.start(`Running ${chalk24.cyan(toolName)}${options.agent ? ` on ${chalk24.cyan(options.agent)}` : ""} (${environment})`);
|
|
72673
74099
|
}
|
|
72674
74100
|
const doRunTool = async () => {
|
|
72675
74101
|
return runTool({
|
|
@@ -72686,7 +74112,7 @@ var runToolCommand = new Command21("run-tool").description("Run a tool as it wou
|
|
|
72686
74112
|
console.log(JSON.stringify({ success: false, error }));
|
|
72687
74113
|
} else {
|
|
72688
74114
|
spinner.fail("Failed to run tool");
|
|
72689
|
-
console.log(
|
|
74115
|
+
console.log(chalk24.red("Error:"), error);
|
|
72690
74116
|
}
|
|
72691
74117
|
process.exit(1);
|
|
72692
74118
|
}
|
|
@@ -72702,51 +74128,51 @@ var runToolCommand = new Command21("run-tool").description("Run a tool as it wou
|
|
|
72702
74128
|
if (jsonMode) {
|
|
72703
74129
|
console.log(JSON.stringify({ success: false, error: `${result.errorType}: ${result.message}`, result }));
|
|
72704
74130
|
} else {
|
|
72705
|
-
spinner.fail(
|
|
74131
|
+
spinner.fail(chalk24.red(`${result.errorType}: ${result.message}`));
|
|
72706
74132
|
}
|
|
72707
74133
|
process.exit(1);
|
|
72708
74134
|
}
|
|
72709
74135
|
if (!jsonMode) {
|
|
72710
|
-
spinner.succeed(`Ran ${
|
|
74136
|
+
spinner.succeed(`Ran ${chalk24.cyan(toolName)}${result.agent ? ` on ${chalk24.cyan(result.agent.slug)}` : ""} (${result.environment}) in ${result.durationMs}ms`);
|
|
72711
74137
|
}
|
|
72712
74138
|
if (jsonMode) {
|
|
72713
74139
|
console.log(JSON.stringify(result, null, 2));
|
|
72714
74140
|
} else {
|
|
72715
74141
|
console.log();
|
|
72716
|
-
console.log(
|
|
74142
|
+
console.log(chalk24.dim("─".repeat(50)));
|
|
72717
74143
|
console.log(JSON.stringify(result.result, null, 2));
|
|
72718
|
-
console.log(
|
|
74144
|
+
console.log(chalk24.dim("─".repeat(50)));
|
|
72719
74145
|
console.log();
|
|
72720
|
-
console.log(
|
|
74146
|
+
console.log(chalk24.dim(`Identity: ${result.identity.actorType} (${result.identity.identityMode} mode)`));
|
|
72721
74147
|
}
|
|
72722
74148
|
});
|
|
72723
74149
|
|
|
72724
74150
|
// src/cli/commands/chat.ts
|
|
72725
74151
|
init_credentials();
|
|
72726
|
-
import { Command as
|
|
72727
|
-
import
|
|
72728
|
-
import
|
|
74152
|
+
import { Command as Command23 } from "commander";
|
|
74153
|
+
import chalk25 from "chalk";
|
|
74154
|
+
import ora19 from "ora";
|
|
72729
74155
|
import readline from "readline";
|
|
72730
74156
|
init_convex();
|
|
72731
74157
|
function printExecutionMeta(meta) {
|
|
72732
|
-
console.log(
|
|
74158
|
+
console.log(chalk25.dim(`Model: ${meta.model} | Duration: ${meta.durationMs}ms`));
|
|
72733
74159
|
if (meta.toolCallSummary.length > 0) {
|
|
72734
|
-
console.log(
|
|
74160
|
+
console.log(chalk25.dim("Tool calls:"));
|
|
72735
74161
|
for (const tc of meta.toolCallSummary) {
|
|
72736
|
-
const status = tc.status === "success" ?
|
|
72737
|
-
const err = tc.errorMessage ?
|
|
72738
|
-
console.log(
|
|
74162
|
+
const status = tc.status === "success" ? chalk25.green("ok") : chalk25.red(tc.status);
|
|
74163
|
+
const err = tc.errorMessage ? chalk25.red(` — ${tc.errorMessage}`) : "";
|
|
74164
|
+
console.log(chalk25.dim(` ${tc.name} ${status} ${tc.durationMs}ms${err}`));
|
|
72739
74165
|
}
|
|
72740
74166
|
}
|
|
72741
74167
|
if (meta.permissionDenialCount > 0) {
|
|
72742
|
-
console.log(
|
|
74168
|
+
console.log(chalk25.dim(`Permission denials: ${chalk25.red(String(meta.permissionDenialCount))}`));
|
|
72743
74169
|
}
|
|
72744
74170
|
if (meta.errorCount > 0) {
|
|
72745
|
-
console.log(
|
|
74171
|
+
console.log(chalk25.dim(`Tool errors: ${chalk25.yellow(String(meta.errorCount))}`));
|
|
72746
74172
|
}
|
|
72747
74173
|
}
|
|
72748
|
-
var chatCommand = new
|
|
72749
|
-
const spinner =
|
|
74174
|
+
var chatCommand = new Command23("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 structured JSON including thread ID, message, usage, and per-tool-call execution metadata.").option("--channel <channel>", "Channel identifier", "api").option("-v, --verbose", "Show execution metadata (model, duration, tool calls)").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").option("--follow", "Continue in interactive mode after sending a message").action(async (slug, options) => {
|
|
74175
|
+
const spinner = ora19();
|
|
72750
74176
|
const cwd = process.cwd();
|
|
72751
74177
|
const nonInteractive = !isInteractive();
|
|
72752
74178
|
const jsonMode = !!options.json;
|
|
@@ -72755,11 +74181,11 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
72755
74181
|
if (jsonMode) {
|
|
72756
74182
|
console.log(JSON.stringify({ success: false, error: "No struere.json found" }));
|
|
72757
74183
|
} else {
|
|
72758
|
-
console.log(
|
|
74184
|
+
console.log(chalk25.red("No struere.json found. Run struere init first."));
|
|
72759
74185
|
}
|
|
72760
74186
|
process.exit(1);
|
|
72761
74187
|
}
|
|
72762
|
-
console.log(
|
|
74188
|
+
console.log(chalk25.yellow("No struere.json found - initializing project..."));
|
|
72763
74189
|
console.log();
|
|
72764
74190
|
const success = await runInit(cwd);
|
|
72765
74191
|
if (!success) {
|
|
@@ -72772,7 +74198,7 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
72772
74198
|
if (jsonMode) {
|
|
72773
74199
|
console.log(JSON.stringify({ success: false, error: "Failed to load struere.json" }));
|
|
72774
74200
|
} else {
|
|
72775
|
-
console.log(
|
|
74201
|
+
console.log(chalk25.red("Failed to load struere.json"));
|
|
72776
74202
|
}
|
|
72777
74203
|
process.exit(1);
|
|
72778
74204
|
}
|
|
@@ -72783,15 +74209,15 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
72783
74209
|
if (jsonMode) {
|
|
72784
74210
|
console.log(JSON.stringify({ success: false, error: "Not authenticated. Set STRUERE_API_KEY or run struere login." }));
|
|
72785
74211
|
} else {
|
|
72786
|
-
console.log(
|
|
74212
|
+
console.log(chalk25.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
|
|
72787
74213
|
}
|
|
72788
74214
|
process.exit(1);
|
|
72789
74215
|
}
|
|
72790
|
-
console.log(
|
|
74216
|
+
console.log(chalk25.yellow("Not logged in - authenticating..."));
|
|
72791
74217
|
console.log();
|
|
72792
74218
|
credentials = await performLogin();
|
|
72793
74219
|
if (!credentials) {
|
|
72794
|
-
console.log(
|
|
74220
|
+
console.log(chalk25.red("Authentication failed"));
|
|
72795
74221
|
process.exit(1);
|
|
72796
74222
|
}
|
|
72797
74223
|
console.log();
|
|
@@ -72802,14 +74228,14 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
72802
74228
|
if (jsonMode) {
|
|
72803
74229
|
console.log(JSON.stringify({ success: false, error: "--phone is required when using --router" }));
|
|
72804
74230
|
} else {
|
|
72805
|
-
console.log(
|
|
74231
|
+
console.log(chalk25.red("--phone is required when using --router"));
|
|
72806
74232
|
}
|
|
72807
74233
|
process.exit(1);
|
|
72808
74234
|
}
|
|
72809
74235
|
if (environment === "production" && !nonInteractive && !options.confirm) {
|
|
72810
74236
|
const confirmRl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
72811
74237
|
await new Promise((resolve) => {
|
|
72812
|
-
confirmRl.question(
|
|
74238
|
+
confirmRl.question(chalk25.yellow(`WARNING: Chatting with agent in PRODUCTION environment.
|
|
72813
74239
|
Press Enter to continue or Ctrl+C to cancel: `), resolve);
|
|
72814
74240
|
});
|
|
72815
74241
|
confirmRl.close();
|
|
@@ -72852,7 +74278,7 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
72852
74278
|
if (jsonMode) {
|
|
72853
74279
|
console.log(JSON.stringify({ success: false, error: "Authentication failed" }));
|
|
72854
74280
|
} else {
|
|
72855
|
-
console.log(
|
|
74281
|
+
console.log(chalk25.red("Authentication failed"));
|
|
72856
74282
|
}
|
|
72857
74283
|
process.exit(1);
|
|
72858
74284
|
}
|
|
@@ -72867,7 +74293,7 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
72867
74293
|
console.log(JSON.stringify({ success: false, error }));
|
|
72868
74294
|
} else {
|
|
72869
74295
|
spinner.fail("Failed to send message");
|
|
72870
|
-
console.log(
|
|
74296
|
+
console.log(chalk25.red("Error:"), error);
|
|
72871
74297
|
}
|
|
72872
74298
|
process.exit(1);
|
|
72873
74299
|
}
|
|
@@ -72904,20 +74330,20 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
72904
74330
|
console.log("─".repeat(60));
|
|
72905
74331
|
console.log();
|
|
72906
74332
|
if (routerResult.routedToAgent) {
|
|
72907
|
-
console.log(
|
|
74333
|
+
console.log(chalk25.green(`${routerResult.routedToAgent}`) + chalk25.dim(` (${routerResult.routedToAgentSlug})`) + chalk25.magenta(" ← routed") + chalk25.dim(":"));
|
|
72908
74334
|
} else {
|
|
72909
|
-
console.log(
|
|
74335
|
+
console.log(chalk25.green("Agent:"));
|
|
72910
74336
|
}
|
|
72911
74337
|
console.log(result.message);
|
|
72912
74338
|
console.log();
|
|
72913
74339
|
if (options.verbose) {
|
|
72914
|
-
console.log(
|
|
72915
|
-
console.log(
|
|
74340
|
+
console.log(chalk25.dim(`Thread: ${result.threadId}`));
|
|
74341
|
+
console.log(chalk25.dim(`Tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out (${result.usage.totalTokens} total)`));
|
|
72916
74342
|
if (result._executionMeta) {
|
|
72917
74343
|
printExecutionMeta(result._executionMeta);
|
|
72918
74344
|
}
|
|
72919
74345
|
} else {
|
|
72920
|
-
console.log(
|
|
74346
|
+
console.log(chalk25.dim(`Thread: ${result.threadId} | Tokens: ${result.usage.totalTokens}`));
|
|
72921
74347
|
}
|
|
72922
74348
|
console.log();
|
|
72923
74349
|
console.log("─".repeat(60));
|
|
@@ -72928,15 +74354,15 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
72928
74354
|
return;
|
|
72929
74355
|
}
|
|
72930
74356
|
}
|
|
72931
|
-
const headerLabel = isRouterMode ? `router ${
|
|
72932
|
-
console.log(
|
|
72933
|
-
console.log(
|
|
74357
|
+
const headerLabel = isRouterMode ? `router ${chalk25.cyan(slug)}` : chalk25.cyan(slug);
|
|
74358
|
+
console.log(chalk25.bold(`Chat with ${headerLabel} (${environment})`));
|
|
74359
|
+
console.log(chalk25.dim("Type 'exit' to quit"));
|
|
72934
74360
|
console.log();
|
|
72935
74361
|
let processing = false;
|
|
72936
74362
|
let generation = 0;
|
|
72937
74363
|
let currentAbort = null;
|
|
72938
74364
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
72939
|
-
rl.setPrompt(
|
|
74365
|
+
rl.setPrompt(chalk25.cyan("You: "));
|
|
72940
74366
|
rl.prompt();
|
|
72941
74367
|
rl.on("SIGINT", () => {
|
|
72942
74368
|
if (processing) {
|
|
@@ -72948,7 +74374,7 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
72948
74374
|
spinner.stop();
|
|
72949
74375
|
processing = false;
|
|
72950
74376
|
console.log();
|
|
72951
|
-
console.log(
|
|
74377
|
+
console.log(chalk25.yellow("Cancelled"));
|
|
72952
74378
|
console.log();
|
|
72953
74379
|
rl.resume();
|
|
72954
74380
|
rl.prompt();
|
|
@@ -72986,7 +74412,7 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
72986
74412
|
if (thisGeneration !== generation)
|
|
72987
74413
|
return;
|
|
72988
74414
|
if (!credentials) {
|
|
72989
|
-
console.log(
|
|
74415
|
+
console.log(chalk25.red("Authentication failed"));
|
|
72990
74416
|
rl.close();
|
|
72991
74417
|
return;
|
|
72992
74418
|
}
|
|
@@ -73000,7 +74426,7 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
73000
74426
|
}
|
|
73001
74427
|
if (error) {
|
|
73002
74428
|
spinner.fail("");
|
|
73003
|
-
console.log(
|
|
74429
|
+
console.log(chalk25.red("Error:"), error);
|
|
73004
74430
|
processing = false;
|
|
73005
74431
|
currentAbort = null;
|
|
73006
74432
|
rl.resume();
|
|
@@ -73020,20 +74446,20 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
73020
74446
|
const interactiveRouterResult = result;
|
|
73021
74447
|
console.log();
|
|
73022
74448
|
if (interactiveRouterResult.routedToAgent) {
|
|
73023
|
-
console.log(
|
|
74449
|
+
console.log(chalk25.green(`${interactiveRouterResult.routedToAgent}`) + chalk25.dim(` (${interactiveRouterResult.routedToAgentSlug})`) + chalk25.magenta(" ← routed") + chalk25.dim(":"));
|
|
73024
74450
|
} else {
|
|
73025
|
-
console.log(
|
|
74451
|
+
console.log(chalk25.green("Agent:"));
|
|
73026
74452
|
}
|
|
73027
74453
|
console.log(result.message);
|
|
73028
74454
|
console.log();
|
|
73029
74455
|
if (options.verbose) {
|
|
73030
|
-
console.log(
|
|
73031
|
-
console.log(
|
|
74456
|
+
console.log(chalk25.dim(`Thread: ${result.threadId}`));
|
|
74457
|
+
console.log(chalk25.dim(`Tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out (${result.usage.totalTokens} total)`));
|
|
73032
74458
|
if (result._executionMeta) {
|
|
73033
74459
|
printExecutionMeta(result._executionMeta);
|
|
73034
74460
|
}
|
|
73035
74461
|
} else {
|
|
73036
|
-
console.log(
|
|
74462
|
+
console.log(chalk25.dim(`Thread: ${result.threadId} | Tokens: ${result.usage.totalTokens}`));
|
|
73037
74463
|
}
|
|
73038
74464
|
console.log();
|
|
73039
74465
|
processing = false;
|
|
@@ -73043,24 +74469,24 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
|
|
|
73043
74469
|
});
|
|
73044
74470
|
rl.on("close", () => {
|
|
73045
74471
|
console.log();
|
|
73046
|
-
console.log(
|
|
74472
|
+
console.log(chalk25.dim("Goodbye!"));
|
|
73047
74473
|
process.exit(0);
|
|
73048
74474
|
});
|
|
73049
74475
|
});
|
|
73050
74476
|
|
|
73051
74477
|
// src/cli/commands/whatsapp.ts
|
|
73052
74478
|
init_credentials();
|
|
73053
|
-
import { Command as
|
|
73054
|
-
import
|
|
73055
|
-
async function
|
|
74479
|
+
import { Command as Command24 } from "commander";
|
|
74480
|
+
import chalk26 from "chalk";
|
|
74481
|
+
async function ensureAuth8() {
|
|
73056
74482
|
const cwd = process.cwd();
|
|
73057
74483
|
const nonInteractive = !isInteractive();
|
|
73058
74484
|
if (!hasProject(cwd)) {
|
|
73059
74485
|
if (nonInteractive) {
|
|
73060
|
-
console.error(
|
|
74486
|
+
console.error(chalk26.red("No struere.json found. Run struere init first."));
|
|
73061
74487
|
process.exit(1);
|
|
73062
74488
|
}
|
|
73063
|
-
console.log(
|
|
74489
|
+
console.log(chalk26.yellow("No struere.json found - initializing project..."));
|
|
73064
74490
|
console.log();
|
|
73065
74491
|
const success = await runInit(cwd);
|
|
73066
74492
|
if (!success) {
|
|
@@ -73072,14 +74498,14 @@ async function ensureAuth7() {
|
|
|
73072
74498
|
const apiKey = getApiKey();
|
|
73073
74499
|
if (!credentials && !apiKey) {
|
|
73074
74500
|
if (nonInteractive) {
|
|
73075
|
-
console.error(
|
|
74501
|
+
console.error(chalk26.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
|
|
73076
74502
|
process.exit(1);
|
|
73077
74503
|
}
|
|
73078
|
-
console.log(
|
|
74504
|
+
console.log(chalk26.yellow("Not logged in - authenticating..."));
|
|
73079
74505
|
console.log();
|
|
73080
74506
|
credentials = await performLogin();
|
|
73081
74507
|
if (!credentials) {
|
|
73082
|
-
console.log(
|
|
74508
|
+
console.log(chalk26.red("Authentication failed"));
|
|
73083
74509
|
process.exit(1);
|
|
73084
74510
|
}
|
|
73085
74511
|
console.log();
|
|
@@ -73119,16 +74545,16 @@ async function resolveConnection(env, identifier, out) {
|
|
|
73119
74545
|
function connectionLabel(conn) {
|
|
73120
74546
|
return conn.label || (conn.phoneNumber ? `+${conn.phoneNumber}` : conn._id.slice(-12));
|
|
73121
74547
|
}
|
|
73122
|
-
function
|
|
74548
|
+
function statusColor6(status) {
|
|
73123
74549
|
switch (status) {
|
|
73124
74550
|
case "connected":
|
|
73125
|
-
return
|
|
74551
|
+
return chalk26.green(status);
|
|
73126
74552
|
case "pending_setup":
|
|
73127
|
-
return
|
|
74553
|
+
return chalk26.yellow("pending");
|
|
73128
74554
|
case "disconnected":
|
|
73129
|
-
return
|
|
74555
|
+
return chalk26.red(status);
|
|
73130
74556
|
default:
|
|
73131
|
-
return
|
|
74557
|
+
return chalk26.gray(status);
|
|
73132
74558
|
}
|
|
73133
74559
|
}
|
|
73134
74560
|
function assignmentLabel(conn) {
|
|
@@ -73140,11 +74566,11 @@ function assignmentLabel(conn) {
|
|
|
73140
74566
|
return `Router: ${conn.routerId.slice(-8)}`;
|
|
73141
74567
|
if (conn.agentId)
|
|
73142
74568
|
return `Agent: ${conn.agentId.slice(-8)}`;
|
|
73143
|
-
return
|
|
74569
|
+
return chalk26.gray("none");
|
|
73144
74570
|
}
|
|
73145
|
-
var whatsappCommand = new
|
|
74571
|
+
var whatsappCommand = new Command24("whatsapp").description("Manage WhatsApp connections and routing");
|
|
73146
74572
|
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) => {
|
|
73147
|
-
await
|
|
74573
|
+
await ensureAuth8();
|
|
73148
74574
|
const env = opts.env;
|
|
73149
74575
|
const out = opts.json ? createSilentOutput() : createOutput();
|
|
73150
74576
|
out.start("Fetching WhatsApp connections");
|
|
@@ -73162,7 +74588,7 @@ whatsappCommand.command("list").description("List WhatsApp connections with rout
|
|
|
73162
74588
|
}
|
|
73163
74589
|
console.log();
|
|
73164
74590
|
if (connections.length === 0) {
|
|
73165
|
-
console.log(
|
|
74591
|
+
console.log(chalk26.gray(" No WhatsApp connections found"));
|
|
73166
74592
|
console.log();
|
|
73167
74593
|
return;
|
|
73168
74594
|
}
|
|
@@ -73173,16 +74599,16 @@ whatsappCommand.command("list").description("List WhatsApp connections with rout
|
|
|
73173
74599
|
{ key: "assignment", label: "Assignment", width: 30 },
|
|
73174
74600
|
{ key: "id", label: "ID", width: 16 }
|
|
73175
74601
|
], connections.map((c) => ({
|
|
73176
|
-
label: c.label ||
|
|
73177
|
-
phone: c.phoneNumber ? `+${c.phoneNumber}` :
|
|
73178
|
-
status:
|
|
74602
|
+
label: c.label || chalk26.gray("-"),
|
|
74603
|
+
phone: c.phoneNumber ? `+${c.phoneNumber}` : chalk26.gray("-"),
|
|
74604
|
+
status: statusColor6(c.status),
|
|
73179
74605
|
assignment: assignmentLabel(c),
|
|
73180
74606
|
id: c._id.slice(-12)
|
|
73181
74607
|
})));
|
|
73182
74608
|
console.log();
|
|
73183
74609
|
});
|
|
73184
74610
|
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) => {
|
|
73185
|
-
await
|
|
74611
|
+
await ensureAuth8();
|
|
73186
74612
|
const env = opts.env;
|
|
73187
74613
|
const out = createOutput();
|
|
73188
74614
|
const conn = await resolveConnection(env, connection, out);
|
|
@@ -73218,7 +74644,7 @@ whatsappCommand.command("set-router <connection> <router-slug>").description("As
|
|
|
73218
74644
|
console.log();
|
|
73219
74645
|
});
|
|
73220
74646
|
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) => {
|
|
73221
|
-
await
|
|
74647
|
+
await ensureAuth8();
|
|
73222
74648
|
const env = opts.env;
|
|
73223
74649
|
const out = createOutput();
|
|
73224
74650
|
const conn = await resolveConnection(env, connection, out);
|
|
@@ -73247,13 +74673,13 @@ whatsappCommand.command("set-agent <connection> <agent-slug>").description("Assi
|
|
|
73247
74673
|
|
|
73248
74674
|
// src/cli/commands/diff.ts
|
|
73249
74675
|
init_credentials();
|
|
73250
|
-
import { Command as
|
|
73251
|
-
import
|
|
73252
|
-
import
|
|
74676
|
+
import { Command as Command25 } from "commander";
|
|
74677
|
+
import chalk28 from "chalk";
|
|
74678
|
+
import ora20 from "ora";
|
|
73253
74679
|
init_convex();
|
|
73254
74680
|
|
|
73255
74681
|
// src/cli/utils/diff.ts
|
|
73256
|
-
import
|
|
74682
|
+
import chalk27 from "chalk";
|
|
73257
74683
|
function computeLCS(a, b) {
|
|
73258
74684
|
const m = a.length;
|
|
73259
74685
|
const n = b.length;
|
|
@@ -73352,13 +74778,13 @@ function renderDiff(lines) {
|
|
|
73352
74778
|
for (const line of lines) {
|
|
73353
74779
|
switch (line.type) {
|
|
73354
74780
|
case "removed":
|
|
73355
|
-
console.log(
|
|
74781
|
+
console.log(chalk27.red("- " + line.content));
|
|
73356
74782
|
break;
|
|
73357
74783
|
case "added":
|
|
73358
|
-
console.log(
|
|
74784
|
+
console.log(chalk27.green("+ " + line.content));
|
|
73359
74785
|
break;
|
|
73360
74786
|
case "context":
|
|
73361
|
-
console.log(
|
|
74787
|
+
console.log(chalk27.gray(" " + line.content));
|
|
73362
74788
|
break;
|
|
73363
74789
|
}
|
|
73364
74790
|
}
|
|
@@ -73475,7 +74901,7 @@ function diffRouter(local, remote) {
|
|
|
73475
74901
|
}
|
|
73476
74902
|
function renderChanges(changes) {
|
|
73477
74903
|
for (const change of changes) {
|
|
73478
|
-
console.log(
|
|
74904
|
+
console.log(chalk28.cyan(` ${change.field}:`));
|
|
73479
74905
|
if (change.field === "systemPrompt") {
|
|
73480
74906
|
const lines = diffLines(String(change.old), String(change.new));
|
|
73481
74907
|
if (lines.length > 0) {
|
|
@@ -73487,10 +74913,10 @@ function renderChanges(changes) {
|
|
|
73487
74913
|
const added = newTools.filter((t2) => !oldTools.includes(t2));
|
|
73488
74914
|
const removed = oldTools.filter((t2) => !newTools.includes(t2));
|
|
73489
74915
|
for (const t2 of removed) {
|
|
73490
|
-
console.log(
|
|
74916
|
+
console.log(chalk28.red(`- ${t2}`));
|
|
73491
74917
|
}
|
|
73492
74918
|
for (const t2 of added) {
|
|
73493
|
-
console.log(
|
|
74919
|
+
console.log(chalk28.green(`+ ${t2}`));
|
|
73494
74920
|
}
|
|
73495
74921
|
} else {
|
|
73496
74922
|
const lines = diffObjects(change.old, change.new);
|
|
@@ -73500,14 +74926,14 @@ function renderChanges(changes) {
|
|
|
73500
74926
|
}
|
|
73501
74927
|
}
|
|
73502
74928
|
}
|
|
73503
|
-
var diffCommand = new
|
|
73504
|
-
const spinner =
|
|
74929
|
+
var diffCommand = new Command25("diff").description("Show content differences between local and remote resources").option("--env <environment>", "Environment to diff against", "development").option("--resource <type>", "Filter to resource type (agents|triggers|entity-types|roles|routers)").option("--name <slug>", "Filter to specific resource by slug/name").option("--json", "Output raw JSON diff").option("--stat", "Show summary stats only (no content diff)").action(async (opts) => {
|
|
74930
|
+
const spinner = ora20();
|
|
73505
74931
|
const cwd = process.cwd();
|
|
73506
74932
|
const jsonMode = !!opts.json;
|
|
73507
74933
|
const nonInteractive = !isInteractive();
|
|
73508
74934
|
if (!jsonMode) {
|
|
73509
74935
|
console.log();
|
|
73510
|
-
console.log(
|
|
74936
|
+
console.log(chalk28.bold("Struere Diff"));
|
|
73511
74937
|
console.log();
|
|
73512
74938
|
}
|
|
73513
74939
|
if (!hasProject(cwd)) {
|
|
@@ -73515,11 +74941,11 @@ var diffCommand = new Command24("diff").description("Show content differences be
|
|
|
73515
74941
|
if (jsonMode) {
|
|
73516
74942
|
console.log(JSON.stringify({ error: "No struere.json found. Run struere init first." }));
|
|
73517
74943
|
} else {
|
|
73518
|
-
console.log(
|
|
74944
|
+
console.log(chalk28.red("No struere.json found. Run struere init first."));
|
|
73519
74945
|
}
|
|
73520
74946
|
process.exit(1);
|
|
73521
74947
|
}
|
|
73522
|
-
console.log(
|
|
74948
|
+
console.log(chalk28.yellow("No struere.json found - initializing project..."));
|
|
73523
74949
|
console.log();
|
|
73524
74950
|
const success = await runInit(cwd);
|
|
73525
74951
|
if (!success) {
|
|
@@ -73532,7 +74958,7 @@ var diffCommand = new Command24("diff").description("Show content differences be
|
|
|
73532
74958
|
if (jsonMode) {
|
|
73533
74959
|
console.log(JSON.stringify({ error: "Failed to load struere.json" }));
|
|
73534
74960
|
} else {
|
|
73535
|
-
console.log(
|
|
74961
|
+
console.log(chalk28.red("Failed to load struere.json"));
|
|
73536
74962
|
}
|
|
73537
74963
|
process.exit(1);
|
|
73538
74964
|
}
|
|
@@ -73543,15 +74969,15 @@ var diffCommand = new Command24("diff").description("Show content differences be
|
|
|
73543
74969
|
if (jsonMode) {
|
|
73544
74970
|
console.log(JSON.stringify({ error: "Not authenticated. Set STRUERE_API_KEY or run struere login." }));
|
|
73545
74971
|
} else {
|
|
73546
|
-
console.log(
|
|
74972
|
+
console.log(chalk28.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
|
|
73547
74973
|
}
|
|
73548
74974
|
process.exit(1);
|
|
73549
74975
|
}
|
|
73550
|
-
console.log(
|
|
74976
|
+
console.log(chalk28.yellow("Not logged in - authenticating..."));
|
|
73551
74977
|
console.log();
|
|
73552
74978
|
credentials = await performLogin();
|
|
73553
74979
|
if (!credentials) {
|
|
73554
|
-
console.log(
|
|
74980
|
+
console.log(chalk28.red("Authentication failed"));
|
|
73555
74981
|
process.exit(1);
|
|
73556
74982
|
}
|
|
73557
74983
|
console.log();
|
|
@@ -73568,7 +74994,7 @@ var diffCommand = new Command24("diff").description("Show content differences be
|
|
|
73568
74994
|
console.log(JSON.stringify({ error: error2 instanceof Error ? error2.message : String(error2) }));
|
|
73569
74995
|
} else {
|
|
73570
74996
|
spinner.fail("Failed to load local resources");
|
|
73571
|
-
console.log(
|
|
74997
|
+
console.log(chalk28.red("Error:"), error2 instanceof Error ? error2.message : String(error2));
|
|
73572
74998
|
}
|
|
73573
74999
|
process.exit(1);
|
|
73574
75000
|
}
|
|
@@ -73581,7 +75007,7 @@ var diffCommand = new Command24("diff").description("Show content differences be
|
|
|
73581
75007
|
console.log(JSON.stringify({ error: error || "Failed to fetch remote state" }));
|
|
73582
75008
|
} else {
|
|
73583
75009
|
spinner.fail("Failed to fetch remote state");
|
|
73584
|
-
console.log(
|
|
75010
|
+
console.log(chalk28.red("Error:"), error || "Unknown error");
|
|
73585
75011
|
}
|
|
73586
75012
|
process.exit(1);
|
|
73587
75013
|
}
|
|
@@ -73723,50 +75149,50 @@ var diffCommand = new Command24("diff").description("Show content differences be
|
|
|
73723
75149
|
return;
|
|
73724
75150
|
}
|
|
73725
75151
|
console.log();
|
|
73726
|
-
console.log(
|
|
75152
|
+
console.log(chalk28.bold(`struere diff`) + chalk28.gray(` (vs ${environment})`));
|
|
73727
75153
|
console.log();
|
|
73728
75154
|
if (entries.length === 0) {
|
|
73729
|
-
console.log(
|
|
75155
|
+
console.log(chalk28.green(" No differences found."));
|
|
73730
75156
|
console.log();
|
|
73731
75157
|
return;
|
|
73732
75158
|
}
|
|
73733
75159
|
for (const entry of entries) {
|
|
73734
75160
|
const filePath = `${entry.type}/${entry.slug}.ts`;
|
|
73735
75161
|
if (entry.status === "new") {
|
|
73736
|
-
console.log(
|
|
75162
|
+
console.log(chalk28.green(` ${filePath} (new — not yet synced)`));
|
|
73737
75163
|
} else if (entry.status === "deleted") {
|
|
73738
|
-
console.log(
|
|
75164
|
+
console.log(chalk28.red(` ${filePath} (deleted — only on remote)`));
|
|
73739
75165
|
} else if (entry.status === "modified") {
|
|
73740
|
-
console.log(
|
|
75166
|
+
console.log(chalk28.yellow(` ${filePath} (modified)`));
|
|
73741
75167
|
if (!opts.stat) {
|
|
73742
|
-
console.log(
|
|
75168
|
+
console.log(chalk28.gray(" " + "─".repeat(37)));
|
|
73743
75169
|
renderChanges(entry.changes);
|
|
73744
75170
|
}
|
|
73745
75171
|
}
|
|
73746
75172
|
if (!opts.stat)
|
|
73747
75173
|
console.log();
|
|
73748
75174
|
}
|
|
73749
|
-
console.log(
|
|
75175
|
+
console.log(chalk28.gray(` Summary: ${modified} modified, ${newCount} new, ${deleted} deleted`));
|
|
73750
75176
|
console.log();
|
|
73751
75177
|
});
|
|
73752
75178
|
|
|
73753
75179
|
// src/cli/commands/doctor.ts
|
|
73754
75180
|
init_credentials();
|
|
73755
|
-
import { Command as
|
|
73756
|
-
import
|
|
73757
|
-
import
|
|
75181
|
+
import { Command as Command26 } from "commander";
|
|
75182
|
+
import chalk29 from "chalk";
|
|
75183
|
+
import ora21 from "ora";
|
|
73758
75184
|
init_convex();
|
|
73759
75185
|
init_convex();
|
|
73760
75186
|
var BUILTIN_ENTITY_TYPES = new Set(["payment"]);
|
|
73761
|
-
async function
|
|
75187
|
+
async function ensureAuth9() {
|
|
73762
75188
|
const cwd = process.cwd();
|
|
73763
75189
|
const nonInteractive = !isInteractive();
|
|
73764
75190
|
if (!hasProject(cwd)) {
|
|
73765
75191
|
if (nonInteractive) {
|
|
73766
|
-
console.error(
|
|
75192
|
+
console.error(chalk29.red("No struere.json found. Run struere init first."));
|
|
73767
75193
|
process.exit(1);
|
|
73768
75194
|
}
|
|
73769
|
-
console.log(
|
|
75195
|
+
console.log(chalk29.yellow("No struere.json found - initializing project..."));
|
|
73770
75196
|
console.log();
|
|
73771
75197
|
const success = await runInit(cwd);
|
|
73772
75198
|
if (!success) {
|
|
@@ -73778,14 +75204,14 @@ async function ensureAuth8() {
|
|
|
73778
75204
|
const apiKey = getApiKey();
|
|
73779
75205
|
if (!credentials && !apiKey) {
|
|
73780
75206
|
if (nonInteractive) {
|
|
73781
|
-
console.error(
|
|
75207
|
+
console.error(chalk29.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
|
|
73782
75208
|
process.exit(1);
|
|
73783
75209
|
}
|
|
73784
|
-
console.log(
|
|
75210
|
+
console.log(chalk29.yellow("Not logged in - authenticating..."));
|
|
73785
75211
|
console.log();
|
|
73786
75212
|
credentials = await performLogin();
|
|
73787
75213
|
if (!credentials) {
|
|
73788
|
-
console.log(
|
|
75214
|
+
console.log(chalk29.red("Authentication failed"));
|
|
73789
75215
|
process.exit(1);
|
|
73790
75216
|
}
|
|
73791
75217
|
console.log();
|
|
@@ -74090,9 +75516,9 @@ async function runCheck(fn) {
|
|
|
74090
75516
|
};
|
|
74091
75517
|
}
|
|
74092
75518
|
}
|
|
74093
|
-
var doctorCommand = new
|
|
74094
|
-
await
|
|
74095
|
-
const spinner = opts.json ? null :
|
|
75519
|
+
var doctorCommand = new Command26("doctor").description("Run diagnostic checks on your Struere project").option("--env <environment>", "Environment to check", "development").option("--json", "Output raw JSON").action(async (opts) => {
|
|
75520
|
+
await ensureAuth9();
|
|
75521
|
+
const spinner = opts.json ? null : ora21();
|
|
74096
75522
|
const cwd = process.cwd();
|
|
74097
75523
|
const environment = opts.env;
|
|
74098
75524
|
const project = loadProject(cwd);
|
|
@@ -74123,29 +75549,29 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
|
|
|
74123
75549
|
return;
|
|
74124
75550
|
}
|
|
74125
75551
|
console.log();
|
|
74126
|
-
console.log(
|
|
75552
|
+
console.log(chalk29.bold(`Struere Doctor`) + chalk29.gray(` (${environment})`));
|
|
74127
75553
|
console.log();
|
|
74128
75554
|
const maxLabelLen = Math.max(...checks.map((c) => c.label.length));
|
|
74129
75555
|
for (const check of checks) {
|
|
74130
|
-
const icon = check.status === "ok" ?
|
|
75556
|
+
const icon = check.status === "ok" ? chalk29.green("●") : check.status === "warn" ? chalk29.yellow("○") : chalk29.red("✖");
|
|
74131
75557
|
const paddedLabel = check.label.padEnd(maxLabelLen);
|
|
74132
75558
|
console.log(` ${icon} ${paddedLabel} ${check.message}`);
|
|
74133
75559
|
}
|
|
74134
75560
|
console.log();
|
|
74135
75561
|
const parts = [];
|
|
74136
75562
|
if (summary.ok > 0)
|
|
74137
|
-
parts.push(
|
|
75563
|
+
parts.push(chalk29.green(`${summary.ok} passed`));
|
|
74138
75564
|
if (summary.warn > 0)
|
|
74139
|
-
parts.push(
|
|
75565
|
+
parts.push(chalk29.yellow(`${summary.warn} warning${summary.warn > 1 ? "s" : ""}`));
|
|
74140
75566
|
if (summary.error > 0)
|
|
74141
|
-
parts.push(
|
|
75567
|
+
parts.push(chalk29.red(`${summary.error} error${summary.error > 1 ? "s" : ""}`));
|
|
74142
75568
|
console.log(` Summary: ${parts.join(", ")}`);
|
|
74143
75569
|
const hints = checks.filter((c) => c.hint && c.status !== "ok");
|
|
74144
75570
|
if (hints.length > 0) {
|
|
74145
75571
|
console.log();
|
|
74146
75572
|
for (const h of hints) {
|
|
74147
|
-
const color = h.status === "error" ?
|
|
74148
|
-
console.log(` ${color("→")} ${
|
|
75573
|
+
const color = h.status === "error" ? chalk29.red : chalk29.yellow;
|
|
75574
|
+
console.log(` ${color("→")} ${chalk29.dim(h.hint)}`);
|
|
74149
75575
|
}
|
|
74150
75576
|
}
|
|
74151
75577
|
console.log();
|
|
@@ -74158,7 +75584,7 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
|
|
|
74158
75584
|
if (opts.json) {
|
|
74159
75585
|
console.log(JSON.stringify({ success: false, error: message }));
|
|
74160
75586
|
} else {
|
|
74161
|
-
console.log(
|
|
75587
|
+
console.log(chalk29.red("Error:"), message);
|
|
74162
75588
|
}
|
|
74163
75589
|
process.exit(1);
|
|
74164
75590
|
}
|
|
@@ -74166,16 +75592,16 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
|
|
|
74166
75592
|
|
|
74167
75593
|
// src/cli/commands/keys.ts
|
|
74168
75594
|
init_credentials();
|
|
74169
|
-
import { Command as
|
|
74170
|
-
import
|
|
74171
|
-
import
|
|
75595
|
+
import { Command as Command27 } from "commander";
|
|
75596
|
+
import chalk30 from "chalk";
|
|
75597
|
+
import ora22 from "ora";
|
|
74172
75598
|
import { input as input3, confirm as confirm8 } from "@inquirer/prompts";
|
|
74173
75599
|
init_convex();
|
|
74174
75600
|
|
|
74175
75601
|
// src/cli/utils/apiKeys.ts
|
|
74176
75602
|
init_credentials();
|
|
74177
75603
|
init_config();
|
|
74178
|
-
function
|
|
75604
|
+
function getToken6() {
|
|
74179
75605
|
const credentials = loadCredentials();
|
|
74180
75606
|
const apiKey = getApiKey();
|
|
74181
75607
|
const token = apiKey || credentials?.token;
|
|
@@ -74183,8 +75609,8 @@ function getToken5() {
|
|
|
74183
75609
|
throw new Error("Not authenticated");
|
|
74184
75610
|
return token;
|
|
74185
75611
|
}
|
|
74186
|
-
async function
|
|
74187
|
-
const token =
|
|
75612
|
+
async function convexQuery9(path, args) {
|
|
75613
|
+
const token = getToken6();
|
|
74188
75614
|
const response = await fetch(`${CONVEX_URL}/api/query`, {
|
|
74189
75615
|
method: "POST",
|
|
74190
75616
|
headers: {
|
|
@@ -74208,8 +75634,8 @@ async function convexQuery8(path, args) {
|
|
|
74208
75634
|
}
|
|
74209
75635
|
return json.value;
|
|
74210
75636
|
}
|
|
74211
|
-
async function
|
|
74212
|
-
const token =
|
|
75637
|
+
async function convexMutation7(path, args) {
|
|
75638
|
+
const token = getToken6();
|
|
74213
75639
|
const response = await fetch(`${CONVEX_URL}/api/mutation`, {
|
|
74214
75640
|
method: "POST",
|
|
74215
75641
|
headers: {
|
|
@@ -74234,17 +75660,17 @@ async function convexMutation6(path, args) {
|
|
|
74234
75660
|
return json.value;
|
|
74235
75661
|
}
|
|
74236
75662
|
async function listApiKeys() {
|
|
74237
|
-
return
|
|
75663
|
+
return convexQuery9("apiKeys:list", {});
|
|
74238
75664
|
}
|
|
74239
75665
|
async function createApiKey(args) {
|
|
74240
|
-
return
|
|
75666
|
+
return convexMutation7("apiKeys:create", {
|
|
74241
75667
|
name: args.name,
|
|
74242
75668
|
environment: args.environment,
|
|
74243
75669
|
permissions: args.permissions ?? ["*"]
|
|
74244
75670
|
});
|
|
74245
75671
|
}
|
|
74246
75672
|
async function removeApiKey(id) {
|
|
74247
|
-
return
|
|
75673
|
+
return convexMutation7("apiKeys:remove", { id });
|
|
74248
75674
|
}
|
|
74249
75675
|
|
|
74250
75676
|
// src/cli/commands/keys.ts
|
|
@@ -74262,15 +75688,15 @@ async function withKeyAuthRetry(fn) {
|
|
|
74262
75688
|
throw err;
|
|
74263
75689
|
}
|
|
74264
75690
|
}
|
|
74265
|
-
async function
|
|
75691
|
+
async function ensureAuth10() {
|
|
74266
75692
|
const cwd = process.cwd();
|
|
74267
75693
|
const nonInteractive = !isInteractive();
|
|
74268
75694
|
if (!hasProject(cwd)) {
|
|
74269
75695
|
if (nonInteractive) {
|
|
74270
|
-
console.error(
|
|
75696
|
+
console.error(chalk30.red("No struere.json found. Run struere init first."));
|
|
74271
75697
|
process.exit(1);
|
|
74272
75698
|
}
|
|
74273
|
-
console.log(
|
|
75699
|
+
console.log(chalk30.yellow("No struere.json found - initializing project..."));
|
|
74274
75700
|
console.log();
|
|
74275
75701
|
const success = await runInit(cwd);
|
|
74276
75702
|
if (!success) {
|
|
@@ -74282,21 +75708,21 @@ async function ensureAuth9() {
|
|
|
74282
75708
|
const apiKey = getApiKey();
|
|
74283
75709
|
if (!credentials && !apiKey) {
|
|
74284
75710
|
if (nonInteractive) {
|
|
74285
|
-
console.error(
|
|
75711
|
+
console.error(chalk30.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
|
|
74286
75712
|
process.exit(1);
|
|
74287
75713
|
}
|
|
74288
|
-
console.log(
|
|
75714
|
+
console.log(chalk30.yellow("Not logged in - authenticating..."));
|
|
74289
75715
|
console.log();
|
|
74290
75716
|
credentials = await performLogin();
|
|
74291
75717
|
if (!credentials) {
|
|
74292
|
-
console.log(
|
|
75718
|
+
console.log(chalk30.red("Authentication failed"));
|
|
74293
75719
|
process.exit(1);
|
|
74294
75720
|
}
|
|
74295
75721
|
console.log();
|
|
74296
75722
|
}
|
|
74297
75723
|
return true;
|
|
74298
75724
|
}
|
|
74299
|
-
function
|
|
75725
|
+
function relativeTime5(ts) {
|
|
74300
75726
|
const diff = Date.now() - ts;
|
|
74301
75727
|
const seconds = Math.floor(diff / 1000);
|
|
74302
75728
|
if (seconds < 60)
|
|
@@ -74316,10 +75742,10 @@ function maskedPrefix(prefix) {
|
|
|
74316
75742
|
function isValidEnvironment(value) {
|
|
74317
75743
|
return value === "development" || value === "production" || value === "eval";
|
|
74318
75744
|
}
|
|
74319
|
-
var keysCommand = new
|
|
75745
|
+
var keysCommand = new Command27("keys").description("Manage API keys");
|
|
74320
75746
|
keysCommand.command("list", { isDefault: true }).description("List API keys").option("--env <environment>", "Filter by environment (development|production|eval)").option("--json", "Output raw JSON").action(async (opts) => {
|
|
74321
|
-
await
|
|
74322
|
-
const spinner = opts.json ? null :
|
|
75747
|
+
await ensureAuth10();
|
|
75748
|
+
const spinner = opts.json ? null : ora22();
|
|
74323
75749
|
try {
|
|
74324
75750
|
spinner?.start("Fetching API keys");
|
|
74325
75751
|
let keys = await withKeyAuthRetry(() => listApiKeys());
|
|
@@ -74348,8 +75774,8 @@ keysCommand.command("list", { isDefault: true }).description("List API keys").op
|
|
|
74348
75774
|
name: k.name,
|
|
74349
75775
|
prefix: maskedPrefix(k.keyPrefix),
|
|
74350
75776
|
environment: k.environment,
|
|
74351
|
-
created:
|
|
74352
|
-
lastUsed: k.lastUsedAt ?
|
|
75777
|
+
created: relativeTime5(k.createdAt),
|
|
75778
|
+
lastUsed: k.lastUsedAt ? relativeTime5(k.lastUsedAt) : chalk30.gray("-")
|
|
74353
75779
|
})));
|
|
74354
75780
|
console.log();
|
|
74355
75781
|
} catch (err) {
|
|
@@ -74358,19 +75784,19 @@ keysCommand.command("list", { isDefault: true }).description("List API keys").op
|
|
|
74358
75784
|
if (opts.json) {
|
|
74359
75785
|
console.log(JSON.stringify({ success: false, error: message }));
|
|
74360
75786
|
} else {
|
|
74361
|
-
console.log(
|
|
75787
|
+
console.log(chalk30.red("Error:"), message);
|
|
74362
75788
|
}
|
|
74363
75789
|
process.exit(1);
|
|
74364
75790
|
}
|
|
74365
75791
|
});
|
|
74366
75792
|
keysCommand.command("create").description("Create a new API key").option("--name <name>", "Key name").option("--env <environment>", "Environment (development|production|eval)", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (opts) => {
|
|
74367
|
-
await
|
|
75793
|
+
await ensureAuth10();
|
|
74368
75794
|
if (!isValidEnvironment(opts.env)) {
|
|
74369
75795
|
const message = `Invalid environment: ${opts.env}`;
|
|
74370
75796
|
if (opts.json) {
|
|
74371
75797
|
console.log(JSON.stringify({ success: false, error: message }));
|
|
74372
75798
|
} else {
|
|
74373
|
-
console.log(
|
|
75799
|
+
console.log(chalk30.red("Error:"), message);
|
|
74374
75800
|
}
|
|
74375
75801
|
process.exit(1);
|
|
74376
75802
|
}
|
|
@@ -74382,7 +75808,7 @@ keysCommand.command("create").description("Create a new API key").option("--name
|
|
|
74382
75808
|
if (opts.json) {
|
|
74383
75809
|
console.log(JSON.stringify({ success: false, error: message }));
|
|
74384
75810
|
} else {
|
|
74385
|
-
console.log(
|
|
75811
|
+
console.log(chalk30.red("Error:"), message);
|
|
74386
75812
|
}
|
|
74387
75813
|
process.exit(1);
|
|
74388
75814
|
}
|
|
@@ -74394,19 +75820,19 @@ keysCommand.command("create").description("Create a new API key").option("--name
|
|
|
74394
75820
|
name = name.trim();
|
|
74395
75821
|
if (environment === "production" && !opts.confirm && !opts.json && isInteractive()) {
|
|
74396
75822
|
const ok = await confirm8({
|
|
74397
|
-
message:
|
|
75823
|
+
message: chalk30.yellow("Create API key in PRODUCTION environment?"),
|
|
74398
75824
|
default: false
|
|
74399
75825
|
});
|
|
74400
75826
|
if (!ok) {
|
|
74401
|
-
console.log(
|
|
75827
|
+
console.log(chalk30.gray("Cancelled"));
|
|
74402
75828
|
process.exit(0);
|
|
74403
75829
|
}
|
|
74404
75830
|
}
|
|
74405
|
-
const spinner = opts.json ? null :
|
|
75831
|
+
const spinner = opts.json ? null : ora22();
|
|
74406
75832
|
try {
|
|
74407
75833
|
spinner?.start("Creating API key");
|
|
74408
75834
|
const result = await withKeyAuthRetry(() => createApiKey({ name, environment }));
|
|
74409
|
-
spinner?.succeed(
|
|
75835
|
+
spinner?.succeed(chalk30.green(`Created API key ${chalk30.cyan(result.name)}`));
|
|
74410
75836
|
if (opts.json) {
|
|
74411
75837
|
console.log(JSON.stringify({
|
|
74412
75838
|
id: result.id,
|
|
@@ -74419,13 +75845,13 @@ keysCommand.command("create").description("Create a new API key").option("--name
|
|
|
74419
75845
|
return;
|
|
74420
75846
|
}
|
|
74421
75847
|
console.log();
|
|
74422
|
-
console.log(
|
|
75848
|
+
console.log(chalk30.bold(" " + chalk30.yellow("Save this key now — it will not be shown again.")));
|
|
74423
75849
|
console.log();
|
|
74424
|
-
console.log(` ${
|
|
74425
|
-
console.log(` ${
|
|
74426
|
-
console.log(` ${
|
|
74427
|
-
console.log(` ${
|
|
74428
|
-
console.log(` ${
|
|
75850
|
+
console.log(` ${chalk30.gray("Key:")} ${chalk30.cyan(result.key)}`);
|
|
75851
|
+
console.log(` ${chalk30.gray("Name:")} ${result.name}`);
|
|
75852
|
+
console.log(` ${chalk30.gray("Prefix:")} ${result.keyPrefix}`);
|
|
75853
|
+
console.log(` ${chalk30.gray("Environment:")} ${environment}`);
|
|
75854
|
+
console.log(` ${chalk30.gray("ID:")} ${result.id}`);
|
|
74429
75855
|
console.log();
|
|
74430
75856
|
} catch (err) {
|
|
74431
75857
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -74433,14 +75859,14 @@ keysCommand.command("create").description("Create a new API key").option("--name
|
|
|
74433
75859
|
if (opts.json) {
|
|
74434
75860
|
console.log(JSON.stringify({ success: false, error: message }));
|
|
74435
75861
|
} else {
|
|
74436
|
-
console.log(
|
|
75862
|
+
console.log(chalk30.red("Error:"), message);
|
|
74437
75863
|
}
|
|
74438
75864
|
process.exit(1);
|
|
74439
75865
|
}
|
|
74440
75866
|
});
|
|
74441
75867
|
keysCommand.command("revoke <id-or-prefix>").description("Revoke an API key (delete)").option("--confirm", "Skip confirmation prompt").option("--json", "Output raw JSON").action(async (idOrPrefix, opts) => {
|
|
74442
|
-
await
|
|
74443
|
-
const spinner = opts.json ? null :
|
|
75868
|
+
await ensureAuth10();
|
|
75869
|
+
const spinner = opts.json ? null : ora22();
|
|
74444
75870
|
try {
|
|
74445
75871
|
spinner?.start("Looking up API key");
|
|
74446
75872
|
const keys = await withKeyAuthRetry(() => listApiKeys());
|
|
@@ -74452,10 +75878,10 @@ keysCommand.command("revoke <id-or-prefix>").description("Revoke an API key (del
|
|
|
74452
75878
|
}
|
|
74453
75879
|
process.exit(1);
|
|
74454
75880
|
}
|
|
74455
|
-
spinner?.succeed(`Found ${
|
|
75881
|
+
spinner?.succeed(`Found ${chalk30.cyan(target.name)} (${maskedPrefix(target.keyPrefix)}, ${target.environment})`);
|
|
74456
75882
|
if (!opts.confirm && !opts.json) {
|
|
74457
75883
|
if (!isInteractive()) {
|
|
74458
|
-
console.log(
|
|
75884
|
+
console.log(chalk30.red("Error:"), "Use --confirm to revoke in non-interactive mode");
|
|
74459
75885
|
process.exit(1);
|
|
74460
75886
|
}
|
|
74461
75887
|
const ok = await confirm8({
|
|
@@ -74463,14 +75889,14 @@ keysCommand.command("revoke <id-or-prefix>").description("Revoke an API key (del
|
|
|
74463
75889
|
default: false
|
|
74464
75890
|
});
|
|
74465
75891
|
if (!ok) {
|
|
74466
|
-
console.log(
|
|
75892
|
+
console.log(chalk30.gray("Cancelled"));
|
|
74467
75893
|
process.exit(0);
|
|
74468
75894
|
}
|
|
74469
75895
|
}
|
|
74470
|
-
const revokeSpinner = opts.json ? null :
|
|
75896
|
+
const revokeSpinner = opts.json ? null : ora22();
|
|
74471
75897
|
revokeSpinner?.start("Revoking API key");
|
|
74472
75898
|
await withKeyAuthRetry(() => removeApiKey(target.id));
|
|
74473
|
-
revokeSpinner?.succeed(
|
|
75899
|
+
revokeSpinner?.succeed(chalk30.green(`Revoked ${chalk30.cyan(target.name)}`));
|
|
74474
75900
|
if (opts.json) {
|
|
74475
75901
|
console.log(JSON.stringify({ success: true, id: target.id, name: target.name }));
|
|
74476
75902
|
}
|
|
@@ -74480,7 +75906,7 @@ keysCommand.command("revoke <id-or-prefix>").description("Revoke an API key (del
|
|
|
74480
75906
|
if (opts.json) {
|
|
74481
75907
|
console.log(JSON.stringify({ success: false, error: message }));
|
|
74482
75908
|
} else {
|
|
74483
|
-
console.log(
|
|
75909
|
+
console.log(chalk30.red("Error:"), message);
|
|
74484
75910
|
}
|
|
74485
75911
|
process.exit(1);
|
|
74486
75912
|
}
|
|
@@ -74488,7 +75914,7 @@ keysCommand.command("revoke <id-or-prefix>").description("Revoke an API key (del
|
|
|
74488
75914
|
// package.json
|
|
74489
75915
|
var package_default = {
|
|
74490
75916
|
name: "struere",
|
|
74491
|
-
version: "0.
|
|
75917
|
+
version: "0.16.0",
|
|
74492
75918
|
description: "Build, test, and deploy AI agents",
|
|
74493
75919
|
keywords: [
|
|
74494
75920
|
"ai",
|
|
@@ -74527,13 +75953,17 @@ var package_default = {
|
|
|
74527
75953
|
"./client": {
|
|
74528
75954
|
import: "./dist/client/index.js",
|
|
74529
75955
|
types: "./dist/client/index.d.ts"
|
|
75956
|
+
},
|
|
75957
|
+
"./workflow-core": {
|
|
75958
|
+
import: "./dist/workflow-core/index.js",
|
|
75959
|
+
types: "./dist/workflow-core/index.d.ts"
|
|
74530
75960
|
}
|
|
74531
75961
|
},
|
|
74532
75962
|
files: [
|
|
74533
75963
|
"dist"
|
|
74534
75964
|
],
|
|
74535
75965
|
scripts: {
|
|
74536
|
-
build: "bun build ./src/cli/index.ts --outdir ./dist/cli --target node --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts --external jiti && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/client/index.ts --outdir ./dist/client --target browser && bun build ./src/bin/struere.ts --outdir ./dist/bin --target node --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts --external jiti && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
|
|
75966
|
+
build: "bun build ./src/cli/index.ts --outdir ./dist/cli --target node --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts --external jiti && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/client/index.ts --outdir ./dist/client --target browser && bun build ./src/workflow-core/index.ts --outdir ./dist/workflow-core --target node && bun build ./src/bin/struere.ts --outdir ./dist/bin --target node --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts --external jiti && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
|
|
74537
75967
|
dev: "tsc --watch",
|
|
74538
75968
|
test: "bun test",
|
|
74539
75969
|
prepublishOnly: "bun run build"
|
|
@@ -74616,6 +76046,7 @@ program.addCommand(evalCommand);
|
|
|
74616
76046
|
program.addCommand(templatesCommand);
|
|
74617
76047
|
program.addCommand(integrationCommand);
|
|
74618
76048
|
program.addCommand(triggersCommand);
|
|
76049
|
+
program.addCommand(workflowsCommand);
|
|
74619
76050
|
program.addCommand(threadsCommand);
|
|
74620
76051
|
program.addCommand(compilePromptCommand);
|
|
74621
76052
|
program.addCommand(runToolCommand);
|