struere 0.9.7 → 0.9.10

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 (39) hide show
  1. package/dist/bin/struere.js +252 -113
  2. package/dist/cli/commands/dev.d.ts.map +1 -1
  3. package/dist/cli/commands/init.d.ts.map +1 -1
  4. package/dist/cli/commands/org.d.ts +5 -0
  5. package/dist/cli/commands/org.d.ts.map +1 -0
  6. package/dist/cli/index.js +692 -549
  7. package/dist/cli/utils/__tests__/plugin.test.d.ts +2 -0
  8. package/dist/cli/utils/__tests__/plugin.test.d.ts.map +1 -0
  9. package/dist/cli/utils/config.d.ts +2 -2
  10. package/dist/cli/utils/config.d.ts.map +1 -1
  11. package/dist/cli/utils/convex.d.ts +6 -1
  12. package/dist/cli/utils/convex.d.ts.map +1 -1
  13. package/dist/cli/utils/entities.d.ts.map +1 -1
  14. package/dist/cli/utils/evals.d.ts.map +1 -1
  15. package/dist/cli/utils/extractor.d.ts +1 -0
  16. package/dist/cli/utils/extractor.d.ts.map +1 -1
  17. package/dist/cli/utils/integrations.d.ts.map +1 -1
  18. package/dist/cli/utils/plugin.d.ts +1 -1
  19. package/dist/cli/utils/plugin.d.ts.map +1 -1
  20. package/dist/cli/utils/whatsapp.d.ts +4 -4
  21. package/dist/cli/utils/whatsapp.d.ts.map +1 -1
  22. package/dist/define/__tests__/agent.test.d.ts +2 -0
  23. package/dist/define/__tests__/agent.test.d.ts.map +1 -0
  24. package/dist/define/__tests__/entityType.test.d.ts +2 -0
  25. package/dist/define/__tests__/entityType.test.d.ts.map +1 -0
  26. package/dist/define/__tests__/role.test.d.ts +2 -0
  27. package/dist/define/__tests__/role.test.d.ts.map +1 -0
  28. package/dist/define/__tests__/trigger.test.d.ts +2 -0
  29. package/dist/define/__tests__/trigger.test.d.ts.map +1 -0
  30. package/dist/define/index.d.ts +0 -1
  31. package/dist/define/index.d.ts.map +1 -1
  32. package/dist/define/tools.d.ts +1 -0
  33. package/dist/define/tools.d.ts.map +1 -1
  34. package/dist/index.d.ts +1 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4 -29
  37. package/dist/types.d.ts +56 -21
  38. package/dist/types.d.ts.map +1 -1
  39. 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",
@@ -19980,7 +20014,7 @@ async function browserLoginInternal(spinner) {
19980
20014
  if (organizations.length > 0) {
19981
20015
  console.log(source_default.gray("Organizations:"), organizations.map((o) => o.name).join(", "));
19982
20016
  } else {
19983
- 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."));
19984
20018
  }
19985
20019
  console.log();
19986
20020
  return credentials;
@@ -20292,7 +20326,7 @@ export default defineTools([
20292
20326
  },
20293
20327
  },
20294
20328
  },
