struere 0.9.6 → 0.9.9

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 (46) hide show
  1. package/dist/bin/struere.js +623 -194
  2. package/dist/cli/commands/add.d.ts.map +1 -1
  3. package/dist/cli/commands/compile-prompt.d.ts +3 -0
  4. package/dist/cli/commands/compile-prompt.d.ts.map +1 -0
  5. package/dist/cli/commands/dev.d.ts.map +1 -1
  6. package/dist/cli/commands/entities.d.ts.map +1 -1
  7. package/dist/cli/commands/init.d.ts.map +1 -1
  8. package/dist/cli/commands/integration.d.ts.map +1 -1
  9. package/dist/cli/commands/org.d.ts +5 -0
  10. package/dist/cli/commands/org.d.ts.map +1 -0
  11. package/dist/cli/index.js +1027 -591
  12. package/dist/cli/utils/__tests__/plugin.test.d.ts +2 -0
  13. package/dist/cli/utils/__tests__/plugin.test.d.ts.map +1 -0
  14. package/dist/cli/utils/config.d.ts +2 -2
  15. package/dist/cli/utils/config.d.ts.map +1 -1
  16. package/dist/cli/utils/convex.d.ts +24 -1
  17. package/dist/cli/utils/convex.d.ts.map +1 -1
  18. package/dist/cli/utils/entities.d.ts.map +1 -1
  19. package/dist/cli/utils/evals.d.ts.map +1 -1
  20. package/dist/cli/utils/extractor.d.ts +3 -0
  21. package/dist/cli/utils/extractor.d.ts.map +1 -1
  22. package/dist/cli/utils/integrations.d.ts.map +1 -1
  23. package/dist/cli/utils/plugin.d.ts +1 -1
  24. package/dist/cli/utils/plugin.d.ts.map +1 -1
  25. package/dist/cli/utils/whatsapp.d.ts +4 -4
  26. package/dist/cli/utils/whatsapp.d.ts.map +1 -1
  27. package/dist/define/__tests__/agent.test.d.ts +2 -0
  28. package/dist/define/__tests__/agent.test.d.ts.map +1 -0
  29. package/dist/define/__tests__/entityType.test.d.ts +2 -0
  30. package/dist/define/__tests__/entityType.test.d.ts.map +1 -0
  31. package/dist/define/__tests__/role.test.d.ts +2 -0
  32. package/dist/define/__tests__/role.test.d.ts.map +1 -0
  33. package/dist/define/__tests__/trigger.test.d.ts +2 -0
  34. package/dist/define/__tests__/trigger.test.d.ts.map +1 -0
  35. package/dist/define/entityType.d.ts +1 -1
  36. package/dist/define/entityType.d.ts.map +1 -1
  37. package/dist/define/index.d.ts +1 -2
  38. package/dist/define/index.d.ts.map +1 -1
  39. package/dist/define/tools.d.ts +1 -0
  40. package/dist/define/tools.d.ts.map +1 -1
  41. package/dist/index.d.ts +2 -3
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +10 -35
  44. package/dist/types.d.ts +58 -21
  45. package/dist/types.d.ts.map +1 -1
  46. package/package.json +1 -1
@@ -19463,11 +19463,13 @@ function getApiKey() {
19463
19463
  return process.env.STRUERE_API_KEY || null;
19464
19464
  }
19465
19465
 
19466
- // src/cli/utils/convex.ts
19466
+ // src/cli/utils/config.ts
19467
19467
  var CONVEX_URL = process.env.STRUERE_CONVEX_URL || "https://rapid-wildebeest-172.convex.cloud";
19468
19468
  function getSiteUrl() {
19469
19469
  return CONVEX_URL.replace(".cloud", ".site");
19470
19470
  }
19471
+
19472
+ // src/cli/utils/convex.ts
19471
19473
  async function refreshToken() {
19472
19474
  const credentials = loadCredentials();
19473
19475
  if (!credentials?.sessionId)
@@ -19492,6 +19494,38 @@ async function refreshToken() {
19492
19494
  return null;
19493
19495
  }
19494
19496
  }
