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.
Files changed (44) hide show
  1. package/dist/bin/struere.js +1733 -302
  2. package/dist/cli/commands/add.d.ts.map +1 -1
  3. package/dist/cli/commands/dev.d.ts.map +1 -1
  4. package/dist/cli/commands/pull.d.ts.map +1 -1
  5. package/dist/cli/commands/status.d.ts.map +1 -1
  6. package/dist/cli/commands/sync.d.ts.map +1 -1
  7. package/dist/cli/commands/workflows.d.ts +3 -0
  8. package/dist/cli/commands/workflows.d.ts.map +1 -0
  9. package/dist/cli/index.js +1746 -304
  10. package/dist/cli/templates/index.d.ts +1 -0
  11. package/dist/cli/templates/index.d.ts.map +1 -1
  12. package/dist/cli/utils/convex.d.ts +70 -0
  13. package/dist/cli/utils/convex.d.ts.map +1 -1
  14. package/dist/cli/utils/extractor.d.ts +17 -0
  15. package/dist/cli/utils/extractor.d.ts.map +1 -1
  16. package/dist/cli/utils/generator.d.ts +3 -2
  17. package/dist/cli/utils/generator.d.ts.map +1 -1
  18. package/dist/cli/utils/loader.d.ts +3 -1
  19. package/dist/cli/utils/loader.d.ts.map +1 -1
  20. package/dist/cli/utils/plugin.d.ts +2 -2
  21. package/dist/cli/utils/plugin.d.ts.map +1 -1
  22. package/dist/cli/utils/scaffold.d.ts +1 -0
  23. package/dist/cli/utils/scaffold.d.ts.map +1 -1
  24. package/dist/cli/utils/workflows.d.ts +123 -0
  25. package/dist/cli/utils/workflows.d.ts.map +1 -0
  26. package/dist/define/__examples__/vip-payment-workflow.d.ts +3 -0
  27. package/dist/define/__examples__/vip-payment-workflow.d.ts.map +1 -0
  28. package/dist/define/workflow.d.ts +132 -0
  29. package/dist/define/workflow.d.ts.map +1 -0
  30. package/dist/index.d.ts +2 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +279 -1
  33. package/dist/types.d.ts +20 -0
  34. package/dist/types.d.ts.map +1 -1
  35. package/dist/workflow-core/catalog.d.ts +23 -0
  36. package/dist/workflow-core/catalog.d.ts.map +1 -0
  37. package/dist/workflow-core/expression.d.ts +3 -0
  38. package/dist/workflow-core/expression.d.ts.map +1 -0
  39. package/dist/workflow-core/index.d.ts +6 -0
  40. package/dist/workflow-core/index.d.ts.map +1 -0
  41. package/dist/workflow-core/index.js +168 -0
  42. package/dist/workflow-core/status.d.ts +5 -0
  43. package/dist/workflow-core/status.d.ts.map +1 -0
  44. package/package.json +6 -2
@@ -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/threads.ts
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/threads.ts
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 threadsApiCall(path, body) {
72022
- const credentials = loadCredentials();
72023
- const apiKey = getApiKey();
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(body),
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.error || text);
72513
+ throw new Error(json.errorData?.message || json.errorMessage || text);
72074
72514
  }