20295
- handler: async (args, context, fetch) => {
20329
+ handler: async (args, context, struere, fetch) => {
20296
20330
  const timezone = (args.timezone as string) || 'UTC'
20297
20331
  const now = new Date()
20298
20332
  return {
@@ -20321,7 +20355,7 @@ export default defineTools([
20321
20355
  },
20322
20356
  required: ['message'],
20323
20357
  },
20324
- handler: async (args, context, fetch) => {
20358
+ handler: async (args, context, struere, fetch) => {
20325
20359
  const webhookUrl = process.env.SLACK_WEBHOOK_URL
20326
20360
  if (!webhookUrl) {
20327
20361
  return { success: false, error: 'SLACK_WEBHOOK_URL not configured' }
@@ -20582,7 +20616,7 @@ function scaffoldFixture(cwd, name, slug) {
20582
20616
  }
20583
20617
 
20584
20618
  // src/cli/utils/plugin.ts
20585
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
20619
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
20586
20620
  import { join as join4 } from "path";
20587
20621
  var registered = false;
20588
20622
  var VIRTUAL_MODULE_SOURCE = `
@@ -20662,6 +20696,15 @@ function defineTrigger(config) {
20662
20696
  if (!action.tool) throw new Error('Trigger action tool is required')
20663
20697
  if (!action.args || typeof action.args !== 'object') throw new Error('Trigger action args must be an object')
20664
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
+ }
20665
20708
  return config
20666
20709
  }
20667
20710
 
@@ -20688,28 +20731,12 @@ function defineTools(tools) {
20688
20731
  parameters: tool.parameters,
20689
20732
  handler: wrapHandler(tool.name, tool.handler),
20690
20733
  _originalHandler: tool.handler,
20734
+ templateOnly: tool.templateOnly,
20691
20735
  }
20692
20736
  })
20693
20737
  }
20694
20738
 
20695
- function defineConfig(config) {
20696
- const defaultConfig = {
20697
- port: 3000,
20698
- host: 'localhost',
20699
- cors: { origins: ['http://localhost:3000'], credentials: true },
20700
- logging: { level: 'info', format: 'pretty' },
20701
- auth: { type: 'none' },
20702
- }
20703
- return {
20704
- ...defaultConfig,
20705
- ...config,
20706
- cors: config.cors ? { ...defaultConfig.cors, ...config.cors } : defaultConfig.cors,
20707
- logging: config.logging ? { ...defaultConfig.logging, ...config.logging } : defaultConfig.logging,
20708
- auth: config.auth ? { ...defaultConfig.auth, ...config.auth } : defaultConfig.auth,
20709
- }
20710
- }
20711
-
20712
- export { defineAgent, defineRole, defineData, defineTrigger, defineTools, defineConfig }
20739
+ export { defineAgent, defineRole, defineData, defineTrigger, defineTools }
20713
20740
  `;
20714
20741
  function registerStruerePlugin() {
20715
20742
  if (registered)
@@ -20755,11 +20782,6 @@ var TYPE_DECLARATIONS = `declare module 'struere' {
20755
20782
  export interface ToolContext {
20756
20783
  conversationId: string
20757
20784
  userId?: string
20758
- state: {
20759
- get<T>(key: string): Promise<T | undefined>
20760
- set<T>(key: string, value: T): Promise<void>
20761
- delete(key: string): Promise<void>
20762
- }
20763
20785
  }
20764
20786
 
20765
20787
  export type ToolHandler = (params: Record<string, unknown>, context: ToolContext) => Promise<unknown>
@@ -20769,6 +20791,7 @@ var TYPE_DECLARATIONS = `declare module 'struere' {
20769
20791
  description: string
20770
20792
  parameters: ToolParameters
20771
20793
  handler: ToolHandler
20794
+ templateOnly?: boolean
20772
20795
  }
20773
20796
 
20774
20797
  export interface ToolReference {
@@ -20777,6 +20800,7 @@ var TYPE_DECLARATIONS = `declare module 'struere' {
20777
20800
  parameters: ToolParameters
20778
20801
  handler: ToolHandler
20779
20802
  _originalHandler?: ToolHandler
20803
+ templateOnly?: boolean
20780
20804
  }
20781
20805
 
20782
20806
  export interface AgentConfig {
@@ -20788,6 +20812,12 @@ var TYPE_DECLARATIONS = `declare module 'struere' {
20788
20812
  model?: ModelConfig
20789
20813
  tools?: string[]
20790
20814
  firstMessageSuggestions?: string[]
20815
+ threadContextParams?: Array<{
20816
+ name: string
20817
+ type: 'string' | 'number' | 'boolean'
20818
+ required?: boolean
20819
+ description?: string
20820
+ }>
20791
20821
  }
20792
20822
 
20793
20823
  export interface JSONSchemaProperty {
@@ -20824,13 +20854,12 @@ var TYPE_DECLARATIONS = `declare module 'struere' {
20824
20854
  resource: string
20825
20855
  actions: string[]
20826
20856
  effect: 'allow' | 'deny'
20827
- priority?: number
20828
20857
  }
20829
20858
 
20830
20859
  export interface ScopeRuleConfig {
20831
20860
  entityType: string
20832
20861
  field: string
20833
- operator: 'eq' | 'ne' | 'in' | 'contains'
20862
+ operator: 'eq' | 'neq' | 'in' | 'contains'
20834
20863
  value: string
20835
20864
  }
20836
20865
 
@@ -20865,22 +20894,15 @@ var TYPE_DECLARATIONS = `declare module 'struere' {
20865
20894
  condition?: Record<string, unknown>
20866
20895
  }
20867
20896
  actions: TriggerAction[]
20868
- }
20869
-
20870
- export interface FrameworkConfig {
20871
- port?: number
20872
- host?: string
20873
- cors?: {
20874
- origins: string[]
20875
- credentials?: boolean
20876
- }
20877
- logging?: {
20878
- level: 'debug' | 'info' | 'warn' | 'error'
20879
- format?: 'json' | 'pretty'
20897
+ schedule?: {
20898
+ delay?: number
20899
+ at?: string
20900
+ offset?: number
20901
+ cancelPrevious?: boolean
20880
20902
  }
20881
- auth?: {
20882
- type: 'none' | 'api-key' | 'jwt' | 'custom'
20883
- validate?: (token: string) => Promise<boolean>
20903
+ retry?: {
20904
+ maxAttempts?: number
20905
+ backoffMs?: number
20884
20906
  }
20885
20907
  }
20886
20908
 
@@ -20889,7 +20911,6 @@ var TYPE_DECLARATIONS = `declare module 'struere' {
20889
20911
  export function defineData(config: EntityTypeConfig): EntityTypeConfig
20890
20912
  export function defineTrigger(config: TriggerConfig): TriggerConfig
20891
20913
  export function defineTools(tools: ToolDefinition[]): ToolReference[]
20892
- export function defineConfig(config?: Partial<FrameworkConfig>): FrameworkConfig
20893
20914
  }
20894
20915
  `;
20895
20916
  function generateTypeDeclarations(cwd) {
@@ -20901,10 +20922,14 @@ function generateTypeDeclarations(cwd) {
20901
20922
  writeFileSync3(join4(struereDir, "index.d.ts"), TYPE_DECLARATIONS);
20902
20923
  writeFileSync3(join4(struereDir, "index.js"), VIRTUAL_MODULE_SOURCE);
20903
20924
  const shimDir = join4(cwd, "node_modules", "struere");
20904
- mkdirSync3(shimDir, { recursive: true });
20905
- writeFileSync3(join4(shimDir, "index.js"), VIRTUAL_MODULE_SOURCE);
20906
- writeFileSync3(join4(shimDir, "index.d.ts"), TYPE_DECLARATIONS);
20907
- writeFileSync3(join4(shimDir, "package.json"), JSON.stringify({ name: "struere", version: "0.0.0", type: "module", main: "index.js", types: "index.d.ts" }));
20925
+ const realPkg = join4(shimDir, "package.json");
20926
+ const isRealPackage = existsSync4(realPkg) && JSON.parse(readFileSync3(realPkg, "utf8")).version !== "0.0.0";
20927
+ if (!isRealPackage) {
20928
+ mkdirSync3(shimDir, { recursive: true });
20929
+ writeFileSync3(join4(shimDir, "index.js"), VIRTUAL_MODULE_SOURCE);
20930
+ writeFileSync3(join4(shimDir, "index.d.ts"), TYPE_DECLARATIONS);
20931
+ writeFileSync3(join4(shimDir, "package.json"), JSON.stringify({ name: "struere", version: "0.0.0", type: "module", main: "index.js", types: "index.d.ts" }));
20932
+ }
20908
20933
  }
20909
20934
 
20910
20935
  // src/cli/commands/docs.ts
@@ -20913,7 +20938,7 @@ import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as wr
20913
20938
 
20914
20939
  // src/cli/utils/loader.ts
20915
20940
  var import_yaml = __toESM(require_dist(), 1);
20916
- import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3 } from "fs";
20941
+ import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync4 } from "fs";
20917
20942
  import { join as join5, basename } from "path";
20918
20943
  var pluginRegistered = false;
20919
20944
  var importCounter = 0;
@@ -20998,7 +21023,7 @@ function loadEvalSuites(dir) {
20998
21023
  const files = readdirSync(dir).filter((f) => f.endsWith(".eval.yaml") || f.endsWith(".eval.yml"));
20999
21024
  for (const file of files) {
21000
21025
  try {
21001
- const content = readFileSync3(join5(dir, file), "utf-8");
21026
+ const content = readFileSync4(join5(dir, file), "utf-8");
21002
21027
  const parsed = import_yaml.default.parse(content);
21003
21028
  suites.push(parsed);
21004
21029
  } catch (err) {
@@ -21016,7 +21041,7 @@ function loadFixtures(dir) {
21016
21041
  const files = readdirSync(dir).filter((f) => f.endsWith(".fixture.yaml") || f.endsWith(".fixture.yml"));
21017
21042
  for (const file of files) {
21018
21043
  try {
21019
- const content = readFileSync3(join5(dir, file), "utf-8");
21044
+ const content = readFileSync4(join5(dir, file), "utf-8");
21020
21045
  const parsed = import_yaml.default.parse(content);
21021
21046
  fixtures.push(parsed);
21022
21047
  } catch (err) {
@@ -21445,6 +21470,112 @@ function isOrgAccessError(error) {
21445
21470
  return message.includes("Access denied") || message.includes("not a member") || message.includes("Organization not found");
21446
21471
  }
21447
21472
 
21473
+ // src/cli/commands/org.ts
21474
+ async function promptCreateOrg(token) {
21475
+ if (!isInteractive2()) {
21476
+ console.log(source_default.red("No organizations found. Run struere org create to create one."));
21477
+ return null;
21478
+ }
21479
+ const shouldCreate = await esm_default2({
21480
+ message: "No organizations found. Create one now?",
21481
+ default: true
21482
+ });
21483
+ if (!shouldCreate)
21484
+ return null;
21485
+ const name = await esm_default3({
21486
+ message: "Organization name:",
21487
+ validate: (v) => v.trim().length > 0 || "Name is required"
21488
+ });
21489
+ const slug = slugify(name.trim());
21490
+ const spinner = ora();
21491
+ spinner.start("Creating organization");
21492
+ const { organization, error } = await createOrganization(token, name.trim(), slug);
21493
+ if (error || !organization) {
21494
+ spinner.fail("Failed to create organization");
21495
+ console.log(source_default.red(error || "Unknown error"));
21496
+ return null;
21497
+ }
21498
+ spinner.succeed(`Created organization ${source_default.cyan(organization.name)} (${organization.slug})`);
21499
+ return organization;
21500
+ }
21501
+ var orgCommand = new Command("org").description("Manage organizations");
21502
+ orgCommand.command("list").description("List your organizations").option("--json", "Output as JSON").action(async (options) => {
21503
+ const credentials = loadCredentials();
21504
+ if (!credentials) {
21505
+ console.log(source_default.red("Not authenticated. Run struere login first."));
21506
+ process.exit(1);
21507
+ }
21508
+ await refreshToken();
21509
+ const fresh = loadCredentials();
21510
+ const token = fresh?.token || credentials.token;
21511
+ const spinner = ora();
21512
+ spinner.start("Fetching organizations");
21513
+ const { organizations, error } = await listMyOrganizations(token);
21514
+ if (error) {
21515
+ spinner.fail("Failed to fetch organizations");
21516
+ console.log(source_default.red(error));
21517
+ process.exit(1);
21518
+ }
21519
+ spinner.stop();
21520
+ if (options.json) {
21521
+ console.log(JSON.stringify(organizations, null, 2));
21522
+ return;
21523
+ }
21524
+ if (organizations.length === 0) {
21525
+ console.log(source_default.yellow("No organizations found."));
21526
+ console.log(source_default.gray("Run"), source_default.cyan("struere org create"), source_default.gray("to create one."));
21527
+ return;
21528
+ }
21529
+ console.log();
21530
+ console.log(source_default.bold("Organizations"));
21531
+ console.log();
21532
+ for (const org of organizations) {
21533
+ console.log(` ${source_default.cyan(org.name)} ${source_default.gray(`(${org.slug})`)} ${source_default.gray(`- ${org.role}`)}`);
21534
+ }
21535
+ console.log();
21536
+ });
21537
+ 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) => {
21538
+ const credentials = loadCredentials();
21539
+ if (!credentials) {
21540
+ console.log(source_default.red("Not authenticated. Run struere login first."));
21541
+ process.exit(1);
21542
+ }
21543
+ await refreshToken();
21544
+ const fresh = loadCredentials();
21545
+ const token = fresh?.token || credentials.token;
21546
+ let name = nameArg;
21547
+ if (!name) {
21548
+ if (!isInteractive2()) {
21549
+ console.log(source_default.red("Organization name is required in non-interactive mode."));
21550
+ process.exit(1);
21551
+ }
21552
+ name = await esm_default3({
21553
+ message: "Organization name:",
21554
+ validate: (v) => v.trim().length > 0 || "Name is required"
21555
+ });
21556
+ }
21557
+ name = name.trim();
21558
+ const slug = options.slug || slugify(name);
21559
+ const spinner = ora();
21560
+ spinner.start("Creating organization");
21561
+ const { organization, error } = await createOrganization(token, name, slug);
21562
+ if (error || !organization) {
21563
+ spinner.fail("Failed to create organization");
21564
+ console.log(source_default.red(error || "Unknown error"));
21565
+ process.exit(1);
21566
+ }
21567
+ spinner.stop();
21568
+ if (options.json) {
21569
+ console.log(JSON.stringify(organization, null, 2));
21570
+ return;
21571
+ }
21572
+ console.log(source_default.green("\u2713"), `Created organization ${source_default.cyan(organization.name)} (${organization.slug})`);
21573
+ console.log();
21574
+ console.log(source_default.gray("Next steps:"));
21575
+ console.log(source_default.gray(" \u2022"), source_default.cyan("struere init"), source_default.gray("- Initialize a project"));
21576
+ console.log();
21577
+ });
21578
+
21448
21579
  // src/cli/commands/init.ts
21449
21580
  async function runInit(cwd, selectedOrg) {
21450
21581
  const spinner = ora();
@@ -21473,10 +21604,11 @@ async function runInit(cwd, selectedOrg) {
21473
21604
  return false;
21474
21605
  }
21475
21606
  if (organizations.length === 0) {
21476
- console.log(source_default.red("No organizations found. Please create one in the dashboard first."));
21477
- return false;
21478
- }
21479
- if (organizations.length === 1) {
21607
+ const created = await promptCreateOrg(credentials.token);
21608
+ if (!created)
21609
+ return false;
21610
+ org = created;
21611
+ } else if (organizations.length === 1) {
21480
21612
  org = organizations[0];
21481
21613
  } else {
21482
21614
  org = await esm_default4({
@@ -21554,12 +21686,17 @@ var initCommand = new Command("init").description("Initialize a new Struere orga
21554
21686
  console.log(source_default.red("Failed to fetch organizations:"), error);
21555
21687
  process.exit(1);
21556
21688
  }
21557
- if (organizations.length === 0) {
21558
- console.log(source_default.red("No organizations found. Please create one in the dashboard first."));
21559
- process.exit(1);
21560
- }
21561
21689
  let selectedOrg;
21562
- if (options.org) {
21690
+ if (organizations.length === 0) {
21691
+ if (nonInteractive) {
21692
+ console.log(source_default.red("No organizations found. Run struere org create to create one."));
21693
+ process.exit(1);
21694
+ }
21695
+ const created = await promptCreateOrg(credentials?.token || "");
21696
+ if (!created)
21697
+ process.exit(1);
21698
+ selectedOrg = created;
21699
+ } else if (options.org) {
21563
21700
  const found = organizations.find((o) => o.slug === options.org);
21564
21701
  if (!found) {
21565
21702
  console.log(source_default.red(`Organization "${options.org}" not found.`));
@@ -21797,7 +21934,8 @@ function extractAgentPayload(agent, customToolsMap) {
21797
21934
  name: customTool.name,
21798
21935
  description: customTool.description,
21799
21936
  parameters: customTool.parameters || { type: "object", properties: {} },
21800
- handlerCode: extractHandlerCode(customTool._originalHandler || customTool.handler)
21937
+ handlerCode: extractHandlerCode(customTool._originalHandler || customTool.handler),
21938
+ ...customTool.templateOnly && { templateOnly: true }
21801
21939
  };
21802
21940
  });
21803
21941
  return {
@@ -22658,9 +22796,15 @@ var devCommand = new Command("dev").description("Watch files and sync to develop
22658
22796
  persistent: true,
22659
22797
  usePolling: false
22660
22798
  });
22661
- const handleFileChange = async (path, action) => {
22662
- const relativePath = path.replace(cwd, ".");
22663
- console.log(source_default.gray(`${action}: ${relativePath}`));
22799
+ let debounceTimer = null;
22800
+ let pendingChanges = [];
22801
+ const triggerSync = async () => {
22802
+ const changes = [...pendingChanges];
22803
+ pendingChanges = [];
22804
+ for (const { path, action } of changes) {
22805
+ const relativePath = path.replace(cwd, ".");
22806
+ console.log(source_default.gray(`${action}: ${relativePath}`));
22807
+ }
22664
22808
  const syncSpinner = ora("Syncing...").start();
22665
22809
  try {
22666
22810
  await performDevSync(cwd, project.organization.id);
@@ -22688,6 +22832,15 @@ var devCommand = new Command("dev").description("Watch files and sync to develop
22688
22832
  }
22689
22833
  }
22690
22834
  };
22835
+ const handleFileChange = (path, action) => {
22836
+ pendingChanges.push({ path, action });
22837
+ if (debounceTimer)
22838
+ clearTimeout(debounceTimer);
22839
+ debounceTimer = setTimeout(() => {
22840
+ debounceTimer = null;
22841
+ triggerSync();
22842
+ }, 300);
22843
+ };
22691
22844
  watcher.on("change", (path) => handleFileChange(path, "Changed"));
22692
22845
  watcher.on("add", (path) => handleFileChange(path, "Added"));
22693
22846
  watcher.on("unlink", (path) => handleFileChange(path, "Removed"));
@@ -23616,13 +23769,13 @@ export default defineTools([])
23616
23769
  ` parameters: ${stringifyValue(tool.parameters, 4)}`
23617
23770
  ];
23618
23771
  if (tool.handlerCode) {
23619
- parts.push(` handler: async (args, context, fetch) => {
23772
+ parts.push(` handler: async (args, context, struere, fetch) => {
23620
23773
  ${tool.handlerCode.split(`
23621
23774
  `).join(`
23622
23775
  `)}
23623
23776
  }`);
23624
23777
  } else {
23625
- parts.push(` handler: async (args, context, fetch) => {
23778
+ parts.push(` handler: async (args, context, struere, fetch) => {
23626
23779
  throw new Error("TODO: implement handler")
23627
23780
  }`);
23628
23781
  }
@@ -23912,7 +24065,6 @@ var pullCommand = new Command("pull").description("Pull remote resources to loca
23912
24065
  });
23913
24066
 
23914
24067
  // src/cli/utils/entities.ts
23915
- var CONVEX_URL2 = process.env.STRUERE_CONVEX_URL || "https://rapid-wildebeest-172.convex.cloud";
23916
24068
  function getToken() {
23917
24069
  const credentials = loadCredentials();
23918
24070
  const apiKey = getApiKey();
@@ -23922,7 +24074,7 @@ async function convexQuery(path, args) {
23922
24074
  const token = getToken();
23923
24075
  if (!token)
23924
24076
  return { error: "Not authenticated" };
23925
- const response = await fetch(`${CONVEX_URL2}/api/query`, {
24077
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
23926
24078
  method: "POST",
23927
24079
  headers: {
23928
24080
  "Content-Type": "application/json",
@@ -23951,7 +24103,7 @@ async function convexMutation(path, args) {
23951
24103
  const token = getToken();
23952
24104
  if (!token)
23953
24105
  return { error: "Not authenticated" };
23954
- const response = await fetch(`${CONVEX_URL2}/api/mutation`, {
24106
+ const response = await fetch(`${CONVEX_URL}/api/mutation`, {
23955
24107
  method: "POST",
23956
24108
  headers: {
23957
24109
  "Content-Type": "application/json",
@@ -24481,7 +24633,6 @@ import { join as join9 } from "path";
24481
24633
  import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
24482
24634
 
24483
24635
  // src/cli/utils/evals.ts
24484
- var CONVEX_URL3 = process.env.STRUERE_CONVEX_URL || "https://rapid-wildebeest-172.convex.cloud";
24485
24636
  function getToken2() {
24486
24637
  const credentials = loadCredentials();
24487
24638
  const apiKey = getApiKey();
@@ -24492,7 +24643,7 @@ function getToken2() {
24492
24643
  }
24493
24644
  async function convexQuery2(path, args) {
24494
24645
  const token = getToken2();
24495
- const response = await fetch(`${CONVEX_URL3}/api/query`, {
24646
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
24496
24647
  method: "POST",
24497
24648
  headers: {
24498
24649
  "Content-Type": "application/json",
@@ -24517,7 +24668,7 @@ async function convexQuery2(path, args) {
24517
24668
  }
24518
24669
  async function convexMutation2(path, args) {
24519
24670
  const token = getToken2();
24520
- const response = await fetch(`${CONVEX_URL3}/api/mutation`, {
24671
+ const response = await fetch(`${CONVEX_URL}/api/mutation`, {
24521
24672
  method: "POST",
24522
24673
  headers: {
24523
24674
  "Content-Type": "application/json",
@@ -24931,13 +25082,9 @@ var evalCommand = new Command("eval").description("Eval suite management");
24931
25082
  evalCommand.addCommand(runCommand);
24932
25083
 
24933
25084
  // src/cli/commands/templates.ts
24934
- import { readFileSync as readFileSync4 } from "fs";
25085
+ import { readFileSync as readFileSync5 } from "fs";
24935
25086
 
24936
25087
  // src/cli/utils/whatsapp.ts
24937
- var CONVEX_URL4 = process.env.STRUERE_CONVEX_URL || "https://rapid-wildebeest-172.convex.cloud";
24938
- function getSiteUrl2() {
24939
- return CONVEX_URL4.replace(".cloud", ".site");
24940
- }
24941
25088
  function getToken3() {
24942
25089
  const credentials = loadCredentials();
24943
25090
  const apiKey = getApiKey();
@@ -24947,7 +25094,7 @@ async function httpPost(path, body) {
24947
25094
  const apiKey = getApiKey();
24948
25095
  if (!apiKey)
24949
25096
  return { error: "Not authenticated" };
24950
- const siteUrl = getSiteUrl2();
25097
+ const siteUrl = getSiteUrl();
24951
25098
  try {
24952
25099
  const response = await fetch(`${siteUrl}${path}`, {
24953
25100
  method: "POST",
@@ -24980,7 +25127,7 @@ async function convexAction(path, args) {
24980
25127
  const token = getToken3();
24981
25128
  if (!token)
24982
25129
  return { error: "Not authenticated" };
24983
- const response = await fetch(`${CONVEX_URL4}/api/action`, {
25130
+ const response = await fetch(`${CONVEX_URL}/api/action`, {
24984
25131
  method: "POST",
24985
25132
  headers: {
24986
25133
  "Content-Type": "application/json",
@@ -25009,7 +25156,7 @@ async function convexQuery3(path, args) {
25009
25156
  const token = getToken3();
25010
25157
  if (!token)
25011
25158
  return { error: "Not authenticated" };
25012
- const response = await fetch(`${CONVEX_URL4}/api/query`, {
25159
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
25013
25160
  method: "POST",
25014
25161
  headers: {
25015
25162
  "Content-Type": "application/json",
@@ -25043,16 +25190,15 @@ async function listWhatsAppConnections(env2) {
25043
25190
  }
25044
25191
  return convexQuery3("whatsapp:listConnections", { environment: env2 });
25045
25192
  }
25046
- async function listTemplates(connectionId, env2) {
25193
+ async function listTemplates(connectionId) {
25047
25194
  if (getApiKey()) {
25048
25195
  return httpPost("/v1/templates/list", { connectionId });
25049
25196
  }
25050
25197
  return convexAction("whatsappActions:listTemplates", {
25051
- connectionId,
25052
- environment: env2
25198
+ connectionId
25053
25199
  });
25054
25200
  }
25055
- async function createTemplate(connectionId, env2, name, language, category, components, allowCategoryChange) {
25201
+ async function createTemplate(connectionId, name, language, category, components, allowCategoryChange) {
25056
25202
  if (getApiKey()) {
25057
25203
  return httpPost("/v1/templates/create", {
25058
25204
  connectionId,
@@ -25065,7 +25211,6 @@ async function createTemplate(connectionId, env2, name, language, category, comp
25065
25211
  }
25066
25212
  return convexAction("whatsappActions:createTemplate", {
25067
25213
  connectionId,
25068
- environment: env2,
25069
25214
  name,
25070
25215
  language,
25071
25216
  category,
@@ -25073,23 +25218,21 @@ async function createTemplate(connectionId, env2, name, language, category, comp
25073
25218
  ...allowCategoryChange !== undefined && { allowCategoryChange }
25074
25219
  });
25075
25220
  }
25076
- async function deleteTemplate(connectionId, env2, name) {
25221
+ async function deleteTemplate(connectionId, name) {
25077
25222
  if (getApiKey()) {
25078
25223
  return httpPost("/v1/templates/delete", { connectionId, name });
25079
25224
  }
25080
25225
  return convexAction("whatsappActions:deleteTemplate", {
25081
25226
  connectionId,
25082
- environment: env2,
25083
25227
  name
25084
25228
  });
25085
25229
  }
25086
- async function getTemplateStatus(connectionId, env2, name) {
25230
+ async function getTemplateStatus(connectionId, name) {
25087
25231
  if (getApiKey()) {
25088
25232
  return httpPost("/v1/templates/status", { connectionId, name });
25089
25233
  }
25090
25234
  return convexAction("whatsappActions:getTemplateStatus", {
25091
25235
  connectionId,
25092
- environment: env2,
25093
25236
  name
25094
25237
  });
25095
25238
  }
@@ -25185,13 +25328,12 @@ function statusColor2(status) {
25185
25328
  }
25186
25329
  }
25187
25330
  var templatesCommand = new Command("templates").description("Manage WhatsApp message templates");
25188
- 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) => {
25331
+ templatesCommand.command("list").description("List all message templates").option("--connection <id>", "WhatsApp connection ID").option("--json", "Output raw JSON").action(async (opts) => {
25189
25332
  await ensureAuth2();
25190
- const env2 = opts.env;
25191
- const connectionId = await resolveConnectionId(env2, opts.connection);
25333
+ const connectionId = await resolveConnectionId("development", opts.connection);
25192
25334
  const out = createOutput();
25193
25335
  out.start("Fetching templates");
25194
- const { data, error } = await listTemplates(connectionId, env2);
25336
+ const { data, error } = await listTemplates(connectionId);
25195
25337
  if (error) {
25196
25338
  out.fail("Failed to fetch templates");
25197
25339
  out.error(error);
@@ -25223,14 +25365,13 @@ templatesCommand.command("list").description("List all message templates").optio
25223
25365
  })));
25224
25366
  console.log();
25225
25367
  });
25226
- 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) => {
25368
+ 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) => {
25227
25369
  await ensureAuth2();
25228
- const env2 = opts.env;
25229
- const connectionId = await resolveConnectionId(env2, opts.connection);
25370
+ const connectionId = await resolveConnectionId("development", opts.connection);
25230
25371
  let components;
25231
25372
  if (opts.file) {
25232
25373
  try {
25233
- const fileContent = readFileSync4(opts.file, "utf-8");
25374
+ const fileContent = readFileSync5(opts.file, "utf-8");
25234
25375
  const parsed = JSON.parse(fileContent);
25235
25376
  components = Array.isArray(parsed) ? parsed : parsed.components ?? [parsed];
25236
25377
  } catch (err) {
@@ -25254,7 +25395,7 @@ templatesCommand.command("create <name>").description("Create a new message temp
25254
25395
  }
25255
25396
  const out = createOutput();
25256
25397
  out.start(`Creating template "${name}"`);
25257
- const { data, error } = await createTemplate(connectionId, env2, name, opts.language, opts.category.toUpperCase(), components, opts.allowCategoryChange);
25398
+ const { data, error } = await createTemplate(connectionId, name, opts.language, opts.category.toUpperCase(), components, opts.allowCategoryChange);
25258
25399
  if (error) {
25259
25400
  out.fail("Failed to create template");
25260
25401
  out.error(error);
@@ -25272,10 +25413,9 @@ templatesCommand.command("create <name>").description("Create a new message temp
25272
25413
  console.log();
25273
25414
  }
25274
25415
  });
25275
- 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) => {
25416
+ templatesCommand.command("delete <name>").description("Delete a message template").option("--connection <id>", "WhatsApp connection ID").option("--yes", "Skip confirmation").action(async (name, opts) => {
25276
25417
  await ensureAuth2();
25277
- const env2 = opts.env;
25278
- const connectionId = await resolveConnectionId(env2, opts.connection);
25418
+ const connectionId = await resolveConnectionId("development", opts.connection);
25279
25419
  if (!opts.yes && isInteractive2()) {
25280
25420
  const confirmed = await esm_default2({
25281
25421
  message: `Delete template "${name}"? This cannot be undone.`,
@@ -25288,7 +25428,7 @@ templatesCommand.command("delete <name>").description("Delete a message template
25288
25428
  }
25289
25429
  const out = createOutput();
25290
25430
  out.start(`Deleting template "${name}"`);
25291
- const { error } = await deleteTemplate(connectionId, env2, name);
25431
+ const { error } = await deleteTemplate(connectionId, name);
25292
25432
  if (error) {
25293
25433
  out.fail("Failed to delete template");
25294
25434
  out.error(error);
@@ -25297,13 +25437,12 @@ templatesCommand.command("delete <name>").description("Delete a message template
25297
25437
  out.succeed(`Template "${name}" deleted`);
25298
25438
  console.log();
25299
25439
  });
25300
- 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) => {
25440
+ templatesCommand.command("status <name>").description("Check template approval status").option("--connection <id>", "WhatsApp connection ID").option("--json", "Output raw JSON").action(async (name, opts) => {
25301
25441
  await ensureAuth2();
25302
- const env2 = opts.env;
25303
- const connectionId = await resolveConnectionId(env2, opts.connection);
25442
+ const connectionId = await resolveConnectionId("development", opts.connection);
25304
25443
  const out = createOutput();
25305
25444
  out.start(`Checking status for "${name}"`);
25306
- const { data, error } = await getTemplateStatus(connectionId, env2, name);
25445
+ const { data, error } = await getTemplateStatus(connectionId, name);
25307
25446
  if (error) {
25308
25447
  out.fail("Failed to fetch template status");
25309
25448
  out.error(error);
@@ -25336,7 +25475,6 @@ templatesCommand.command("status <name>").description("Check template approval s
25336
25475
  });
25337
25476
 
25338
25477
  // src/cli/utils/integrations.ts
25339
- var CONVEX_URL5 = process.env.STRUERE_CONVEX_URL || "https://rapid-wildebeest-172.convex.cloud";
25340
25478
  function getToken4() {
25341
25479
  const credentials = loadCredentials();
25342
25480
  const apiKey = getApiKey();
@@ -25346,7 +25484,7 @@ async function convexQuery4(path, args) {
25346
25484
  const token = getToken4();
25347
25485
  if (!token)
25348
25486
  return { error: "Not authenticated" };
25349
- const response = await fetch(`${CONVEX_URL5}/api/query`, {
25487
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
25350
25488
  method: "POST",
25351
25489
  headers: {
25352
25490
  "Content-Type": "application/json",
@@ -25375,7 +25513,7 @@ async function convexMutation3(path, args) {
25375
25513
  const token = getToken4();
25376
25514
  if (!token)
25377
25515
  return { error: "Not authenticated" };
25378
- const response = await fetch(`${CONVEX_URL5}/api/mutation`, {
25516
+ const response = await fetch(`${CONVEX_URL}/api/mutation`, {
25379
25517
  method: "POST",
25380
25518
  headers: {
25381
25519
  "Content-Type": "application/json",
@@ -25404,7 +25542,7 @@ async function convexAction2(path, args) {
25404
25542
  const token = getToken4();
25405
25543
  if (!token)
25406
25544
  return { error: "Not authenticated" };
25407
- const response = await fetch(`${CONVEX_URL5}/api/action`, {
25545
+ const response = await fetch(`${CONVEX_URL}/api/action`, {
25408
25546
  method: "POST",
25409
25547
  headers: {
25410
25548
  "Content-Type": "application/json",
@@ -25869,7 +26007,7 @@ var compilePromptCommand = new Command("compile-prompt").description("Compile an
25869
26007
  // package.json
25870
26008
  var package_default = {
25871
26009
  name: "struere",
25872
- version: "0.9.7",
26010
+ version: "0.9.10",
25873
26011
  description: "Build, test, and deploy AI agents",
25874
26012
  keywords: [
25875
26013
  "ai",
@@ -25973,6 +26111,7 @@ program.addCommand(initCommand);
25973
26111
  program.addCommand(loginCommand);
25974
26112
  program.addCommand(logoutCommand);
25975
26113
  program.addCommand(whoamiCommand);
26114
+ program.addCommand(orgCommand);
25976
26115
  program.addCommand(syncCommand);
25977
26116
  program.addCommand(devCommand);
25978
26117
  program.addCommand(deployCommand);