19497
+ async function createOrganization(token, name, slug) {
19498
+ const response = await fetch(`${CONVEX_URL}/api/action`, {
19499
+ method: "POST",
19500
+ headers: {
19501
+ "Content-Type": "application/json",
19502
+ Authorization: `Bearer ${token}`
19503
+ },
19504
+ body: JSON.stringify({
19505
+ path: "organizations:createFromCli",
19506
+ args: { name, slug }
19507
+ }),
19508
+ signal: AbortSignal.timeout(30000)
19509
+ });
19510
+ const text = await response.text();
19511
+ let json;
19512
+ try {
19513
+ json = JSON.parse(text);
19514
+ } catch {
19515
+ return { error: text || `HTTP ${response.status}` };
19516
+ }
19517
+ if (!response.ok) {
19518
+ const msg = json.errorData?.message || json.errorMessage || text;
19519
+ return { error: msg };
19520
+ }
19521
+ if (json.status === "success" && json.value) {
19522
+ return { organization: json.value };
19523
+ }
19524
+ if (json.status === "error") {
19525
+ return { error: json.errorData?.message || json.errorMessage || "Unknown error" };
19526
+ }
19527
+ return { error: `Unexpected response: ${text}` };
19528
+ }
19495
19529
  async function listMyOrganizations(token) {
19496
19530
  const response = await fetch(`${CONVEX_URL}/api/query`, {
19497
19531
  method: "POST",
@@ -19710,6 +19744,115 @@ async function getSyncState(organizationId, environment) {
19710
19744
  }
19711
19745
  return { error: `Unexpected response: ${JSON.stringify(result)}` };
19712
19746
  }
19747
+ async function compilePrompt(options) {
19748
+ const credentials = loadCredentials();
19749
+ const apiKey = getApiKey();
19750
+ if (apiKey && !credentials?.token) {
19751
+ const siteUrl = getSiteUrl();
19752
+ try {
19753
+ const response2 = await fetch(`${siteUrl}/v1/compile-prompt`, {
19754
+ method: "POST",
19755
+ headers: {
19756
+ "Content-Type": "application/json",
19757
+ Authorization: `Bearer ${apiKey}`
19758
+ },
19759
+ body: JSON.stringify({
19760
+ slug: options.slug,
19761
+ message: options.message,
19762
+ channel: options.channel,
19763
+ threadMetadata: options.threadMetadata
19764
+ }),
19765
+ signal: AbortSignal.timeout(30000)
19766
+ });
19767
+ const text2 = await response2.text();
19768
+ let json2;
19769
+ try {
19770
+ json2 = JSON.parse(text2);
19771
+ } catch {
19772
+ return { error: text2 || `HTTP ${response2.status}` };
19773
+ }
19774
+ if (!response2.ok) {
19775
+ return { error: json2.error || text2 };
19776
+ }
19777
+ return { result: json2 };
19778
+ } catch (err) {
19779
+ if (err instanceof DOMException && err.name === "TimeoutError") {
19780
+ return { error: "Request timed out after 30s" };
19781
+ }
19782
+ return { error: `Network error: ${err instanceof Error ? err.message : String(err)}` };
19783
+ }
19784
+ }
19785
+ if (credentials?.sessionId) {
19786
+ await refreshToken();
19787
+ }
19788
+ const freshCredentials = loadCredentials();
19789
+ const token = apiKey || freshCredentials?.token;
19790
+ if (!token) {
19791
+ return { error: "Not authenticated" };
19792
+ }
19793
+ const agentResponse = await fetch(`${CONVEX_URL}/api/query`, {
19794
+ method: "POST",
19795
+ headers: {
19796
+ "Content-Type": "application/json",
19797
+ Authorization: `Bearer ${token}`
19798
+ },
19799
+ body: JSON.stringify({
19800
+ path: "agents:getBySlug",
19801
+ args: { slug: options.slug }
19802
+ })
19803
+ });
19804
+ if (!agentResponse.ok) {
19805
+ return { error: await agentResponse.text() };
19806
+ }
19807
+ const agentResult = await agentResponse.json();
19808
+ if (agentResult.status === "error") {
19809
+ return { error: agentResult.errorMessage || "Failed to look up agent" };
19810
+ }
19811
+ if (!agentResult.value) {
19812
+ return { error: `Agent not found: ${options.slug}` };
19813
+ }
19814
+ const response = await fetch(`${CONVEX_URL}/api/action`, {
19815
+ method: "POST",
19816
+ headers: {
19817
+ "Content-Type": "application/json",
19818
+ Authorization: `Bearer ${token}`
19819
+ },
19820
+ body: JSON.stringify({
19821
+ path: "agents:compileSystemPrompt",
19822
+ args: {
19823
+ agentId: agentResult.value._id,
19824
+ environment: options.environment,
19825
+ sampleContext: {
19826
+ message: options.message,
19827
+ channel: options.channel,
19828
+ threadMetadata: options.threadMetadata
19829
+ }
19830
+ }
19831
+ }),
19832
+ signal: AbortSignal.timeout(30000)
19833
+ });
19834
+ const text = await response.text();
19835
+ let json;
19836
+ try {
19837
+ json = JSON.parse(text);
19838
+ } catch {
19839
+ return { error: text || `HTTP ${response.status}` };
19840
+ }
19841
+ if (!response.ok) {
19842
+ const msg = json.errorData?.message || json.errorMessage || text;
19843
+ return { error: msg };
19844
+ }
19845
+ if (json.status === "success" && json.value) {
19846
+ return { result: json.value };
19847
+ }
19848
+ if (json.status === "success" && json.value === null) {
19849
+ return { error: `Agent not found or no config for environment: ${options.environment}` };
19850
+ }
19851
+ if (json.status === "error") {
19852
+ return { error: json.errorData?.message || json.errorMessage || "Unknown error from Convex" };
19853
+ }
19854
+ return { error: `Unexpected response: ${text}` };
19855
+ }
19713
19856
  async function getPullState(organizationId, environment = "development") {
19714
19857
  const credentials = loadCredentials();
19715
19858
  const apiKey = getApiKey();
@@ -19802,6 +19945,7 @@ async function browserLoginInternal(spinner) {
19802
19945
  const authPromise = new Promise((resolve, reject) => {
19803
19946
  const server = Bun.serve({
19804
19947
  port: AUTH_CALLBACK_PORT,
19948
+ hostname: "127.0.0.1",
19805
19949
  async fetch(req) {
19806
19950
  const url = new URL(req.url);
19807
19951
  if (url.pathname === "/callback") {
@@ -19870,7 +20014,7 @@ async function browserLoginInternal(spinner) {
19870
20014
  if (organizations.length > 0) {
19871
20015
  console.log(source_default.gray("Organizations:"), organizations.map((o) => o.name).join(", "));
19872
20016
  } else {
19873
- console.log(source_default.yellow("No organizations found. Create one in the dashboard first."));
20017
+ console.log(source_default.yellow("No organizations found. Run"), source_default.cyan("struere org create"), source_default.yellow("to create one."));
19874
20018
  }
19875
20019
  console.log();
19876
20020
  return credentials;
@@ -20102,9 +20246,9 @@ logs/
20102
20246
  }
20103
20247
  function getEntityTypeTs(name, slug) {
20104
20248
  const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
20105
- return `import { defineEntityType } from 'struere'
20249
+ return `import { defineData } from 'struere'
20106
20250
 
20107
- export default defineEntityType({
20251
+ export default defineData({
20108
20252
  name: "${displayName}",
20109
20253
  slug: "${slug}",
20110
20254
  schema: {
@@ -20182,7 +20326,7 @@ export default defineTools([
20182
20326
  },
20183
20327
  },
20184
20328
  },
20185
- handler: async (args, context, fetch) => {
20329
+ handler: async (args, context, struere, fetch) => {
20186
20330
  const timezone = (args.timezone as string) || 'UTC'
20187
20331
  const now = new Date()
20188
20332
  return {
@@ -20211,7 +20355,7 @@ export default defineTools([
20211
20355
  },
20212
20356
  required: ['message'],
20213
20357
  },
20214
- handler: async (args, context, fetch) => {
20358
+ handler: async (args, context, struere, fetch) => {
20215
20359
  const webhookUrl = process.env.SLACK_WEBHOOK_URL
20216
20360
  if (!webhookUrl) {
20217
20361
  return { success: false, error: 'SLACK_WEBHOOK_URL not configured' }
@@ -20520,11 +20664,11 @@ function validateObjectProperties(schema, path) {
20520
20664
  }
20521
20665
  }
20522
20666
 
20523
- function defineEntityType(config) {
20524
- if (!config.name) throw new Error('Entity type name is required')
20525
- if (!config.slug) throw new Error('Entity type slug is required')
20526
- if (!config.schema) throw new Error('Entity type schema is required')
20527
- if (config.schema.type !== 'object') throw new Error('Entity type schema must be an object type')
20667
+ function defineData(config) {
20668
+ if (!config.name) throw new Error('Data type name is required')
20669
+ if (!config.slug) throw new Error('Data type slug is required')
20670
+ if (!config.schema) throw new Error('Data type schema is required')
20671
+ if (config.schema.type !== 'object') throw new Error('Data type schema must be an object type')
20528
20672
  if (config.schema.properties) {
20529
20673
  for (const [key, value] of Object.entries(config.schema.properties)) {
20530
20674
  validateObjectProperties(value, key)
@@ -20552,6 +20696,15 @@ function defineTrigger(config) {
20552
20696
  if (!action.tool) throw new Error('Trigger action tool is required')
20553
20697
  if (!action.args || typeof action.args !== 'object') throw new Error('Trigger action args must be an object')
20554
20698
  }
20699
+ if (config.schedule) {
20700
+ if (config.schedule.delay !== undefined && config.schedule.at !== undefined) throw new Error('Trigger schedule cannot have both "delay" and "at"')
20701
+ if (config.schedule.delay !== undefined && typeof config.schedule.delay !== 'number') throw new Error('Trigger schedule.delay must be a number')
20702
+ if (config.schedule.at !== undefined && typeof config.schedule.at !== 'string') throw new Error('Trigger schedule.at must be a string')
20703
+ }
20704
+ if (config.retry) {
20705
+ if (config.retry.maxAttempts !== undefined && (typeof config.retry.maxAttempts !== 'number' || config.retry.maxAttempts < 1)) throw new Error('Trigger retry.maxAttempts must be a positive number')
20706
+ if (config.retry.backoffMs !== undefined && (typeof config.retry.backoffMs !== 'number' || config.retry.backoffMs < 0)) throw new Error('Trigger retry.backoffMs must be a non-negative number')
20707
+ }
20555
20708
  return config
20556
20709
  }
20557
20710
 
@@ -20582,24 +20735,7 @@ function defineTools(tools) {
20582
20735
  })
20583
20736
  }
20584
20737
 
20585
- function defineConfig(config) {
20586
- const defaultConfig = {
20587
- port: 3000,
20588
- host: 'localhost',
20589
- cors: { origins: ['http://localhost:3000'], credentials: true },
20590
- logging: { level: 'info', format: 'pretty' },
20591
- auth: { type: 'none' },
20592
- }
20593
- return {
20594
- ...defaultConfig,
20595
- ...config,
20596
- cors: config.cors ? { ...defaultConfig.cors, ...config.cors } : defaultConfig.cors,
20597
- logging: config.logging ? { ...defaultConfig.logging, ...config.logging } : defaultConfig.logging,
20598
- auth: config.auth ? { ...defaultConfig.auth, ...config.auth } : defaultConfig.auth,
20599
- }
20600
- }
20601
-
20602
- export { defineAgent, defineRole, defineEntityType, defineTrigger, defineTools, defineConfig }
20738
+ export { defineAgent, defineRole, defineData, defineTrigger, defineTools }
20603
20739
  `;
20604
20740
  function registerStruerePlugin() {
20605
20741
  if (registered)
@@ -20645,11 +20781,6 @@ var TYPE_DECLARATIONS = `declare module 'struere' {
20645
20781
  export interface ToolContext {
20646
20782
  conversationId: string
20647
20783
  userId?: string
20648
- state: {
20649
- get<T>(key: string): Promise<T | undefined>
20650
- set<T>(key: string, value: T): Promise<void>
20651
- delete(key: string): Promise<void>
20652
- }
20653
20784
  }
20654
20785
 
20655
20786
  export type ToolHandler = (params: Record<string, unknown>, context: ToolContext) => Promise<unknown>
@@ -20678,6 +20809,12 @@ var TYPE_DECLARATIONS = `declare module 'struere' {
20678
20809
  model?: ModelConfig
20679
20810
  tools?: string[]
20680
20811
  firstMessageSuggestions?: string[]
20812
+ threadContextParams?: Array<{
20813
+ name: string
20814
+ type: 'string' | 'number' | 'boolean'
20815
+ required?: boolean
20816
+ description?: string
20817
+ }>
20681
20818
  }
20682
20819
 
20683
20820
  export interface JSONSchemaProperty {
@@ -20714,13 +20851,12 @@ var TYPE_DECLARATIONS = `declare module 'struere' {
20714
20851
  resource: string
20715
20852
  actions: string[]
20716
20853
  effect: 'allow' | 'deny'
20717
- priority?: number
20718
20854
  }
20719
20855
 
20720
20856
  export interface ScopeRuleConfig {
20721
20857
  entityType: string
20722
20858
  field: string
20723
- operator: 'eq' | 'ne' | 'in' | 'contains'
20859
+ operator: 'eq' | 'neq' | 'in' | 'contains'
20724
20860
  value: string
20725
20861
  }
20726
20862
 
@@ -20755,31 +20891,23 @@ var TYPE_DECLARATIONS = `declare module 'struere' {
20755
20891
  condition?: Record<string, unknown>
20756
20892
  }
20757
20893
  actions: TriggerAction[]
20758
- }
20759
-
20760
- export interface FrameworkConfig {
20761
- port?: number
20762
- host?: string
20763
- cors?: {
20764
- origins: string[]
20765
- credentials?: boolean
20766
- }
20767
- logging?: {
20768
- level: 'debug' | 'info' | 'warn' | 'error'
20769
- format?: 'json' | 'pretty'
20894
+ schedule?: {
20895
+ delay?: number
20896
+ at?: string
20897
+ offset?: number
20898
+ cancelPrevious?: boolean
20770
20899
  }
20771
- auth?: {
20772
- type: 'none' | 'api-key' | 'jwt' | 'custom'
20773
- validate?: (token: string) => Promise<boolean>
20900
+ retry?: {
20901
+ maxAttempts?: number
20902
+ backoffMs?: number
20774
20903
  }
20775
20904
  }
20776
20905
 
20777
20906
  export function defineAgent(config: AgentConfig): AgentConfig
20778
20907
  export function defineRole(config: RoleConfig): RoleConfig
20779
- export function defineEntityType(config: EntityTypeConfig): EntityTypeConfig
20908
+ export function defineData(config: EntityTypeConfig): EntityTypeConfig
20780
20909
  export function defineTrigger(config: TriggerConfig): TriggerConfig
20781
20910
  export function defineTools(tools: ToolDefinition[]): ToolReference[]
20782
- export function defineConfig(config?: Partial<FrameworkConfig>): FrameworkConfig
20783
20911
  }
20784
20912
  `;
20785
20913
  function generateTypeDeclarations(cwd) {
@@ -20950,7 +21078,7 @@ function buildProjectContext(orgName, resources) {
20950
21078
  }
20951
21079
  if (resources.entityTypes.length > 0) {
20952
21080
  lines.push("");
20953
- lines.push(`### Entity Types (${resources.entityTypes.length})`);
21081
+ lines.push(`### Data Types (${resources.entityTypes.length})`);
20954
21082
  for (const et of resources.entityTypes) {
20955
21083
  const fields = et.schema?.properties ? Object.keys(et.schema.properties).join(", ") : "";
20956
21084
  const fieldStr = fields ? ` \u2014 fields: ${fields}` : "";
@@ -20986,7 +21114,7 @@ function buildDocument(projectContext) {
20986
21114
  const lines = [];
20987
21115
  lines.push(`# Struere Workspace`);
20988
21116
  lines.push("");
20989
- lines.push(`> This is a Struere workspace project. You define agents, entity types, roles, triggers, and custom tools here. The CLI syncs them to the Convex backend.`);
21117
+ lines.push(`> This is a Struere workspace project. You define agents, data types, roles, triggers, and custom tools here. The CLI syncs them to the Convex backend.`);
20990
21118
  lines.push("");
20991
21119
  lines.push(`## Agent Usage`);
20992
21120
  lines.push("");
@@ -21006,7 +21134,7 @@ function buildDocument(projectContext) {
21006
21134
  lines.push("");
21007
21135
  lines.push("**JSON output**: Most commands support `--json` for structured output:");
21008
21136
  lines.push("```bash");
21009
- lines.push("struere entities list <type> --json");
21137
+ lines.push("struere data list <type> --json");
21010
21138
  lines.push("struere status --json");
21011
21139
  lines.push("struere deploy --json --force");
21012
21140
  lines.push("```");
@@ -21023,7 +21151,7 @@ function buildDocument(projectContext) {
21023
21151
  lines.push("");
21024
21152
  lines.push("```");
21025
21153
  lines.push("agents/ # Agent definitions (one file per agent)");
21026
- lines.push("entity-types/ # Entity type schemas (like DB tables)");
21154
+ lines.push("entity-types/ # Data type schemas (like DB tables)");
21027
21155
  lines.push("roles/ # RBAC roles with policies, scope rules, field masks");
21028
21156
  lines.push("triggers/ # Automation rules (react to entity changes)");
21029
21157
  lines.push("tools/index.ts # Custom tools shared by all agents");
@@ -21039,16 +21167,16 @@ function buildDocument(projectContext) {
21039
21167
  lines.push("| `struere sync` | One-shot sync to Convex and exit (agent-friendly) |");
21040
21168
  lines.push("| `struere dev` | Watch files and sync to Convex on save |");
21041
21169
  lines.push("| `struere deploy` | Push development config to production |");
21042
- lines.push("| `struere add agent\\|entity-type\\|role\\|trigger\\|eval\\|fixture <name>` | Scaffold a new resource |");
21170
+ lines.push("| `struere add agent\\|data-type\\|role\\|trigger\\|eval\\|fixture <name>` | Scaffold a new resource |");
21043
21171
  lines.push("| `struere status` | Compare local vs remote state |");
21044
21172
  lines.push("| `struere pull` | Download remote resources to local files |");
21045
- lines.push("| `struere entities types` | List entity types in an environment |");
21046
- lines.push("| `struere entities list <type>` | List entities (supports `--status`, `--limit`, `--json`) |");
21047
- lines.push("| `struere entities get <id>` | Get entity details |");
21048
- lines.push("| `struere entities create <type>` | Create entity (interactive or `--data <json>`) |");
21049
- lines.push("| `struere entities update <id>` | Update entity (`--data <json>`, `--status`) |");
21050
- lines.push("| `struere entities delete <id>` | Delete entity (with confirmation) |");
21051
- lines.push("| `struere entities search <type> <query>` | Search entities by text |");
21173
+ lines.push("| `struere data types` | List data types in an environment |");
21174
+ lines.push("| `struere data list <type>` | List records (supports `--status`, `--limit`, `--json`) |");
21175
+ lines.push("| `struere data get <id>` | Get record details |");
21176
+ lines.push("| `struere data create <type>` | Create record (interactive or `--data <json>`) |");
21177
+ lines.push("| `struere data update <id>` | Update record (`--data <json>`, `--status`) |");
21178
+ lines.push("| `struere data delete <id>` | Delete record (with confirmation) |");
21179
+ lines.push("| `struere data search <type> <query>` | Search records by text |");
21052
21180
  lines.push("| `struere eval run <suite>` | Run an eval suite and write Markdown results |");
21053
21181
  lines.push("| `struere eval run <suite> --case <name>` | Run specific case(s) by name |");
21054
21182
  lines.push("| `struere eval run <suite> --tag <tag>` | Run cases matching a tag |");
@@ -21060,7 +21188,7 @@ function buildDocument(projectContext) {
21060
21188
  lines.push("");
21061
21189
  lines.push(`## Key Patterns`);
21062
21190
  lines.push("");
21063
- lines.push("- **Imports**: `import { defineAgent, defineEntityType, defineRole, defineTrigger, defineTools } from 'struere'`");
21191
+ lines.push("- **Imports**: `import { defineAgent, defineData, defineRole, defineTrigger, defineTools } from 'struere'`");
21064
21192
  lines.push("- **Default model**: `grok-4-1-fast` (provider: `xai`). Also supports `anthropic`, `openai` and `google`");
21065
21193
  lines.push("- **Scope rule values**: `actor.userId`, `actor.entityId`, `actor.organizationId`, `actor.relatedIds:TYPE`, `literal:VALUE`");
21066
21194
  lines.push("- **Policy actions**: `create`, `read`, `update`, `delete`, `list` (deny overrides allow)");
@@ -21157,7 +21285,7 @@ function buildDocument(projectContext) {
21157
21285
  lines.push("### SDK");
21158
21286
  lines.push(`- [SDK Overview](${DOCS_BASE}/sdk/overview.md)`);
21159
21287
  lines.push(`- [defineAgent](${DOCS_BASE}/sdk/define-agent.md)`);
21160
- lines.push(`- [defineEntityType](${DOCS_BASE}/sdk/define-entity-type.md)`);
21288
+ lines.push(`- [defineData](${DOCS_BASE}/sdk/define-data.md)`);
21161
21289
  lines.push(`- [defineRole](${DOCS_BASE}/sdk/define-role.md)`);
21162
21290
  lines.push(`- [defineTrigger](${DOCS_BASE}/sdk/define-trigger.md)`);
21163
21291
  lines.push(`- [defineTools](${DOCS_BASE}/sdk/define-tools.md)`);
@@ -21168,7 +21296,7 @@ function buildDocument(projectContext) {
21168
21296
  lines.push(`- [System Prompt Templates](${DOCS_BASE}/tools/system-prompt-templates.md)`);
21169
21297
  lines.push("");
21170
21298
  lines.push("### Platform");
21171
- lines.push(`- [Entities](${DOCS_BASE}/platform/entities.md)`);
21299
+ lines.push(`- [Data](${DOCS_BASE}/platform/data.md)`);
21172
21300
  lines.push(`- [Permissions](${DOCS_BASE}/platform/permissions.md)`);
21173
21301
  lines.push(`- [Agents](${DOCS_BASE}/platform/agents.md)`);
21174
21302
  lines.push(`- [Events](${DOCS_BASE}/platform/events.md)`);
@@ -21335,6 +21463,112 @@ function isOrgAccessError(error) {
21335
21463
  return message.includes("Access denied") || message.includes("not a member") || message.includes("Organization not found");
21336
21464
  }
21337
21465
 
21466
+ // src/cli/commands/org.ts
21467
+ async function promptCreateOrg(token) {
21468
+ if (!isInteractive2()) {
21469
+ console.log(source_default.red("No organizations found. Run struere org create to create one."));
21470
+ return null;
21471
+ }
21472
+ const shouldCreate = await esm_default2({
21473
+ message: "No organizations found. Create one now?",
21474
+ default: true
21475
+ });
21476
+ if (!shouldCreate)
21477
+ return null;
21478
+ const name = await esm_default3({
21479
+ message: "Organization name:",
21480
+ validate: (v) => v.trim().length > 0 || "Name is required"
21481
+ });
21482
+ const slug = slugify(name.trim());
21483
+ const spinner = ora();
21484
+ spinner.start("Creating organization");
21485
+ const { organization, error } = await createOrganization(token, name.trim(), slug);
21486
+ if (error || !organization) {
21487
+ spinner.fail("Failed to create organization");
21488
+ console.log(source_default.red(error || "Unknown error"));
21489
+ return null;
21490
+ }
21491
+ spinner.succeed(`Created organization ${source_default.cyan(organization.name)} (${organization.slug})`);
21492
+ return organization;
21493
+ }
21494
+ var orgCommand = new Command("org").description("Manage organizations");
21495
+ orgCommand.command("list").description("List your organizations").option("--json", "Output as JSON").action(async (options) => {
21496
+ const credentials = loadCredentials();
21497
+ if (!credentials) {
21498
+ console.log(source_default.red("Not authenticated. Run struere login first."));
21499
+ process.exit(1);
21500
+ }
21501
+ await refreshToken();
21502
+ const fresh = loadCredentials();
21503
+ const token = fresh?.token || credentials.token;
21504
+ const spinner = ora();
21505
+ spinner.start("Fetching organizations");
21506
+ const { organizations, error } = await listMyOrganizations(token);
21507
+ if (error) {
21508
+ spinner.fail("Failed to fetch organizations");
21509
+ console.log(source_default.red(error));
21510
+ process.exit(1);
21511
+ }
21512
+ spinner.stop();
21513
+ if (options.json) {
21514
+ console.log(JSON.stringify(organizations, null, 2));
21515
+ return;
21516
+ }
21517
+ if (organizations.length === 0) {
21518
+ console.log(source_default.yellow("No organizations found."));
21519
+ console.log(source_default.gray("Run"), source_default.cyan("struere org create"), source_default.gray("to create one."));
21520
+ return;
21521
+ }
21522
+ console.log();
21523
+ console.log(source_default.bold("Organizations"));
21524
+ console.log();
21525
+ for (const org of organizations) {
21526
+ console.log(` ${source_default.cyan(org.name)} ${source_default.gray(`(${org.slug})`)} ${source_default.gray(`- ${org.role}`)}`);
21527
+ }
21528
+ console.log();
21529
+ });
21530
+ orgCommand.command("create").argument("[name]", "Organization name").description("Create a new organization").option("--slug <slug>", "Custom slug").option("--json", "Output as JSON").action(async (nameArg, options) => {
21531
+ const credentials = loadCredentials();
21532
+ if (!credentials) {
21533
+ console.log(source_default.red("Not authenticated. Run struere login first."));
21534
+ process.exit(1);
21535
+ }
21536
+ await refreshToken();
21537
+ const fresh = loadCredentials();
21538
+ const token = fresh?.token || credentials.token;
21539
+ let name = nameArg;
21540
+ if (!name) {
21541
+ if (!isInteractive2()) {
21542
+ console.log(source_default.red("Organization name is required in non-interactive mode."));
21543
+ process.exit(1);
21544
+ }
21545
+ name = await esm_default3({
21546
+ message: "Organization name:",
21547
+ validate: (v) => v.trim().length > 0 || "Name is required"
21548
+ });
21549
+ }
21550
+ name = name.trim();
21551
+ const slug = options.slug || slugify(name);
21552
+ const spinner = ora();
21553
+ spinner.start("Creating organization");
21554
+ const { organization, error } = await createOrganization(token, name, slug);
21555
+ if (error || !organization) {
21556
+ spinner.fail("Failed to create organization");
21557
+ console.log(source_default.red(error || "Unknown error"));
21558
+ process.exit(1);
21559
+ }
21560
+ spinner.stop();
21561
+ if (options.json) {
21562
+ console.log(JSON.stringify(organization, null, 2));
21563
+ return;
21564
+ }
21565
+ console.log(source_default.green("\u2713"), `Created organization ${source_default.cyan(organization.name)} (${organization.slug})`);
21566
+ console.log();
21567
+ console.log(source_default.gray("Next steps:"));
21568
+ console.log(source_default.gray(" \u2022"), source_default.cyan("struere init"), source_default.gray("- Initialize a project"));
21569
+ console.log();
21570
+ });
21571
+
21338
21572
  // src/cli/commands/init.ts
21339
21573
  async function runInit(cwd, selectedOrg) {
21340
21574
  const spinner = ora();
@@ -21363,10 +21597,11 @@ async function runInit(cwd, selectedOrg) {
21363
21597
  return false;
21364
21598
  }
21365
21599
  if (organizations.length === 0) {
21366
- console.log(source_default.red("No organizations found. Please create one in the dashboard first."));
21367
- return false;
21368
- }
21369
- if (organizations.length === 1) {
21600
+ const created = await promptCreateOrg(credentials.token);
21601
+ if (!created)
21602
+ return false;
21603
+ org = created;
21604
+ } else if (organizations.length === 1) {
21370
21605
  org = organizations[0];
21371
21606
  } else {
21372
21607
  org = await esm_default4({
@@ -21444,12 +21679,17 @@ var initCommand = new Command("init").description("Initialize a new Struere orga
21444
21679
  console.log(source_default.red("Failed to fetch organizations:"), error);
21445
21680
  process.exit(1);
21446
21681
  }
21447
- if (organizations.length === 0) {
21448
- console.log(source_default.red("No organizations found. Please create one in the dashboard first."));
21449
- process.exit(1);
21450
- }
21451
21682
  let selectedOrg;
21452
- if (options.org) {
21683
+ if (organizations.length === 0) {
21684
+ if (nonInteractive) {
21685
+ console.log(source_default.red("No organizations found. Run struere org create to create one."));
21686
+ process.exit(1);
21687
+ }
21688
+ const created = await promptCreateOrg(credentials?.token || "");
21689
+ if (!created)
21690
+ process.exit(1);
21691
+ selectedOrg = created;
21692
+ } else if (options.org) {
21453
21693
  const found = organizations.find((o) => o.slug === options.org);
21454
21694
  if (!found) {
21455
21695
  console.log(source_default.red(`Organization "${options.org}" not found.`));
@@ -21505,7 +21745,7 @@ var initCommand = new Command("init").description("Initialize a new Struere orga
21505
21745
  console.log();
21506
21746
  console.log(source_default.gray("Project structure:"));
21507
21747
  console.log(source_default.gray(" agents/ "), source_default.cyan("Agent definitions"));
21508
- console.log(source_default.gray(" entity-types/ "), source_default.cyan("Entity type schemas"));
21748
+ console.log(source_default.gray(" entity-types/ "), source_default.cyan("Data type schemas"));
21509
21749
  console.log(source_default.gray(" roles/ "), source_default.cyan("Role + permission definitions"));
21510
21750
  console.log(source_default.gray(" tools/ "), source_default.cyan("Shared custom tools"));
21511
21751
  console.log();
@@ -21563,7 +21803,9 @@ var BUILTIN_TOOLS = [
21563
21803
  "airtable.createRecords",
21564
21804
  "airtable.updateRecords",
21565
21805
  "airtable.deleteRecords",
21566
- "email.send"
21806
+ "email.send",
21807
+ "payment.create",
21808
+ "payment.getStatus"
21567
21809
  ];
21568
21810
  function extractSyncPayload(resources) {
21569
21811
  const customToolsMap = new Map;
@@ -21618,7 +21860,9 @@ function extractSyncPayload(resources) {
21618
21860
  userMessage: t.user,
21619
21861
  assertions: t.assertions
21620
21862
  })),
21621
- finalAssertions: c.finalAssertions
21863
+ finalAssertions: c.finalAssertions,
21864
+ channel: c.channel,
21865
+ contextParams: c.contextParams
21622
21866
  }))
21623
21867
  })) : undefined;
21624
21868
  const triggers = resources.triggers.length > 0 ? resources.triggers.map((t) => ({
@@ -21683,7 +21927,8 @@ function extractAgentPayload(agent, customToolsMap) {
21683
21927
  name: customTool.name,
21684
21928
  description: customTool.description,
21685
21929
  parameters: customTool.parameters || { type: "object", properties: {} },
21686
- handlerCode: extractHandlerCode(customTool._originalHandler || customTool.handler)
21930
+ handlerCode: extractHandlerCode(customTool._originalHandler || customTool.handler),
21931
+ ...customTool.templateOnly && { templateOnly: true }
21687
21932
  };
21688
21933
  });
21689
21934
  return {
@@ -21734,7 +21979,9 @@ function getBuiltinToolDescription(name) {
21734
21979
  "airtable.createRecords": "Create up to 10 records in an Airtable table",
21735
21980
  "airtable.updateRecords": "Update up to 10 records in an Airtable table",
21736
21981
  "airtable.deleteRecords": "Delete up to 10 records from an Airtable table",
21737
- "email.send": "Send an email via Resend"
21982
+ "email.send": "Send an email via Resend",
21983
+ "payment.create": "Create a payment link via Flow.cl and return the URL",
21984
+ "payment.getStatus": "Check the current status of a payment"
21738
21985
  };
21739
21986
  return descriptions[name] || name;
21740
21987
  }
@@ -22042,6 +22289,24 @@ function getBuiltinToolParameters(name) {
22042
22289
  replyTo: { type: "string", description: "Reply-to email address" }
22043
22290
  },
22044
22291
  required: ["to", "subject"]
22292
+ },
22293
+ "payment.create": {
22294
+ type: "object",
22295
+ properties: {
22296
+ amount: { type: "number", description: "Payment amount in the smallest currency unit" },
22297
+ description: { type: "string", description: "Description of the payment" },
22298
+ currency: { type: "string", description: "Currency code (defaults to CLP)" },
22299
+ customerEmail: { type: "string", description: "Customer email address" },
22300
+ entityId: { type: "string", description: "Optional entity ID to link the payment to" }
22301
+ },
22302
+ required: ["amount", "description"]
22303
+ },
22304
+ "payment.getStatus": {
22305
+ type: "object",
22306
+ properties: {
22307
+ entityId: { type: "string", description: "Payment entity ID to check status for" }
22308
+ },
22309
+ required: ["entityId"]
22045
22310
  }
22046
22311
  };
22047
22312
  return schemas[name] || { type: "object", properties: {} };
@@ -22118,7 +22383,7 @@ async function checkForDeletions(resources, organizationId, environment) {
22118
22383
  deletions.push({ type: "Agents", remote: remoteState.agents.length, local: payload.agents.length, deleted: deletedAgents });
22119
22384
  const deletedEntityTypes = remoteState.entityTypes.filter((et) => !localSlugs.entityTypes.has(et.slug)).map((et) => et.name);
22120
22385
  if (deletedEntityTypes.length > 0)
22121
- deletions.push({ type: "Entity types", remote: remoteState.entityTypes.length, local: payload.entityTypes.length, deleted: deletedEntityTypes });
22386
+ deletions.push({ type: "Data types", remote: remoteState.entityTypes.length, local: payload.entityTypes.length, deleted: deletedEntityTypes });
22122
22387
  const deletedRoles = remoteState.roles.filter((r) => !localSlugs.roles.has(r.name)).map((r) => r.name);
22123
22388
  if (deletedRoles.length > 0)
22124
22389
  deletions.push({ type: "Roles", remote: remoteState.roles.length, local: payload.roles.length, deleted: deletedRoles });
@@ -22223,7 +22488,7 @@ var syncCommand = new Command("sync").description("Sync resources to Convex and
22223
22488
  process.exit(1);
22224
22489
  }
22225
22490
  if (!jsonMode && !options.dryRun)
22226
- output.succeed(`Loaded ${resources.agents.length} agents, ${resources.entityTypes.length} entity types, ${resources.roles.length} roles`);
22491
+ output.succeed(`Loaded ${resources.agents.length} agents, ${resources.entityTypes.length} data types, ${resources.roles.length} roles`);
22227
22492
  } catch (error) {
22228
22493
  if (jsonMode) {
22229
22494
  console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }));
@@ -22255,7 +22520,7 @@ var syncCommand = new Command("sync").description("Sync resources to Convex and
22255
22520
  console.log(source_default.bold("Dry run \u2014 nothing will be synced"));
22256
22521
  console.log();
22257
22522
  console.log(source_default.gray(" Agents:"), payload.agents.map((a) => a.slug).join(", ") || "none");
22258
- console.log(source_default.gray(" Entity types:"), payload.entityTypes.map((et) => et.slug).join(", ") || "none");
22523
+ console.log(source_default.gray(" Data types:"), payload.entityTypes.map((et) => et.slug).join(", ") || "none");
22259
22524
  console.log(source_default.gray(" Roles:"), payload.roles.map((r) => r.name).join(", ") || "none");
22260
22525
  console.log(source_default.gray(" Triggers:"), (payload.triggers || []).map((t) => t.slug).join(", ") || "none");
22261
22526
  if (deletions.length > 0) {
@@ -22421,7 +22686,7 @@ var devCommand = new Command("dev").description("Watch files and sync to develop
22421
22686
  spinner.start("Loading resources");
22422
22687
  try {
22423
22688
  loadedResources = await loadAllResources(cwd);
22424
- spinner.succeed(`Loaded ${loadedResources.agents.length} agents, ${loadedResources.entityTypes.length} entity types, ${loadedResources.roles.length} roles, ${loadedResources.customTools.length} custom tools, ${loadedResources.evalSuites.length} eval suites, ${loadedResources.triggers.length} triggers, ${loadedResources.fixtures.length} fixtures`);
22689
+ spinner.succeed(`Loaded ${loadedResources.agents.length} agents, ${loadedResources.entityTypes.length} data types, ${loadedResources.roles.length} roles, ${loadedResources.customTools.length} custom tools, ${loadedResources.evalSuites.length} eval suites, ${loadedResources.triggers.length} triggers, ${loadedResources.fixtures.length} fixtures`);
22425
22690
  for (const err of loadedResources.errors) {
22426
22691
  console.log(source_default.red(" \u2716"), err);
22427
22692
  }
@@ -22524,9 +22789,15 @@ var devCommand = new Command("dev").description("Watch files and sync to develop
22524
22789
  persistent: true,
22525
22790
  usePolling: false
22526
22791
  });
22527
- const handleFileChange = async (path, action) => {
22528
- const relativePath = path.replace(cwd, ".");
22529
- console.log(source_default.gray(`${action}: ${relativePath}`));
22792
+ let debounceTimer = null;
22793
+ let pendingChanges = [];
22794
+ const triggerSync = async () => {
22795
+ const changes = [...pendingChanges];
22796
+ pendingChanges = [];
22797
+ for (const { path, action } of changes) {
22798
+ const relativePath = path.replace(cwd, ".");
22799
+ console.log(source_default.gray(`${action}: ${relativePath}`));
22800
+ }
22530
22801
  const syncSpinner = ora("Syncing...").start();
22531
22802
  try {
22532
22803
  await performDevSync(cwd, project.organization.id);
@@ -22554,6 +22825,15 @@ var devCommand = new Command("dev").description("Watch files and sync to develop
22554
22825
  }
22555
22826
  }
22556
22827
  };
22828
+ const handleFileChange = (path, action) => {
22829
+ pendingChanges.push({ path, action });
22830
+ if (debounceTimer)
22831
+ clearTimeout(debounceTimer);
22832
+ debounceTimer = setTimeout(() => {
22833
+ debounceTimer = null;
22834
+ triggerSync();
22835
+ }, 300);
22836
+ };
22557
22837
  watcher.on("change", (path) => handleFileChange(path, "Changed"));
22558
22838
  watcher.on("add", (path) => handleFileChange(path, "Added"));
22559
22839
  watcher.on("unlink", (path) => handleFileChange(path, "Removed"));
@@ -22634,7 +22914,7 @@ var deployCommand = new Command("deploy").description("Deploy all resources to p
22634
22914
  try {
22635
22915
  resources = await loadAllResources(cwd);
22636
22916
  if (!jsonMode)
22637
- spinner.succeed(`Loaded ${resources.agents.length} agents, ${resources.entityTypes.length} entity types, ${resources.roles.length} roles, ${resources.customTools.length} custom tools, ${resources.evalSuites.length} eval suites`);
22917
+ spinner.succeed(`Loaded ${resources.agents.length} agents, ${resources.entityTypes.length} data types, ${resources.roles.length} roles, ${resources.customTools.length} custom tools, ${resources.evalSuites.length} eval suites`);
22638
22918
  for (const err of resources.errors) {
22639
22919
  if (!jsonMode)
22640
22920
  console.log(source_default.red(" \u2716"), err);
@@ -22698,7 +22978,7 @@ var deployCommand = new Command("deploy").description("Deploy all resources to p
22698
22978
  console.log(source_default.gray(" -"), `${source_default.cyan(agent.name)} (${agent.slug}) v${agent.version}`);
22699
22979
  }
22700
22980
  console.log();
22701
- console.log("Entity types:");
22981
+ console.log("Data types:");
22702
22982
  for (const et of resources.entityTypes) {
22703
22983
  console.log(source_default.gray(" -"), source_default.cyan(et.name), `(${et.slug})`);
22704
22984
  }
@@ -22976,7 +23256,7 @@ var whoamiCommand = new Command("whoami").description("Show current logged in us
22976
23256
  });
22977
23257
 
22978
23258
  // src/cli/commands/add.ts
22979
- var addCommand = new Command("add").description("Scaffold a new resource").argument("<type>", "Resource type: agent, entity-type, role, eval, trigger, or fixture").argument("<name>", "Resource name").action(async (type, name) => {
23259
+ var addCommand = new Command("add").description("Scaffold a new resource").argument("<type>", "Resource type: agent, data-type, role, eval, trigger, or fixture").argument("<name>", "Resource name").action(async (type, name) => {
22980
23260
  const cwd = process.cwd();
22981
23261
  console.log();
22982
23262
  if (!hasProject(cwd)) {
@@ -23003,17 +23283,19 @@ var addCommand = new Command("add").description("Scaffold a new resource").argum
23003
23283
  console.log(source_default.yellow("Agent already exists:"), `agents/${slug}.ts`);
23004
23284
  }
23005
23285
  break;
23286
+ case "data-type":
23287
+ case "datatype":
23006
23288
  case "entity-type":
23007
23289
  case "entitytype":
23008
23290
  case "type":
23009
23291
  result = scaffoldEntityType(cwd, displayName, slug);
23010
23292
  if (result.createdFiles.length > 0) {
23011
- console.log(source_default.green("\u2713"), `Created entity type "${displayName}"`);
23293
+ console.log(source_default.green("\u2713"), `Created data type "${displayName}"`);
23012
23294
  for (const file of result.createdFiles) {
23013
23295
  console.log(source_default.gray(" \u2192"), file);
23014
23296
  }
23015
23297
  } else {
23016
- console.log(source_default.yellow("Entity type already exists:"), `entity-types/${slug}.ts`);
23298
+ console.log(source_default.yellow("Data type already exists:"), `entity-types/${slug}.ts`);
23017
23299
  }
23018
23300
  break;
23019
23301
  case "role":
@@ -23070,10 +23352,10 @@ var addCommand = new Command("add").description("Scaffold a new resource").argum
23070
23352
  console.log();
23071
23353
  console.log("Available types:");
23072
23354
  console.log(source_default.gray(" -"), source_default.cyan("agent"), "- Create an AI agent");
23073
- console.log(source_default.gray(" -"), source_default.cyan("entity-type"), "- Create an entity type schema");
23355
+ console.log(source_default.gray(" -"), source_default.cyan("data-type"), "- Create a data type schema");
23074
23356
  console.log(source_default.gray(" -"), source_default.cyan("role"), "- Create a role with permissions");
23075
23357
  console.log(source_default.gray(" -"), source_default.cyan("eval"), "- Create an eval suite (YAML)");
23076
- console.log(source_default.gray(" -"), source_default.cyan("trigger"), "- Create an entity trigger");
23358
+ console.log(source_default.gray(" -"), source_default.cyan("trigger"), "- Create a data trigger");
23077
23359
  console.log(source_default.gray(" -"), source_default.cyan("fixture"), "- Create a test data fixture (YAML)");
23078
23360
  console.log();
23079
23361
  process.exit(1);
@@ -23153,7 +23435,7 @@ var statusCommand = new Command("status").description("Compare local vs remote s
23153
23435
  try {
23154
23436
  localResources = await loadAllResources(cwd);
23155
23437
  if (!jsonMode) {
23156
- spinner.succeed(`Loaded ${localResources.agents.length} agents, ${localResources.entityTypes.length} entity types, ${localResources.roles.length} roles, ${localResources.customTools.length} custom tools, ${localResources.evalSuites.length} eval suites`);
23438
+ spinner.succeed(`Loaded ${localResources.agents.length} agents, ${localResources.entityTypes.length} data types, ${localResources.roles.length} roles, ${localResources.customTools.length} custom tools, ${localResources.evalSuites.length} eval suites`);
23157
23439
  for (const err of localResources.errors) {
23158
23440
  console.log(source_default.red(" \u2716"), err);
23159
23441
  }
@@ -23241,10 +23523,10 @@ var statusCommand = new Command("status").description("Compare local vs remote s
23241
23523
  }
23242
23524
  }
23243
23525
  console.log();
23244
- console.log(source_default.bold("Entity Types"));
23526
+ console.log(source_default.bold("Data Types"));
23245
23527
  console.log(source_default.gray("\u2500".repeat(60)));
23246
23528
  if (localResources.entityTypes.length === 0 && devState.entityTypes.length === 0) {
23247
- console.log(source_default.gray(" No entity types"));
23529
+ console.log(source_default.gray(" No data types"));
23248
23530
  } else {
23249
23531
  for (const et of localResources.entityTypes) {
23250
23532
  const remote = devState.entityTypes.find((r) => r.slug === et.slug);
@@ -23390,9 +23672,9 @@ function generateEntityTypeFile(entityType) {
23390
23672
  if (entityType.displayConfig) {
23391
23673
  parts.push(` displayConfig: ${stringifyValue(entityType.displayConfig, 2)}`);
23392
23674
  }
23393
- return `import { defineEntityType } from 'struere'
23675
+ return `import { defineData } from 'struere'
23394
23676
 
23395
- export default defineEntityType({
23677
+ export default defineData({
23396
23678
  ${parts.join(`,
23397
23679
  `)},
23398
23680
  })
@@ -23480,13 +23762,13 @@ export default defineTools([])
23480
23762
  ` parameters: ${stringifyValue(tool.parameters, 4)}`
23481
23763
  ];
23482
23764
  if (tool.handlerCode) {
23483
- parts.push(` handler: async (args, context, fetch) => {
23765
+ parts.push(` handler: async (args, context, struere, fetch) => {
23484
23766
  ${tool.handlerCode.split(`
23485
23767
  `).join(`
23486
23768
  `)}
23487
23769
  }`);
23488
23770
  } else {
23489
- parts.push(` handler: async (args, context, fetch) => {
23771
+ parts.push(` handler: async (args, context, struere, fetch) => {
23490
23772
  throw new Error("TODO: implement handler")
23491
23773
  }`);
23492
23774
  }
@@ -23776,7 +24058,6 @@ var pullCommand = new Command("pull").description("Pull remote resources to loca
23776
24058
  });
23777
24059
 
23778
24060
  // src/cli/utils/entities.ts
23779
- var CONVEX_URL2 = process.env.STRUERE_CONVEX_URL || "https://rapid-wildebeest-172.convex.cloud";
23780
24061
  function getToken() {
23781
24062
  const credentials = loadCredentials();
23782
24063
  const apiKey = getApiKey();
@@ -23786,7 +24067,7 @@ async function convexQuery(path, args) {
23786
24067
  const token = getToken();
23787
24068
  if (!token)
23788
24069
  return { error: "Not authenticated" };
23789
- const response = await fetch(`${CONVEX_URL2}/api/query`, {
24070
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
23790
24071
  method: "POST",
23791
24072
  headers: {
23792
24073
  "Content-Type": "application/json",
@@ -23815,7 +24096,7 @@ async function convexMutation(path, args) {
23815
24096
  const token = getToken();
23816
24097
  if (!token)
23817
24098
  return { error: "Not authenticated" };
23818
- const response = await fetch(`${CONVEX_URL2}/api/mutation`, {
24099
+ const response = await fetch(`${CONVEX_URL}/api/mutation`, {
23819
24100
  method: "POST",
23820
24101
  headers: {
23821
24102
  "Content-Type": "application/json",
@@ -24001,20 +24282,20 @@ function flattenEntityForTable(entity) {
24001
24282
  status: entity.status
24002
24283
  };
24003
24284
  }
24004
- var entitiesCommand = new Command("entities").description("Manage entity data");
24005
- entitiesCommand.command("types").description("List available entity types").option("--env <environment>", "Environment (development|production)", "development").option("--json", "Output raw JSON").action(async (opts) => {
24285
+ var entitiesCommand = new Command("data").description("Manage data records");
24286
+ entitiesCommand.command("types").description("List available data types").option("--env <environment>", "Environment (development|production)", "development").option("--json", "Output raw JSON").action(async (opts) => {
24006
24287
  await ensureAuth();
24007
24288
  const spinner = ora();
24008
24289
  const env2 = opts.env;
24009
24290
  const orgId = getOrgId();
24010
- spinner.start("Fetching entity types");
24291
+ spinner.start("Fetching data types");
24011
24292
  const { data, error } = await queryEntityTypes(env2, orgId);
24012
24293
  if (error || !data) {
24013
- spinner.fail("Failed to fetch entity types");
24294
+ spinner.fail("Failed to fetch data types");
24014
24295
  console.log(source_default.red("Error:"), error);
24015
24296
  process.exit(1);
24016
24297
  }
24017
- spinner.succeed("Entity types loaded");
24298
+ spinner.succeed("Data types loaded");
24018
24299
  const types = data;
24019
24300
  if (opts.json) {
24020
24301
  console.log(JSON.stringify(types, null, 2));
@@ -24037,12 +24318,12 @@ entitiesCommand.command("types").description("List available entity types").opti
24037
24318
  }));
24038
24319
  console.log();
24039
24320
  });
24040
- entitiesCommand.command("list <type>").description("List entities of a type").option("--env <environment>", "Environment (development|production)", "development").option("--status <status>", "Filter by status").option("--limit <n>", "Maximum results", "50").option("--json", "Output raw JSON").action(async (type, opts) => {
24321
+ entitiesCommand.command("list <type>").description("List records of a type").option("--env <environment>", "Environment (development|production)", "development").option("--status <status>", "Filter by status").option("--limit <n>", "Maximum results", "50").option("--json", "Output raw JSON").action(async (type, opts) => {
24041
24322
  await ensureAuth();
24042
24323
  const spinner = ora();
24043
24324
  const env2 = opts.env;
24044
24325
  const orgId = getOrgId();
24045
- spinner.start(`Fetching ${type} entities`);
24326
+ spinner.start(`Fetching ${type} records`);
24046
24327
  const [entitiesResult, typeResult] = await Promise.all([
24047
24328
  queryEntities(type, env2, {
24048
24329
  status: opts.status,
@@ -24051,12 +24332,12 @@ entitiesCommand.command("list <type>").description("List entities of a type").op
24051
24332
  queryEntityTypeBySlug(type, env2, orgId)
24052
24333
  ]);
24053
24334
  if (entitiesResult.error || !entitiesResult.data) {
24054
- spinner.fail(`Failed to fetch ${type} entities`);
24335
+ spinner.fail(`Failed to fetch ${type} records`);
24055
24336
  console.log(source_default.red("Error:"), entitiesResult.error);
24056
24337
  process.exit(1);
24057
24338
  }
24058
24339
  const entities = entitiesResult.data;
24059
- spinner.succeed(`Found ${entities.length} ${type} entities`);
24340
+ spinner.succeed(`Found ${entities.length} ${type} records`);
24060
24341
  if (opts.json) {
24061
24342
  console.log(JSON.stringify(entities, null, 2));
24062
24343
  return;
@@ -24069,27 +24350,27 @@ entitiesCommand.command("list <type>").description("List entities of a type").op
24069
24350
  renderTable(columns, entities.map(flattenEntityForTable));
24070
24351
  console.log();
24071
24352
  });
24072
- entitiesCommand.command("get <id>").description("Get entity details").option("--env <environment>", "Environment (development|production)", "development").option("--json", "Output raw JSON").action(async (rawId, opts) => {
24353
+ entitiesCommand.command("get <id>").description("Get record details").option("--env <environment>", "Environment (development|production)", "development").option("--json", "Output raw JSON").action(async (rawId, opts) => {
24073
24354
  await ensureAuth();
24074
24355
  const spinner = ora();
24075
24356
  const env2 = opts.env;
24076
24357
  const orgId = getOrgId();
24077
- spinner.start("Resolving entity ID");
24358
+ spinner.start("Resolving record ID");
24078
24359
  const resolved = await resolveEntityId(rawId, env2, orgId);
24079
24360
  if (resolved.error || !resolved.data) {
24080
- spinner.fail("Entity not found");
24081
- console.log(source_default.red("Error:"), resolved.error || `No entity matched "${rawId}"`);
24361
+ spinner.fail("Record not found");
24362
+ console.log(source_default.red("Error:"), resolved.error || `No record matched "${rawId}"`);
24082
24363
  process.exit(1);
24083
24364
  }
24084
24365
  const id = resolved.data;
24085
- spinner.text = "Fetching entity";
24366
+ spinner.text = "Fetching record";
24086
24367
  const { data, error } = await queryEntity(id, env2, orgId);
24087
24368
  if (error || !data) {
24088
- spinner.fail("Failed to fetch entity");
24089
- console.log(source_default.red("Error:"), error || "Entity not found");
24369
+ spinner.fail("Failed to fetch record");
24370
+ console.log(source_default.red("Error:"), error || "Record not found");
24090
24371
  process.exit(1);
24091
24372
  }
24092
- spinner.succeed("Entity loaded");
24373
+ spinner.succeed("Record loaded");
24093
24374
  const result = data;
24094
24375
  if (opts.json) {
24095
24376
  console.log(JSON.stringify(result, null, 2));
@@ -24117,7 +24398,7 @@ entitiesCommand.command("get <id>").description("Get entity details").option("--
24117
24398
  }
24118
24399
  console.log();
24119
24400
  });
24120
- entitiesCommand.command("create <type>").description("Create a new entity").option("--env <environment>", "Environment (development|production)", "development").option("--data <json>", "Entity data as JSON").option("--status <status>", "Initial status").option("--json", "Output raw JSON").action(async (type, opts) => {
24401
+ entitiesCommand.command("create <type>").description("Create a new record").option("--env <environment>", "Environment (development|production)", "development").option("--data <json>", "Record data as JSON").option("--status <status>", "Initial status").option("--json", "Output raw JSON").action(async (type, opts) => {
24121
24402
  await ensureAuth();
24122
24403
  const spinner = ora();
24123
24404
  const env2 = opts.env;
@@ -24137,7 +24418,7 @@ entitiesCommand.command("create <type>").description("Create a new entity").opti
24137
24418
  spinner.start(`Fetching ${type} schema`);
24138
24419
  const { data: typeData, error: error2 } = await queryEntityTypeBySlug(type, env2, orgId);
24139
24420
  if (error2 || !typeData) {
24140
- spinner.fail(`Entity type not found: ${type}`);
24421
+ spinner.fail(`Data type not found: ${type}`);
24141
24422
  console.log(source_default.red("Error:"), error2 || "Not found");
24142
24423
  process.exit(1);
24143
24424
  }
@@ -24146,7 +24427,7 @@ entitiesCommand.command("create <type>").description("Create a new entity").opti
24146
24427
  const entityType = typeData;
24147
24428
  const schema = entityType.schema;
24148
24429
  if (!schema?.properties) {
24149
- console.log(source_default.red("Entity type has no schema properties defined"));
24430
+ console.log(source_default.red("Data type has no schema properties defined"));
24150
24431
  process.exit(1);
24151
24432
  }
24152
24433
  data = {};
@@ -24175,14 +24456,14 @@ entitiesCommand.command("create <type>").description("Create a new entity").opti
24175
24456
  }
24176
24457
  console.log();
24177
24458
  }
24178
- spinner.start(`Creating ${type} entity`);
24459
+ spinner.start(`Creating ${type} record`);
24179
24460
  const { data: result, error } = await createEntity(type, data, env2, opts.status, orgId);
24180
24461
  if (error) {
24181
- spinner.fail("Failed to create entity");
24462
+ spinner.fail("Failed to create record");
24182
24463
  console.log(source_default.red("Error:"), error);
24183
24464
  process.exit(1);
24184
24465
  }
24185
- spinner.succeed(`Entity created`);
24466
+ spinner.succeed(`Record created`);
24186
24467
  if (opts.json) {
24187
24468
  console.log(JSON.stringify({ id: result }, null, 2));
24188
24469
  } else {
@@ -24191,7 +24472,7 @@ entitiesCommand.command("create <type>").description("Create a new entity").opti
24191
24472
  console.log();
24192
24473
  }
24193
24474
  });
24194
- entitiesCommand.command("update <id>").description("Update an entity").option("--env <environment>", "Environment (development|production)", "development").option("--data <json>", "Update data as JSON").option("--status <status>", "New status").option("--json", "Output raw JSON").action(async (rawId, opts) => {
24475
+ entitiesCommand.command("update <id>").description("Update a record").option("--env <environment>", "Environment (development|production)", "development").option("--data <json>", "Update data as JSON").option("--status <status>", "New status").option("--json", "Output raw JSON").action(async (rawId, opts) => {
24195
24476
  await ensureAuth();
24196
24477
  const spinner = ora();
24197
24478
  const env2 = opts.env;
@@ -24209,63 +24490,63 @@ entitiesCommand.command("update <id>").description("Update an entity").option("-
24209
24490
  process.exit(1);
24210
24491
  }
24211
24492
  }
24212
- spinner.start("Resolving entity ID");
24493
+ spinner.start("Resolving record ID");
24213
24494
  const resolved = await resolveEntityId(rawId, env2, orgId);
24214
24495
  if (resolved.error || !resolved.data) {
24215
- spinner.fail("Entity not found");
24216
- console.log(source_default.red("Error:"), resolved.error || `No entity matched "${rawId}"`);
24496
+ spinner.fail("Record not found");
24497
+ console.log(source_default.red("Error:"), resolved.error || `No record matched "${rawId}"`);
24217
24498
  process.exit(1);
24218
24499
  }
24219
24500
  const id = resolved.data;
24220
- spinner.text = "Updating entity";
24501
+ spinner.text = "Updating record";
24221
24502
  const { data: result, error } = await updateEntity(id, data, env2, opts.status, orgId);
24222
24503
  if (error) {
24223
- spinner.fail("Failed to update entity");
24504
+ spinner.fail("Failed to update record");
24224
24505
  console.log(source_default.red("Error:"), error);
24225
24506
  process.exit(1);
24226
24507
  }
24227
- spinner.succeed("Entity updated");
24508
+ spinner.succeed("Record updated");
24228
24509
  if (opts.json) {
24229
24510
  console.log(JSON.stringify(result, null, 2));
24230
24511
  } else {
24231
24512
  console.log();
24232
- console.log(source_default.green(" Entity updated successfully"));
24513
+ console.log(source_default.green(" Record updated successfully"));
24233
24514
  console.log();
24234
24515
  }
24235
24516
  });
24236
- entitiesCommand.command("delete <id>").description("Delete an entity").option("--env <environment>", "Environment (development|production)", "development").option("--yes", "Skip confirmation").option("--json", "Output raw JSON").action(async (rawId, opts) => {
24517
+ entitiesCommand.command("delete <id>").description("Delete a record").option("--env <environment>", "Environment (development|production)", "development").option("--yes", "Skip confirmation").option("--json", "Output raw JSON").action(async (rawId, opts) => {
24237
24518
  await ensureAuth();
24238
24519
  const spinner = ora();
24239
24520
  const env2 = opts.env;
24240
24521
  const orgId = getOrgId();
24241
24522
  const jsonMode = !!opts.json;
24242
24523
  if (!jsonMode)
24243
- spinner.start("Resolving entity ID");
24524
+ spinner.start("Resolving record ID");
24244
24525
  const resolved = await resolveEntityId(rawId, env2, orgId);
24245
24526
  if (resolved.error || !resolved.data) {
24246
24527
  if (jsonMode) {
24247
- console.log(JSON.stringify({ success: false, error: resolved.error || `No entity matched "${rawId}"` }));
24528
+ console.log(JSON.stringify({ success: false, error: resolved.error || `No record matched "${rawId}"` }));
24248
24529
  } else {
24249
- spinner.fail("Entity not found");
24250
- console.log(source_default.red("Error:"), resolved.error || `No entity matched "${rawId}"`);
24530
+ spinner.fail("Record not found");
24531
+ console.log(source_default.red("Error:"), resolved.error || `No record matched "${rawId}"`);
24251
24532
  }
24252
24533
  process.exit(1);
24253
24534
  }
24254
24535
  const id = resolved.data;
24255
24536
  if (!jsonMode)
24256
- spinner.text = "Fetching entity";
24537
+ spinner.text = "Fetching record";
24257
24538
  const { data, error: fetchError } = await queryEntity(id, env2, orgId);
24258
24539
  if (fetchError || !data) {
24259
24540
  if (jsonMode) {
24260
- console.log(JSON.stringify({ success: false, error: fetchError || "Entity not found" }));
24541
+ console.log(JSON.stringify({ success: false, error: fetchError || "Record not found" }));
24261
24542
  } else {
24262
- spinner.fail("Failed to fetch entity");
24263
- console.log(source_default.red("Error:"), fetchError || "Entity not found");
24543
+ spinner.fail("Failed to fetch record");
24544
+ console.log(source_default.red("Error:"), fetchError || "Record not found");
24264
24545
  }
24265
24546
  process.exit(1);
24266
24547
  }
24267
24548
  if (!jsonMode)
24268
- spinner.succeed("Entity loaded");
24549
+ spinner.succeed("Record loaded");
24269
24550
  const result = data;
24270
24551
  const entity = result.entity;
24271
24552
  const entityType = result.entityType;
@@ -24283,7 +24564,7 @@ entitiesCommand.command("delete <id>").description("Delete an entity").option("-
24283
24564
  }
24284
24565
  if (!opts.yes && !jsonMode && isInteractive2()) {
24285
24566
  const confirmed = await esm_default2({
24286
- message: "Are you sure you want to delete this entity?",
24567
+ message: "Are you sure you want to delete this record?",
24287
24568
  default: false
24288
24569
  });
24289
24570
  if (!confirmed) {
@@ -24292,13 +24573,13 @@ entitiesCommand.command("delete <id>").description("Delete an entity").option("-
24292
24573
  }
24293
24574
  }
24294
24575
  if (!jsonMode)
24295
- spinner.start("Deleting entity");
24576
+ spinner.start("Deleting record");
24296
24577
  const { error } = await removeEntity(id, env2, orgId);
24297
24578
  if (error) {
24298
24579
  if (jsonMode) {
24299
24580
  console.log(JSON.stringify({ success: false, error }));
24300
24581
  } else {
24301
- spinner.fail("Failed to delete entity");
24582
+ spinner.fail("Failed to delete record");
24302
24583
  console.log(source_default.red("Error:"), error);
24303
24584
  }
24304
24585
  process.exit(1);
@@ -24306,11 +24587,11 @@ entitiesCommand.command("delete <id>").description("Delete an entity").option("-
24306
24587
  if (jsonMode) {
24307
24588
  console.log(JSON.stringify({ success: true, id }));
24308
24589
  } else {
24309
- spinner.succeed("Entity deleted");
24590
+ spinner.succeed("Record deleted");
24310
24591
  console.log();
24311
24592
  }
24312
24593
  });
24313
- entitiesCommand.command("search <type> <query>").description("Search entities").option("--env <environment>", "Environment (development|production)", "development").option("--limit <n>", "Maximum results", "25").option("--json", "Output raw JSON").action(async (type, query, opts) => {
24594
+ entitiesCommand.command("search <type> <query>").description("Search records").option("--env <environment>", "Environment (development|production)", "development").option("--limit <n>", "Maximum results", "25").option("--json", "Output raw JSON").action(async (type, query, opts) => {
24314
24595
  await ensureAuth();
24315
24596
  const spinner = ora();
24316
24597
  const env2 = opts.env;
@@ -24345,7 +24626,6 @@ import { join as join9 } from "path";
24345
24626
  import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
24346
24627
 
24347
24628
  // src/cli/utils/evals.ts
24348
- var CONVEX_URL3 = process.env.STRUERE_CONVEX_URL || "https://rapid-wildebeest-172.convex.cloud";
24349
24629
  function getToken2() {
24350
24630
  const credentials = loadCredentials();
24351
24631
  const apiKey = getApiKey();
@@ -24356,7 +24636,7 @@ function getToken2() {
24356
24636
  }
24357
24637
  async function convexQuery2(path, args) {
24358
24638
  const token = getToken2();
24359
- const response = await fetch(`${CONVEX_URL3}/api/query`, {
24639
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
24360
24640
  method: "POST",
24361
24641
  headers: {
24362
24642
  "Content-Type": "application/json",
@@ -24381,7 +24661,7 @@ async function convexQuery2(path, args) {
24381
24661
  }
24382
24662
  async function convexMutation2(path, args) {
24383
24663
  const token = getToken2();
24384
- const response = await fetch(`${CONVEX_URL3}/api/mutation`, {
24664
+ const response = await fetch(`${CONVEX_URL}/api/mutation`, {
24385
24665
  method: "POST",
24386
24666
  headers: {
24387
24667
  "Content-Type": "application/json",
@@ -24798,10 +25078,6 @@ evalCommand.addCommand(runCommand);
24798
25078
  import { readFileSync as readFileSync4 } from "fs";
24799
25079
 
24800
25080
  // src/cli/utils/whatsapp.ts
24801
- var CONVEX_URL4 = process.env.STRUERE_CONVEX_URL || "https://rapid-wildebeest-172.convex.cloud";
24802
- function getSiteUrl2() {
24803
- return CONVEX_URL4.replace(".cloud", ".site");
24804
- }
24805
25081
  function getToken3() {
24806
25082
  const credentials = loadCredentials();
24807
25083
  const apiKey = getApiKey();
@@ -24811,7 +25087,7 @@ async function httpPost(path, body) {
24811
25087
  const apiKey = getApiKey();
24812
25088
  if (!apiKey)
24813
25089
  return { error: "Not authenticated" };
24814
- const siteUrl = getSiteUrl2();
25090
+ const siteUrl = getSiteUrl();
24815
25091
  try {
24816
25092
  const response = await fetch(`${siteUrl}${path}`, {
24817
25093
  method: "POST",
@@ -24844,7 +25120,7 @@ async function convexAction(path, args) {
24844
25120
  const token = getToken3();
24845
25121
  if (!token)
24846
25122
  return { error: "Not authenticated" };
24847
- const response = await fetch(`${CONVEX_URL4}/api/action`, {
25123
+ const response = await fetch(`${CONVEX_URL}/api/action`, {
24848
25124
  method: "POST",
24849
25125
  headers: {
24850
25126
  "Content-Type": "application/json",
@@ -24873,7 +25149,7 @@ async function convexQuery3(path, args) {
24873
25149
  const token = getToken3();
24874
25150
  if (!token)
24875
25151
  return { error: "Not authenticated" };
24876
- const response = await fetch(`${CONVEX_URL4}/api/query`, {
25152
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
24877
25153
  method: "POST",
24878
25154
  headers: {
24879
25155
  "Content-Type": "application/json",
@@ -24907,16 +25183,15 @@ async function listWhatsAppConnections(env2) {
24907
25183
  }
24908
25184
  return convexQuery3("whatsapp:listConnections", { environment: env2 });
24909
25185
  }
24910
- async function listTemplates(connectionId, env2) {
25186
+ async function listTemplates(connectionId) {
24911
25187
  if (getApiKey()) {
24912
25188
  return httpPost("/v1/templates/list", { connectionId });
24913
25189
  }
24914
25190
  return convexAction("whatsappActions:listTemplates", {
24915
- connectionId,
24916
- environment: env2
25191
+ connectionId
24917
25192
  });
24918
25193
  }
24919
- async function createTemplate(connectionId, env2, name, language, category, components, allowCategoryChange) {
25194
+ async function createTemplate(connectionId, name, language, category, components, allowCategoryChange) {
24920
25195
  if (getApiKey()) {
24921
25196
  return httpPost("/v1/templates/create", {
24922
25197
  connectionId,
@@ -24929,7 +25204,6 @@ async function createTemplate(connectionId, env2, name, language, category, comp
24929
25204
  }
24930
25205
  return convexAction("whatsappActions:createTemplate", {
24931
25206
  connectionId,
24932
- environment: env2,
24933
25207
  name,
24934
25208
  language,
24935
25209
  category,
@@ -24937,23 +25211,21 @@ async function createTemplate(connectionId, env2, name, language, category, comp
24937
25211
  ...allowCategoryChange !== undefined && { allowCategoryChange }
24938
25212
  });
24939
25213
  }
24940
- async function deleteTemplate(connectionId, env2, name) {
25214
+ async function deleteTemplate(connectionId, name) {
24941
25215
  if (getApiKey()) {
24942
25216
  return httpPost("/v1/templates/delete", { connectionId, name });
24943
25217
  }
24944
25218
  return convexAction("whatsappActions:deleteTemplate", {
24945
25219
  connectionId,
24946
- environment: env2,
24947
25220
  name
24948
25221
  });
24949
25222
  }
24950
- async function getTemplateStatus(connectionId, env2, name) {
25223
+ async function getTemplateStatus(connectionId, name) {
24951
25224
  if (getApiKey()) {
24952
25225
  return httpPost("/v1/templates/status", { connectionId, name });
24953
25226
  }
24954
25227
  return convexAction("whatsappActions:getTemplateStatus", {
24955
25228
  connectionId,
24956
- environment: env2,
24957
25229
  name
24958
25230
  });
24959
25231
  }
@@ -25049,13 +25321,12 @@ function statusColor2(status) {
25049
25321
  }
25050
25322
  }
25051
25323
  var templatesCommand = new Command("templates").description("Manage WhatsApp message templates");
25052
- templatesCommand.command("list").description("List all message templates").option("--env <environment>", "Environment (development|production)", "development").option("--connection <id>", "WhatsApp connection ID").option("--json", "Output raw JSON").action(async (opts) => {
25324
+ templatesCommand.command("list").description("List all message templates").option("--connection <id>", "WhatsApp connection ID").option("--json", "Output raw JSON").action(async (opts) => {
25053
25325
  await ensureAuth2();
25054
- const env2 = opts.env;
25055
- const connectionId = await resolveConnectionId(env2, opts.connection);
25326
+ const connectionId = await resolveConnectionId("development", opts.connection);
25056
25327
  const out = createOutput();
25057
25328
  out.start("Fetching templates");
25058
- const { data, error } = await listTemplates(connectionId, env2);
25329
+ const { data, error } = await listTemplates(connectionId);
25059
25330
  if (error) {
25060
25331
  out.fail("Failed to fetch templates");
25061
25332
  out.error(error);
@@ -25087,10 +25358,9 @@ templatesCommand.command("list").description("List all message templates").optio
25087
25358
  })));
25088
25359
  console.log();
25089
25360
  });
25090
- templatesCommand.command("create <name>").description("Create a new message template").option("--env <environment>", "Environment (development|production)", "development").option("--connection <id>", "WhatsApp connection ID").option("--language <code>", "Language code", "en_US").option("--category <cat>", "Category (UTILITY|MARKETING|AUTHENTICATION)", "UTILITY").option("--components <json>", "Components as JSON string").option("--file <path>", "Read components from a JSON file").option("--allow-category-change", "Allow Meta to reassign category").option("--json", "Output raw JSON").action(async (name, opts) => {
25361
+ templatesCommand.command("create <name>").description("Create a new message template").option("--connection <id>", "WhatsApp connection ID").option("--language <code>", "Language code", "en_US").option("--category <cat>", "Category (UTILITY|MARKETING|AUTHENTICATION)", "UTILITY").option("--components <json>", "Components as JSON string").option("--file <path>", "Read components from a JSON file").option("--allow-category-change", "Allow Meta to reassign category").option("--json", "Output raw JSON").action(async (name, opts) => {
25091
25362
  await ensureAuth2();
25092
- const env2 = opts.env;
25093
- const connectionId = await resolveConnectionId(env2, opts.connection);
25363
+ const connectionId = await resolveConnectionId("development", opts.connection);
25094
25364
  let components;
25095
25365
  if (opts.file) {
25096
25366
  try {
@@ -25118,7 +25388,7 @@ templatesCommand.command("create <name>").description("Create a new message temp
25118
25388
  }
25119
25389
  const out = createOutput();
25120
25390
  out.start(`Creating template "${name}"`);
25121
- const { data, error } = await createTemplate(connectionId, env2, name, opts.language, opts.category.toUpperCase(), components, opts.allowCategoryChange);
25391
+ const { data, error } = await createTemplate(connectionId, name, opts.language, opts.category.toUpperCase(), components, opts.allowCategoryChange);
25122
25392
  if (error) {
25123
25393
  out.fail("Failed to create template");
25124
25394
  out.error(error);
@@ -25136,10 +25406,9 @@ templatesCommand.command("create <name>").description("Create a new message temp
25136
25406
  console.log();
25137
25407
  }
25138
25408
  });
25139
- templatesCommand.command("delete <name>").description("Delete a message template").option("--env <environment>", "Environment (development|production)", "development").option("--connection <id>", "WhatsApp connection ID").option("--yes", "Skip confirmation").action(async (name, opts) => {
25409
+ templatesCommand.command("delete <name>").description("Delete a message template").option("--connection <id>", "WhatsApp connection ID").option("--yes", "Skip confirmation").action(async (name, opts) => {
25140
25410
  await ensureAuth2();
25141
- const env2 = opts.env;
25142
- const connectionId = await resolveConnectionId(env2, opts.connection);
25411
+ const connectionId = await resolveConnectionId("development", opts.connection);
25143
25412
  if (!opts.yes && isInteractive2()) {
25144
25413
  const confirmed = await esm_default2({
25145
25414
  message: `Delete template "${name}"? This cannot be undone.`,
@@ -25152,7 +25421,7 @@ templatesCommand.command("delete <name>").description("Delete a message template
25152
25421
  }
25153
25422
  const out = createOutput();
25154
25423
  out.start(`Deleting template "${name}"`);
25155
- const { error } = await deleteTemplate(connectionId, env2, name);
25424
+ const { error } = await deleteTemplate(connectionId, name);
25156
25425
  if (error) {
25157
25426
  out.fail("Failed to delete template");
25158
25427
  out.error(error);
@@ -25161,13 +25430,12 @@ templatesCommand.command("delete <name>").description("Delete a message template
25161
25430
  out.succeed(`Template "${name}" deleted`);
25162
25431
  console.log();
25163
25432
  });
25164
- templatesCommand.command("status <name>").description("Check template approval status").option("--env <environment>", "Environment (development|production)", "development").option("--connection <id>", "WhatsApp connection ID").option("--json", "Output raw JSON").action(async (name, opts) => {
25433
+ templatesCommand.command("status <name>").description("Check template approval status").option("--connection <id>", "WhatsApp connection ID").option("--json", "Output raw JSON").action(async (name, opts) => {
25165
25434
  await ensureAuth2();
25166
- const env2 = opts.env;
25167
- const connectionId = await resolveConnectionId(env2, opts.connection);
25435
+ const connectionId = await resolveConnectionId("development", opts.connection);
25168
25436
  const out = createOutput();
25169
25437
  out.start(`Checking status for "${name}"`);
25170
- const { data, error } = await getTemplateStatus(connectionId, env2, name);
25438
+ const { data, error } = await getTemplateStatus(connectionId, name);
25171
25439
  if (error) {
25172
25440
  out.fail("Failed to fetch template status");
25173
25441
  out.error(error);
@@ -25200,7 +25468,6 @@ templatesCommand.command("status <name>").description("Check template approval s
25200
25468
  });
25201
25469
 
25202
25470
  // src/cli/utils/integrations.ts
25203
- var CONVEX_URL5 = process.env.STRUERE_CONVEX_URL || "https://rapid-wildebeest-172.convex.cloud";
25204
25471
  function getToken4() {
25205
25472
  const credentials = loadCredentials();
25206
25473
  const apiKey = getApiKey();
@@ -25210,7 +25477,7 @@ async function convexQuery4(path, args) {
25210
25477
  const token = getToken4();
25211
25478
  if (!token)
25212
25479
  return { error: "Not authenticated" };
25213
- const response = await fetch(`${CONVEX_URL5}/api/query`, {
25480
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
25214
25481
  method: "POST",
25215
25482
  headers: {
25216
25483
  "Content-Type": "application/json",
@@ -25239,7 +25506,7 @@ async function convexMutation3(path, args) {
25239
25506
  const token = getToken4();
25240
25507
  if (!token)
25241
25508
  return { error: "Not authenticated" };
25242
- const response = await fetch(`${CONVEX_URL5}/api/mutation`, {
25509
+ const response = await fetch(`${CONVEX_URL}/api/mutation`, {
25243
25510
  method: "POST",
25244
25511
  headers: {
25245
25512
  "Content-Type": "application/json",
@@ -25268,7 +25535,7 @@ async function convexAction2(path, args) {
25268
25535
  const token = getToken4();
25269
25536
  if (!token)
25270
25537
  return { error: "Not authenticated" };
25271
- const response = await fetch(`${CONVEX_URL5}/api/action`, {
25538
+ const response = await fetch(`${CONVEX_URL}/api/action`, {
25272
25539
  method: "POST",
25273
25540
  headers: {
25274
25541
  "Content-Type": "application/json",
@@ -25313,7 +25580,7 @@ async function setIntegrationStatus(provider, env2, status) {
25313
25580
  }
25314
25581
 
25315
25582
  // src/cli/commands/integration.ts
25316
- var VALID_PROVIDERS = ["airtable", "resend"];
25583
+ var VALID_PROVIDERS = ["airtable", "resend", "flow"];
25317
25584
  async function ensureAuth3() {
25318
25585
  const cwd = process.cwd();
25319
25586
  const nonInteractive = !isInteractive2();
@@ -25366,6 +25633,8 @@ function getProviderHelp(provider) {
25366
25633
  return `Usage: struere integration airtable --token <pat> [--base-id <id>] [--test]`;
25367
25634
  case "resend":
25368
25635
  return `Usage: struere integration resend --from-email <email> [--from-name <name>] [--reply-to <email>]`;
25636
+ case "flow":
25637
+ return `Usage: struere integration flow --api-url <url> --api-key <key> --secret-key <secret> [--return-url <url>]`;
25369
25638
  default:
25370
25639
  return "";
25371
25640
  }
@@ -25393,9 +25662,23 @@ function buildConfigFromOpts(provider, opts) {
25393
25662
  return null;
25394
25663
  return config;
25395
25664
  }
25665
+ if (provider === "flow") {
25666
+ const config = {};
25667
+ if (opts.apiUrl)
25668
+ config.apiUrl = opts.apiUrl;
25669
+ if (opts.apiKey)
25670
+ config.apiKey = opts.apiKey;
25671
+ if (opts.secretKey)
25672
+ config.secretKey = opts.secretKey;
25673
+ if (opts.returnUrl)
25674
+ config.returnUrl = opts.returnUrl;
25675
+ if (Object.keys(config).length === 0)
25676
+ return null;
25677
+ return config;
25678
+ }
25396
25679
  return null;
25397
25680
  }
25398
- var integrationCommand = new Command("integration").description("Manage integrations").argument("[provider]", "Integration provider (airtable, resend)").option("--env <environment>", "Environment (development|production)", "development").option("--token <pat>", "Personal access token (airtable)").option("--base-id <id>", "Default base ID (airtable)").option("--from-email <email>", "From email address (resend)").option("--from-name <name>", "From display name (resend)").option("--reply-to <email>", "Reply-to address (resend)").option("--test", "Test the connection after saving").option("--remove", "Remove integration config").option("--enable", "Enable integration").option("--disable", "Disable integration").option("--status", "Show current config status").option("--yes", "Skip confirmation prompts").option("--json", "Output raw JSON").action(async (provider, opts) => {
25681
+ var integrationCommand = new Command("integration").description("Manage integrations").argument("[provider]", "Integration provider (airtable, resend, flow)").option("--env <environment>", "Environment (development|production)", "development").option("--token <pat>", "Personal access token (airtable)").option("--base-id <id>", "Default base ID (airtable)").option("--from-email <email>", "From email address (resend)").option("--from-name <name>", "From display name (resend)").option("--reply-to <email>", "Reply-to address (resend)").option("--api-url <url>", "API URL (flow)").option("--api-key <key>", "API key (flow)").option("--secret-key <secret>", "Secret key (flow)").option("--return-url <url>", "Return URL after payment (flow)").option("--test", "Test the connection after saving").option("--remove", "Remove integration config").option("--enable", "Enable integration").option("--disable", "Disable integration").option("--status", "Show current config status").option("--yes", "Skip confirmation prompts").option("--json", "Output raw JSON").action(async (provider, opts) => {
25399
25682
  await ensureAuth3();
25400
25683
  const env2 = opts.env;
25401
25684
  const out = createOutput();
@@ -25570,10 +25853,154 @@ var integrationCommand = new Command("integration").description("Manage integrat
25570
25853
  }
25571
25854
  console.log();
25572
25855
  });
25856
+
25857
+ // src/cli/commands/compile-prompt.ts
25858
+ var compilePromptCommand = new Command("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", "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) => {
25859
+ acc.push(val);
25860
+ return acc;
25861
+ }, []).option("--json", "Output full JSON (raw + compiled + context)").option("--raw", "Show raw uncompiled template instead of compiled").action(async (agentSlug, options) => {
25862
+ const spinner = ora();
25863
+ const cwd = process.cwd();
25864
+ const nonInteractive = !isInteractive2();
25865
+ const jsonMode = !!options.json;
25866
+ if (!hasProject(cwd)) {
25867
+ if (nonInteractive) {
25868
+ if (jsonMode) {
25869
+ console.log(JSON.stringify({ success: false, error: "No struere.json found" }));
25870
+ } else {
25871
+ console.log(source_default.red("No struere.json found. Run struere init first."));
25872
+ }
25873
+ process.exit(1);
25874
+ }
25875
+ console.log(source_default.yellow("No struere.json found - initializing project..."));
25876
+ console.log();
25877
+ const success = await runInit(cwd);
25878
+ if (!success) {
25879
+ process.exit(1);
25880
+ }
25881
+ console.log();
25882
+ }
25883
+ const project = loadProject(cwd);
25884
+ if (!project) {
25885
+ if (jsonMode) {
25886
+ console.log(JSON.stringify({ success: false, error: "Failed to load struere.json" }));
25887
+ } else {
25888
+ console.log(source_default.red("Failed to load struere.json"));
25889
+ }
25890
+ process.exit(1);
25891
+ }
25892
+ let credentials = loadCredentials();
25893
+ const apiKey = getApiKey();
25894
+ if (!credentials && !apiKey) {
25895
+ if (nonInteractive) {
25896
+ if (jsonMode) {
25897
+ console.log(JSON.stringify({ success: false, error: "Not authenticated. Set STRUERE_API_KEY or run struere login." }));
25898
+ } else {
25899
+ console.log(source_default.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
25900
+ }
25901
+ process.exit(1);
25902
+ }
25903
+ console.log(source_default.yellow("Not logged in - authenticating..."));
25904
+ console.log();
25905
+ credentials = await performLogin();
25906
+ if (!credentials) {
25907
+ console.log(source_default.red("Authentication failed"));
25908
+ process.exit(1);
25909
+ }
25910
+ console.log();
25911
+ }
25912
+ const threadMetadata = {};
25913
+ for (const param of options.param) {
25914
+ const eqIndex = param.indexOf("=");
25915
+ if (eqIndex === -1) {
25916
+ if (jsonMode) {
25917
+ console.log(JSON.stringify({ success: false, error: `Invalid param format: ${param}. Use key=value.` }));
25918
+ } else {
25919
+ console.log(source_default.red(`Invalid param format: ${param}. Use key=value.`));
25920
+ }
25921
+ process.exit(1);
25922
+ }
25923
+ const key = param.slice(0, eqIndex);
25924
+ const value = param.slice(eqIndex + 1);
25925
+ threadMetadata[key] = value;
25926
+ }
25927
+ const environment = options.env;
25928
+ if (!jsonMode) {
25929
+ spinner.start(`Compiling prompt for ${source_default.cyan(agentSlug)} (${environment})`);
25930
+ }
25931
+ const doCompile = async () => {
25932
+ return compilePrompt({
25933
+ slug: agentSlug,
25934
+ environment,
25935
+ message: options.message,
25936
+ channel: options.channel,
25937
+ threadMetadata: Object.keys(threadMetadata).length > 0 ? threadMetadata : undefined
25938
+ });
25939
+ };
25940
+ let { result, error } = await doCompile();
25941
+ if (error && isAuthError(error) && !nonInteractive) {
25942
+ if (!jsonMode)
25943
+ spinner.fail("Session expired - re-authenticating...");
25944
+ clearCredentials();
25945
+ credentials = await performLogin();
25946
+ if (!credentials) {
25947
+ if (jsonMode) {
25948
+ console.log(JSON.stringify({ success: false, error: "Authentication failed" }));
25949
+ } else {
25950
+ console.log(source_default.red("Authentication failed"));
25951
+ }
25952
+ process.exit(1);
25953
+ }
25954
+ const retry = await doCompile();
25955
+ result = retry.result;
25956
+ error = retry.error;
25957
+ if (!jsonMode && !error)
25958
+ spinner.succeed("Compiled prompt");
25959
+ }
25960
+ if (error) {
25961
+ if (jsonMode) {
25962
+ console.log(JSON.stringify({ success: false, error }));
25963
+ } else {
25964
+ spinner.fail("Failed to compile prompt");
25965
+ console.log(source_default.red("Error:"), error);
25966
+ }
25967
+ process.exit(1);
25968
+ }
25969
+ if (!result) {
25970
+ if (jsonMode) {
25971
+ console.log(JSON.stringify({ success: false, error: "No result returned" }));
25972
+ } else {
25973
+ spinner.fail("No result returned");
25974
+ }
25975
+ process.exit(1);
25976
+ }
25977
+ if (!jsonMode)
25978
+ spinner.succeed("Compiled prompt");
25979
+ if (jsonMode) {
25980
+ console.log(JSON.stringify({
25981
+ success: true,
25982
+ raw: result.raw,
25983
+ compiled: result.compiled,
25984
+ context: result.context
25985
+ }, null, 2));
25986
+ } else if (options.raw) {
25987
+ console.log();
25988
+ console.log(source_default.bold("Raw System Prompt"));
25989
+ console.log(source_default.gray("\u2500".repeat(60)));
25990
+ console.log(result.raw);
25991
+ console.log(source_default.gray("\u2500".repeat(60)));
25992
+ } else {
25993
+ console.log();
25994
+ console.log(source_default.bold("Compiled System Prompt"));
25995
+ console.log(source_default.gray("\u2500".repeat(60)));
25996
+ console.log(result.compiled);
25997
+ console.log(source_default.gray("\u2500".repeat(60)));
25998
+ }
25999
+ });
25573
26000
  // package.json
25574
26001
  var package_default = {
25575
26002
  name: "struere",
25576
- version: "0.9.6",
26003
+ version: "0.9.9",
25577
26004
  description: "Build, test, and deploy AI agents",
25578
26005
  keywords: [
25579
26006
  "ai",
@@ -25677,6 +26104,7 @@ program.addCommand(initCommand);
25677
26104
  program.addCommand(loginCommand);
25678
26105
  program.addCommand(logoutCommand);
25679
26106
  program.addCommand(whoamiCommand);
26107
+ program.addCommand(orgCommand);
25680
26108
  program.addCommand(syncCommand);
25681
26109
  program.addCommand(devCommand);
25682
26110
  program.addCommand(deployCommand);
@@ -25688,4 +26116,5 @@ program.addCommand(docsCommand);
25688
26116
  program.addCommand(evalCommand);
25689
26117
  program.addCommand(templatesCommand);
25690
26118
  program.addCommand(integrationCommand);
26119
+ program.addCommand(compilePromptCommand);
25691
26120
  program.parse();