72075
- return json;
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 convexQuery7(path, args) {
72520
+ async function convexMutation6(path, args) {
72078
72521
  const token = getToken4();
72079
- const response = await fetch(`${CONVEX_URL}/api/query`, {
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 convexQuery7("threads:listWithPreviews", {
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 convexQuery7("threads:getWithMessages", {
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 = getToken4();
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 ensureAuth6() {
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(chalk21.red("No struere.json found. Run struere init first."));
73606
+ console.error(chalk22.red("No struere.json found. Run struere init first."));
72181
73607
  process.exit(1);
72182
73608
  }
72183
- console.log(chalk21.yellow("No struere.json found - initializing project..."));
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(chalk21.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
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(chalk21.yellow("Not logged in - authenticating..."));
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(chalk21.red("Authentication failed"));
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 relativeTime3(ts) {
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 chalk21.green(channel);
73652
+ return chalk22.green(channel);
72227
73653
  case "api":
72228
- return chalk21.blue(channel);
73654
+ return chalk22.blue(channel);
72229
73655
  case "widget":
72230
- return chalk21.magenta(channel);
73656
+ return chalk22.magenta(channel);
72231
73657
  case "dashboard":
72232
- return chalk21.cyan(channel);
73658
+ return chalk22.cyan(channel);
72233
73659
  case "voice":
72234
- return chalk21.yellow(channel);
73660
+ return chalk22.yellow(channel);
72235
73661
  default:
72236
- return chalk21.gray(channel ?? "-");
73662
+ return chalk22.gray(channel ?? "-");
72237
73663
  }
72238
73664
  }
72239
73665
  function roleColor(role) {
72240
73666
  switch (role) {
72241
73667
  case "user":
72242
- return chalk21.cyan(role);
73668
+ return chalk22.cyan(role);
72243
73669
  case "assistant":
72244
- return chalk21.green(role);
73670
+ return chalk22.green(role);
72245
73671
  case "system":
72246
- return chalk21.yellow(role);
73672
+ return chalk22.yellow(role);
72247
73673
  case "tool":
72248
- return chalk21.magenta(role);
73674
+ return chalk22.magenta(role);
72249
73675
  default:
72250
- return chalk21.gray(role);
73676
+ return chalk22.gray(role);
72251
73677
  }
72252
73678
  }
72253
- var threadsCommand = new Command19("threads").description("Manage conversation threads");
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 ensureAuth6();
72256
- const spinner = opts.json ? null : ora15();
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) : chalk21.gray("-"),
72283
- time: relativeTime3(t2.updatedAt ?? t2.createdAt)
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(chalk21.red("Error:"), message);
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 ensureAuth6();
72299
- const spinner = opts.json ? null : ora15();
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(` ${chalk21.gray("ID:")} ${chalk21.cyan(thread._id)}`);
72320
- console.log(` ${chalk21.gray("Channel:")} ${channelColor(thread.channel)}`);
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(` ${chalk21.gray("External:")} ${chalk21.cyan(thread.externalId)}`);
73748
+ console.log(` ${chalk22.gray("External:")} ${chalk22.cyan(thread.externalId)}`);
72323
73749
  }
72324
- console.log(` ${chalk21.gray("Agent:")} ${chalk21.cyan(thread.agentId)}`);
73750
+ console.log(` ${chalk22.gray("Agent:")} ${chalk22.cyan(thread.agentId)}`);
72325
73751
  if (thread.currentAgentId) {
72326
- console.log(` ${chalk21.gray("Current:")} ${chalk21.cyan(thread.currentAgentId)}`);
73752
+ console.log(` ${chalk22.gray("Current:")} ${chalk22.cyan(thread.currentAgentId)}`);
72327
73753
  }
72328
- console.log(` ${chalk21.gray("Created:")} ${chalk21.cyan(new Date(thread.createdAt).toISOString())}`);
72329
- console.log(` ${chalk21.gray("Updated:")} ${chalk21.cyan(new Date(thread.updatedAt).toISOString())}`);
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(chalk21.bold("Messages"));
72333
- console.log(chalk21.gray("─".repeat(60)));
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(` ${chalk21.gray(time)} ${role}: ${content}`);
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(chalk21.red("Error:"), message);
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 ensureAuth6();
72357
- const spinner = opts.json ? null : ora15();
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(chalk21.yellow(`WARNING: Archiving thread in PRODUCTION environment.
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(chalk21.green(`Thread ${id} archived`));
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(chalk21.red("Error:"), message);
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 ensureAuth6();
72391
- const spinner = opts.json ? null : ora15();
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(chalk21.yellow(`WARNING: Resetting thread in PRODUCTION environment.
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(chalk21.green(`Thread ${thread._id} archived (phone: ${opts.phone})`));
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(chalk21.red("Error:"), message);
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 Command20 } from "commander";
72440
- import chalk22 from "chalk";
72441
- import ora16 from "ora";
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 Command20("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) => {
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 = ora16();
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(chalk22.red("No struere.json found. Run struere init first."));
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(chalk22.yellow("No struere.json found - initializing project..."));
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(chalk22.red("Failed to load struere.json"));
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(chalk22.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
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(chalk22.yellow("Not logged in - authenticating..."));
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(chalk22.red("Authentication failed"));
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(chalk22.red(`Invalid param format: ${param}. Use key=value.`));
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 ${chalk22.cyan(agentSlug)} (${environment})`);
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(chalk22.red("Error:"), error);
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(chalk22.bold("Raw System Prompt"));
72565
- console.log(chalk22.gray("─".repeat(60)));
73990
+ console.log(chalk23.bold("Raw System Prompt"));
73991
+ console.log(chalk23.gray("─".repeat(60)));
72566
73992
  console.log(result.raw);
72567
- console.log(chalk22.gray("─".repeat(60)));
73993
+ console.log(chalk23.gray("─".repeat(60)));
72568
73994
  if (Object.keys(threadMetadata).length > 0) {
72569
- console.log(chalk22.gray("Params:"), Object.entries(threadMetadata).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", "));
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(chalk22.bold("Compiled System Prompt"));
72574
- console.log(chalk22.gray("─".repeat(60)));
73999
+ console.log(chalk23.bold("Compiled System Prompt"));
74000
+ console.log(chalk23.gray("─".repeat(60)));
72575
74001
  console.log(result.compiled);
72576
- console.log(chalk22.gray("─".repeat(60)));
74002
+ console.log(chalk23.gray("─".repeat(60)));
72577
74003
  if (Object.keys(threadMetadata).length > 0) {
72578
- console.log(chalk22.gray("Params:"), Object.entries(threadMetadata).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", "));
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 Command21 } from "commander";
72586
- import chalk23 from "chalk";
72587
- import ora17 from "ora";
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 Command21("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) => {
72590
- const spinner = ora17();
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(chalk23.red("No struere.json found. Run struere init first."));
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(chalk23.yellow("No struere.json found - initializing project..."));
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(chalk23.red("Failed to load struere.json"));
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(chalk23.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
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(chalk23.yellow("Not logged in - authenticating..."));
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(chalk23.red("Authentication failed"));
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(chalk23.red(`Invalid JSON: ${err instanceof Error ? err.message : String(err)}`));
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(chalk23.dim(`Tip: Running without agent context. Use --agent <slug> to run with a specific agent.`));
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(chalk23.yellow(`WARNING: Running tool against PRODUCTION environment.
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 ${chalk23.cyan(toolName)}${options.agent ? ` on ${chalk23.cyan(options.agent)}` : ""} (${environment})`);
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(chalk23.red("Error:"), error);
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(chalk23.red(`${result.errorType}: ${result.message}`));
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 ${chalk23.cyan(toolName)}${result.agent ? ` on ${chalk23.cyan(result.agent.slug)}` : ""} (${result.environment}) in ${result.durationMs}ms`);
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(chalk23.dim("─".repeat(50)));
74142
+ console.log(chalk24.dim("─".repeat(50)));
72717
74143
  console.log(JSON.stringify(result.result, null, 2));
72718
- console.log(chalk23.dim("─".repeat(50)));
74144
+ console.log(chalk24.dim("─".repeat(50)));
72719
74145
  console.log();
72720
- console.log(chalk23.dim(`Identity: ${result.identity.actorType} (${result.identity.identityMode} mode)`));
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 Command22 } from "commander";
72727
- import chalk24 from "chalk";
72728
- import ora18 from "ora";
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(chalk24.dim(`Model: ${meta.model} | Duration: ${meta.durationMs}ms`));
74158
+ console.log(chalk25.dim(`Model: ${meta.model} | Duration: ${meta.durationMs}ms`));
72733
74159
  if (meta.toolCallSummary.length > 0) {
72734
- console.log(chalk24.dim("Tool calls:"));
74160
+ console.log(chalk25.dim("Tool calls:"));
72735
74161
  for (const tc of meta.toolCallSummary) {
72736
- const status = tc.status === "success" ? chalk24.green("ok") : chalk24.red(tc.status);
72737
- const err = tc.errorMessage ? chalk24.red(` — ${tc.errorMessage}`) : "";
72738
- console.log(chalk24.dim(` ${tc.name} ${status} ${tc.durationMs}ms${err}`));
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(chalk24.dim(`Permission denials: ${chalk24.red(String(meta.permissionDenialCount))}`));
74168
+ console.log(chalk25.dim(`Permission denials: ${chalk25.red(String(meta.permissionDenialCount))}`));
72743
74169
  }
72744
74170
  if (meta.errorCount > 0) {
72745
- console.log(chalk24.dim(`Tool errors: ${chalk24.yellow(String(meta.errorCount))}`));
74171
+ console.log(chalk25.dim(`Tool errors: ${chalk25.yellow(String(meta.errorCount))}`));
72746
74172
  }
72747
74173
  }
72748
- var chatCommand = new Command22("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) => {
72749
- const spinner = ora18();
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(chalk24.red("No struere.json found. Run struere init first."));
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(chalk24.yellow("No struere.json found - initializing project..."));
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(chalk24.red("Failed to load struere.json"));
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(chalk24.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
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(chalk24.yellow("Not logged in - authenticating..."));
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(chalk24.red("Authentication failed"));
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(chalk24.red("--phone is required when using --router"));
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(chalk24.yellow(`WARNING: Chatting with agent in PRODUCTION environment.
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(chalk24.red("Authentication failed"));
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(chalk24.red("Error:"), error);
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(chalk24.green(`${routerResult.routedToAgent}`) + chalk24.dim(` (${routerResult.routedToAgentSlug})`) + chalk24.magenta(" ← routed") + chalk24.dim(":"));
74333
+ console.log(chalk25.green(`${routerResult.routedToAgent}`) + chalk25.dim(` (${routerResult.routedToAgentSlug})`) + chalk25.magenta(" ← routed") + chalk25.dim(":"));
72908
74334
  } else {
72909
- console.log(chalk24.green("Agent:"));
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(chalk24.dim(`Thread: ${result.threadId}`));
72915
- console.log(chalk24.dim(`Tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out (${result.usage.totalTokens} total)`));
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(chalk24.dim(`Thread: ${result.threadId} | Tokens: ${result.usage.totalTokens}`));
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 ${chalk24.cyan(slug)}` : chalk24.cyan(slug);
72932
- console.log(chalk24.bold(`Chat with ${headerLabel} (${environment})`));
72933
- console.log(chalk24.dim("Type 'exit' to quit"));
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(chalk24.cyan("You: "));
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(chalk24.yellow("Cancelled"));
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(chalk24.red("Authentication failed"));
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(chalk24.red("Error:"), error);
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(chalk24.green(`${interactiveRouterResult.routedToAgent}`) + chalk24.dim(` (${interactiveRouterResult.routedToAgentSlug})`) + chalk24.magenta(" ← routed") + chalk24.dim(":"));
74449
+ console.log(chalk25.green(`${interactiveRouterResult.routedToAgent}`) + chalk25.dim(` (${interactiveRouterResult.routedToAgentSlug})`) + chalk25.magenta(" ← routed") + chalk25.dim(":"));
73024
74450
  } else {
73025
- console.log(chalk24.green("Agent:"));
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(chalk24.dim(`Thread: ${result.threadId}`));
73031
- console.log(chalk24.dim(`Tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out (${result.usage.totalTokens} total)`));
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(chalk24.dim(`Thread: ${result.threadId} | Tokens: ${result.usage.totalTokens}`));
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(chalk24.dim("Goodbye!"));
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 Command23 } from "commander";
73054
- import chalk25 from "chalk";
73055
- async function ensureAuth7() {
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(chalk25.red("No struere.json found. Run struere init first."));
74486
+ console.error(chalk26.red("No struere.json found. Run struere init first."));
73061
74487
  process.exit(1);
73062
74488
  }
73063
- console.log(chalk25.yellow("No struere.json found - initializing project..."));
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(chalk25.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
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(chalk25.yellow("Not logged in - authenticating..."));
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(chalk25.red("Authentication failed"));
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 statusColor5(status) {
74548
+ function statusColor6(status) {
73123
74549
  switch (status) {
73124
74550
  case "connected":
73125
- return chalk25.green(status);
74551
+ return chalk26.green(status);
73126
74552
  case "pending_setup":
73127
- return chalk25.yellow("pending");
74553
+ return chalk26.yellow("pending");
73128
74554
  case "disconnected":
73129
- return chalk25.red(status);
74555
+ return chalk26.red(status);
73130
74556
  default:
73131
- return chalk25.gray(status);
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 chalk25.gray("none");
74569
+ return chalk26.gray("none");
73144
74570
  }
73145
- var whatsappCommand = new Command23("whatsapp").description("Manage WhatsApp connections and routing");
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 ensureAuth7();
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(chalk25.gray(" No WhatsApp connections found"));
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 || chalk25.gray("-"),
73177
- phone: c.phoneNumber ? `+${c.phoneNumber}` : chalk25.gray("-"),
73178
- status: statusColor5(c.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 ensureAuth7();
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 ensureAuth7();
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 Command24 } from "commander";
73251
- import chalk27 from "chalk";
73252
- import ora19 from "ora";
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 chalk26 from "chalk";
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(chalk26.red("- " + line.content));
74781
+ console.log(chalk27.red("- " + line.content));
73356
74782
  break;
73357
74783
  case "added":
73358
- console.log(chalk26.green("+ " + line.content));
74784
+ console.log(chalk27.green("+ " + line.content));
73359
74785
  break;
73360
74786
  case "context":
73361
- console.log(chalk26.gray(" " + line.content));
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(chalk27.cyan(` ${change.field}:`));
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(chalk27.red(`- ${t2}`));
74916
+ console.log(chalk28.red(`- ${t2}`));
73491
74917
  }
73492
74918
  for (const t2 of added) {
73493
- console.log(chalk27.green(`+ ${t2}`));
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 Command24("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) => {
73504
- const spinner = ora19();
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(chalk27.bold("Struere Diff"));
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(chalk27.red("No struere.json found. Run struere init first."));
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(chalk27.yellow("No struere.json found - initializing project..."));
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(chalk27.red("Failed to load struere.json"));
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(chalk27.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
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(chalk27.yellow("Not logged in - authenticating..."));
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(chalk27.red("Authentication failed"));
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(chalk27.red("Error:"), error2 instanceof Error ? error2.message : String(error2));
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(chalk27.red("Error:"), error || "Unknown error");
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(chalk27.bold(`struere diff`) + chalk27.gray(` (vs ${environment})`));
75152
+ console.log(chalk28.bold(`struere diff`) + chalk28.gray(` (vs ${environment})`));
73727
75153
  console.log();
73728
75154
  if (entries.length === 0) {
73729
- console.log(chalk27.green(" No differences found."));
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(chalk27.green(` ${filePath} (new — not yet synced)`));
75162
+ console.log(chalk28.green(` ${filePath} (new — not yet synced)`));
73737
75163
  } else if (entry.status === "deleted") {
73738
- console.log(chalk27.red(` ${filePath} (deleted — only on remote)`));
75164
+ console.log(chalk28.red(` ${filePath} (deleted — only on remote)`));
73739
75165
  } else if (entry.status === "modified") {
73740
- console.log(chalk27.yellow(` ${filePath} (modified)`));
75166
+ console.log(chalk28.yellow(` ${filePath} (modified)`));
73741
75167
  if (!opts.stat) {
73742
- console.log(chalk27.gray(" " + "─".repeat(37)));
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(chalk27.gray(` Summary: ${modified} modified, ${newCount} new, ${deleted} deleted`));
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 Command25 } from "commander";
73756
- import chalk28 from "chalk";
73757
- import ora20 from "ora";
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 ensureAuth8() {
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(chalk28.red("No struere.json found. Run struere init first."));
75192
+ console.error(chalk29.red("No struere.json found. Run struere init first."));
73767
75193
  process.exit(1);
73768
75194
  }
73769
- console.log(chalk28.yellow("No struere.json found - initializing project..."));
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(chalk28.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
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(chalk28.yellow("Not logged in - authenticating..."));
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(chalk28.red("Authentication failed"));
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 Command25("doctor").description("Run diagnostic checks on your Struere project").option("--env <environment>", "Environment to check", "development").option("--json", "Output raw JSON").action(async (opts) => {
74094
- await ensureAuth8();
74095
- const spinner = opts.json ? null : ora20();
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(chalk28.bold(`Struere Doctor`) + chalk28.gray(` (${environment})`));
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" ? chalk28.green("●") : check.status === "warn" ? chalk28.yellow("○") : chalk28.red("✖");
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(chalk28.green(`${summary.ok} passed`));
75563
+ parts.push(chalk29.green(`${summary.ok} passed`));
74138
75564
  if (summary.warn > 0)
74139
- parts.push(chalk28.yellow(`${summary.warn} warning${summary.warn > 1 ? "s" : ""}`));
75565
+ parts.push(chalk29.yellow(`${summary.warn} warning${summary.warn > 1 ? "s" : ""}`));
74140
75566
  if (summary.error > 0)
74141
- parts.push(chalk28.red(`${summary.error} error${summary.error > 1 ? "s" : ""}`));
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" ? chalk28.red : chalk28.yellow;
74148
- console.log(` ${color("→")} ${chalk28.dim(h.hint)}`);
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(chalk28.red("Error:"), message);
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 Command26 } from "commander";
74170
- import chalk29 from "chalk";
74171
- import ora21 from "ora";
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 getToken5() {
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 convexQuery8(path, args) {
74187
- const token = getToken5();
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 convexMutation6(path, args) {
74212
- const token = getToken5();
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 convexQuery8("apiKeys:list", {});
75663
+ return convexQuery9("apiKeys:list", {});
74238
75664
  }
74239
75665
  async function createApiKey(args) {
74240
- return convexMutation6("apiKeys:create", {
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 convexMutation6("apiKeys:remove", { id });
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 ensureAuth9() {
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(chalk29.red("No struere.json found. Run struere init first."));
75696
+ console.error(chalk30.red("No struere.json found. Run struere init first."));
74271
75697
  process.exit(1);
74272
75698
  }
74273
- console.log(chalk29.yellow("No struere.json found - initializing project..."));
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(chalk29.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
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(chalk29.yellow("Not logged in - authenticating..."));
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(chalk29.red("Authentication failed"));
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 relativeTime4(ts) {
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 Command26("keys").description("Manage API keys");
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 ensureAuth9();
74322
- const spinner = opts.json ? null : ora21();
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: relativeTime4(k.createdAt),
74352
- lastUsed: k.lastUsedAt ? relativeTime4(k.lastUsedAt) : chalk29.gray("-")
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(chalk29.red("Error:"), message);
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 ensureAuth9();
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(chalk29.red("Error:"), message);
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(chalk29.red("Error:"), message);
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: chalk29.yellow("Create API key in PRODUCTION environment?"),
75823
+ message: chalk30.yellow("Create API key in PRODUCTION environment?"),
74398
75824
  default: false
74399
75825
  });
74400
75826
  if (!ok) {
74401
- console.log(chalk29.gray("Cancelled"));
75827
+ console.log(chalk30.gray("Cancelled"));
74402
75828
  process.exit(0);
74403
75829
  }
74404
75830
  }
74405
- const spinner = opts.json ? null : ora21();
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(chalk29.green(`Created API key ${chalk29.cyan(result.name)}`));
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(chalk29.bold(" " + chalk29.yellow("Save this key now — it will not be shown again.")));
75848
+ console.log(chalk30.bold(" " + chalk30.yellow("Save this key now — it will not be shown again.")));
74423
75849
  console.log();
74424
- console.log(` ${chalk29.gray("Key:")} ${chalk29.cyan(result.key)}`);
74425
- console.log(` ${chalk29.gray("Name:")} ${result.name}`);
74426
- console.log(` ${chalk29.gray("Prefix:")} ${result.keyPrefix}`);
74427
- console.log(` ${chalk29.gray("Environment:")} ${environment}`);
74428
- console.log(` ${chalk29.gray("ID:")} ${result.id}`);
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(chalk29.red("Error:"), message);
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 ensureAuth9();
74443
- const spinner = opts.json ? null : ora21();
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 ${chalk29.cyan(target.name)} (${maskedPrefix(target.keyPrefix)}, ${target.environment})`);
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(chalk29.red("Error:"), "Use --confirm to revoke in non-interactive mode");
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(chalk29.gray("Cancelled"));
75892
+ console.log(chalk30.gray("Cancelled"));
74467
75893
  process.exit(0);
74468
75894
  }
74469
75895
  }
74470
- const revokeSpinner = opts.json ? null : ora21();
75896
+ const revokeSpinner = opts.json ? null : ora22();
74471
75897
  revokeSpinner?.start("Revoking API key");
74472
75898
  await withKeyAuthRetry(() => removeApiKey(target.id));
74473
- revokeSpinner?.succeed(chalk29.green(`Revoked ${chalk29.cyan(target.name)}`));
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(chalk29.red("Error:"), message);
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.15.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);