struere 0.16.3 → 0.16.5

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.
@@ -237,7 +237,8 @@ async function syncViaHttp(apiKey, payload) {
237
237
  triggers: payload.triggers,
238
238
  routers: payload.routers,
239
239
  workflows: payload.workflows,
240
- fixtures: payload.fixtures
240
+ fixtures: payload.fixtures,
241
+ secrets: payload.secrets
241
242
  }),
242
243
  signal: AbortSignal.timeout(30000)
243
244
  });
@@ -65921,8 +65922,8 @@ import { Command as Command6 } from "commander";
65921
65922
  import chalk7 from "chalk";
65922
65923
  import ora6 from "ora";
65923
65924
  import chokidar from "chokidar";
65924
- import { join as join8 } from "path";
65925
- import { existsSync as existsSync8 } from "fs";
65925
+ import { join as join9 } from "path";
65926
+ import { existsSync as existsSync9 } from "fs";
65926
65927
 
65927
65928
  // src/cli/commands/sync.ts
65928
65929
  init_credentials();
@@ -66753,7 +66754,9 @@ function extractAgentPayload(agent, customToolsMap) {
66753
66754
  } else {
66754
66755
  systemPrompt = agent.systemPrompt;
66755
66756
  }
66756
- const tools = (agent.tools || []).map((toolName) => {
66757
+ const tools = (agent.tools || []).map((entry) => {
66758
+ const spec = typeof entry === "string" ? null : entry;
66759
+ const toolName = typeof entry === "string" ? entry : entry.name;
66757
66760
  const isBuiltin = BUILTIN_TOOLS.includes(toolName);
66758
66761
  if (!isBuiltin && !customToolsMap.has(toolName)) {
66759
66762
  const builtinPrefixes = ["entity.", "calendar.", "whatsapp.", "agent.", "airtable.", "email.", "payment.", "web.", "voice.", "router."];
@@ -66764,7 +66767,13 @@ function extractAgentPayload(agent, customToolsMap) {
66764
66767
  const available = customToolsMap.size > 0 ? `Available custom tools: ${Array.from(customToolsMap.keys()).join(", ")}` : "No custom tools were loaded from tools/index.ts";
66765
66768
  throw new Error(`Agent "${agent.name}" references tool "${toolName}" but it was not found. ${available}`);
66766
66769
  }
66767
- return toolName;
66770
+ if (!spec)
66771
+ return toolName;
66772
+ return {
66773
+ name: spec.name,
66774
+ identityMode: spec.identityMode,
66775
+ configuredRole: spec.configuredRole
66776
+ };
66768
66777
  });
66769
66778
  return {
66770
66779
  name: agent.name,
@@ -66802,6 +66811,39 @@ function extractHandlerCode(handler) {
66802
66811
  return code;
66803
66812
  }
66804
66813
 
66814
+ // src/cli/utils/env-file.ts
66815
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
66816
+ import { join as join8 } from "path";
66817
+ function readEnvSecrets(cwd, environment) {
66818
+ const filePath = join8(cwd, `.env.${environment}`);
66819
+ if (!existsSync8(filePath)) {
66820
+ return {};
66821
+ }
66822
+ const content = readFileSync6(filePath, "utf-8");
66823
+ const secrets = {};
66824
+ for (const rawLine of content.split(/\r?\n/)) {
66825
+ const line = rawLine.trim();
66826
+ if (!line || line.startsWith("#")) {
66827
+ continue;
66828
+ }
66829
+ const withoutExport = line.startsWith("export ") ? line.slice(7).trim() : line;
66830
+ const eqIndex = withoutExport.indexOf("=");
66831
+ if (eqIndex === -1) {
66832
+ continue;
66833
+ }
66834
+ const key = withoutExport.slice(0, eqIndex).trim();
66835
+ if (!key) {
66836
+ continue;
66837
+ }
66838
+ let value = withoutExport.slice(eqIndex + 1).trim();
66839
+ if (value.startsWith('"') && value.endsWith('"') && value.length >= 2 || value.startsWith("'") && value.endsWith("'") && value.length >= 2) {
66840
+ value = value.slice(1, -1);
66841
+ }
66842
+ secrets[key] = value;
66843
+ }
66844
+ return secrets;
66845
+ }
66846
+
66805
66847
  // src/cli/utils/validator.ts
66806
66848
  var INTEGRATION_PREFIXES = {
66807
66849
  "whatsapp.": "WhatsApp (Kapso)",
@@ -66894,7 +66936,8 @@ function validateResources(payload, resources) {
66894
66936
  }
66895
66937
  const seenIntegrations = new Set;
66896
66938
  for (const agent of payload.agents) {
66897
- for (const toolName of agent.tools) {
66939
+ for (const tool of agent.tools) {
66940
+ const toolName = typeof tool === "string" ? tool : tool.name;
66898
66941
  for (const [prefix, integration] of Object.entries(INTEGRATION_PREFIXES)) {
66899
66942
  if (toolName.startsWith(prefix) && !seenIntegrations.has(integration)) {
66900
66943
  seenIntegrations.add(integration);
@@ -66973,6 +67016,7 @@ ${resources.errors.join(`
66973
67016
  triggers: payload.triggers,
66974
67017
  routers: payload.routers,
66975
67018
  workflows: payload.workflows,
67019
+ secrets: readEnvSecrets(cwd, "development"),
66976
67020
  organizationId,
66977
67021
  environment: "development"
66978
67022
  });
@@ -67090,6 +67134,7 @@ ${resources.errors.join(`
67090
67134
  if (environment === "production") {
67091
67135
  const result = await syncOrganization({
67092
67136
  ...payload,
67137
+ secrets: readEnvSecrets(cwd, "production"),
67093
67138
  organizationId,
67094
67139
  environment: "production"
67095
67140
  });
@@ -67102,7 +67147,7 @@ ${resources.errors.join(`
67102
67147
  }
67103
67148
  return performDevSync(cwd, organizationId);
67104
67149
  }
67105
- var syncCommand = new Command5("sync").description("Sync resources to Convex and exit").option("--force", "Skip destructive sync confirmation").option("--json", "Output results as JSON").option("--dry-run", "Show what would be synced without syncing").option("--env <environment>", "Target environment (development|production|eval)").action(async (options) => {
67150
+ var syncCommand = new Command5("sync").description("Sync resources to Convex and exit. Syncs .env.development (development) or .env.production (production) from the project root as secrets; development secrets are mirrored to eval.").option("--force", "Skip destructive sync confirmation").option("--json", "Output results as JSON").option("--dry-run", "Show what would be synced without syncing").option("--env <environment>", "Target environment (development|production|eval)").action(async (options) => {
67106
67151
  const cwd = process.cwd();
67107
67152
  const jsonMode = !!options.json;
67108
67153
  const output = createOutput();
@@ -67174,9 +67219,9 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
67174
67219
  const payload = await extractSyncPayload(resources);
67175
67220
  if (!jsonMode)
67176
67221
  output.stop();
67177
- let deletions = [];
67222
+ let deletions2 = [];
67178
67223
  try {
67179
- deletions = await checkForDeletions(resources, project.organization.id, environment);
67224
+ deletions2 = await checkForDeletions(resources, project.organization.id, environment);
67180
67225
  } catch {}
67181
67226
  if (jsonMode) {
67182
67227
  console.log(JSON.stringify({
@@ -67189,7 +67234,7 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
67189
67234
  triggers: (payload.triggers || []).map((t2) => t2.slug),
67190
67235
  routers: (payload.routers || []).map((r) => r.slug),
67191
67236
  workflows: (payload.workflows || []).map((w) => w.slug),
67192
- deletions: deletions.map((d) => ({ type: d.type, names: d.deleted }))
67237
+ deletions: deletions2.map((d) => ({ type: d.type, names: d.deleted }))
67193
67238
  }));
67194
67239
  } else {
67195
67240
  console.log(chalk6.bold("Dry run — nothing will be synced"));
@@ -67201,10 +67246,10 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
67201
67246
  console.log(chalk6.gray(" Triggers:"), (payload.triggers || []).map((t2) => t2.slug).join(", ") || "none");
67202
67247
  console.log(chalk6.gray(" Routers:"), (payload.routers || []).map((r) => r.slug).join(", ") || "none");
67203
67248
  console.log(chalk6.gray(" Workflows:"), (payload.workflows || []).map((w) => w.slug).join(", ") || "none");
67204
- if (deletions.length > 0) {
67249
+ if (deletions2.length > 0) {
67205
67250
  console.log();
67206
67251
  console.log(chalk6.yellow.bold(" Would delete:"));
67207
- for (const d of deletions) {
67252
+ for (const d of deletions2) {
67208
67253
  for (const name of d.deleted) {
67209
67254
  console.log(chalk6.red(` - ${d.type}: ${name}`));
67210
67255
  }
@@ -67214,35 +67259,43 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
67214
67259
  }
67215
67260
  return;
67216
67261
  }
67217
- if (!shouldForce) {
67262
+ if (!jsonMode)
67263
+ output.start("Checking remote state");
67264
+ let deletions = [];
67265
+ try {
67266
+ deletions = await checkForDeletions(resources, project.organization.id, environment);
67218
67267
  if (!jsonMode)
67219
- output.start("Checking remote state");
67220
- try {
67221
- const deletions = await checkForDeletions(resources, project.organization.id, environment);
67222
- if (!jsonMode)
67223
- output.stop();
67224
- if (deletions.length > 0) {
67225
- console.log(chalk6.yellow.bold(" Warning: this sync will DELETE remote resources:"));
67226
- console.log();
67227
- for (const d of deletions) {
67228
- console.log(chalk6.yellow(` ${d.type}:`.padEnd(20)), `${d.remote} remote ${d.local} local`, chalk6.red(`(${d.deleted.length} will be deleted)`));
67229
- for (const name of d.deleted) {
67230
- console.log(chalk6.red(` - ${name}`));
67231
- }
67232
- }
67233
- console.log();
67234
- console.log(chalk6.gray(" Run"), chalk6.cyan("struere pull"), chalk6.gray("first to download remote resources."));
67235
- console.log();
67236
- const shouldContinue = await confirm2({ message: "Continue anyway?", default: false });
67237
- if (!shouldContinue) {
67238
- console.log(chalk6.gray("Aborted."));
67239
- process.exit(0);
67240
- }
67241
- console.log();
67268
+ output.stop();
67269
+ } catch (err) {
67270
+ if (!jsonMode) {
67271
+ output.stop();
67272
+ console.log(chalk6.yellow.bold(" ⚠ Could not verify which remote resources would be deleted:"), err instanceof Error ? err.message : String(err));
67273
+ console.log(chalk6.yellow(" Proceeding a destructive sync cannot be ruled out."));
67274
+ console.log();
67275
+ }
67276
+ }
67277
+ if (deletions.length > 0 && !jsonMode) {
67278
+ console.log(chalk6.red.bold(" ⚠ WARNING: this sync will DELETE remote resources:"));
67279
+ console.log();
67280
+ for (const d of deletions) {
67281
+ console.log(chalk6.yellow(` ${d.type}:`.padEnd(20)), `${d.remote} remote → ${d.local} local`, chalk6.red(`(${d.deleted.length} will be deleted)`));
67282
+ for (const name of d.deleted) {
67283
+ console.log(chalk6.red(` - ${name}`));
67242
67284
  }
67243
- } catch {
67244
- if (!jsonMode)
67245
- output.stop();
67285
+ }
67286
+ console.log();
67287
+ console.log(chalk6.gray(" Run"), chalk6.cyan("struere pull"), chalk6.gray("first to download remote resources."));
67288
+ console.log();
67289
+ if (!shouldForce) {
67290
+ const shouldContinue = await confirm2({ message: "Continue anyway?", default: false });
67291
+ if (!shouldContinue) {
67292
+ console.log(chalk6.gray("Aborted."));
67293
+ process.exit(0);
67294
+ }
67295
+ console.log();
67296
+ } else {
67297
+ console.log(chalk6.yellow(" Proceeding without confirmation (non-interactive or --force)."));
67298
+ console.log();
67246
67299
  }
67247
67300
  }
67248
67301
  if (!jsonMode)
@@ -67335,7 +67388,7 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
67335
67388
 
67336
67389
  // src/cli/commands/dev.ts
67337
67390
  import { confirm as confirm3 } from "@inquirer/prompts";
67338
- var devCommand = new Command6("dev").description("Watch files and sync to development on change (long-running)").option("--force", "Skip destructive sync confirmation").action(async (options) => {
67391
+ var devCommand = new Command6("dev").description("Watch files and sync to development on change (long-running). Syncs .env.development from the project root as secrets; development secrets are mirrored to eval.").option("--force", "Skip destructive sync confirmation").action(async (options) => {
67339
67392
  const spinner = ora6();
67340
67393
  const cwd = process.cwd();
67341
67394
  const apiKey = getApiKey();
@@ -67383,8 +67436,8 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
67383
67436
  }
67384
67437
  console.log();
67385
67438
  }
67386
- const claudeMdPath = join8(cwd, "CLAUDE.md");
67387
- if (!existsSync8(claudeMdPath)) {
67439
+ const claudeMdPath = join9(cwd, "CLAUDE.md");
67440
+ if (!existsSync9(claudeMdPath)) {
67388
67441
  try {
67389
67442
  const { generated } = await generateDocs(cwd, ["claude"]);
67390
67443
  if (generated.length > 0) {
@@ -67411,23 +67464,31 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
67411
67464
  console.log(chalk7.red("Error:"), error instanceof Error ? error.message : String(error));
67412
67465
  }
67413
67466
  const shouldSkipConfirmation = options.force || nonInteractive;
67414
- if (initialSyncOk && !shouldSkipConfirmation && loadedResources) {
67467
+ if (initialSyncOk && loadedResources) {
67415
67468
  spinner.start("Checking remote state");
67469
+ let deletions = [];
67416
67470
  try {
67417
- const deletions = await checkForDeletions(loadedResources, project.organization.id, "development");
67471
+ deletions = await checkForDeletions(loadedResources, project.organization.id, "development");
67418
67472
  spinner.stop();
67419
- if (deletions.length > 0) {
67420
- console.log(chalk7.yellow.bold(" Warning: this sync will DELETE remote resources:"));
67421
- console.log();
67422
- for (const d of deletions) {
67423
- console.log(chalk7.yellow(` ${d.type}:`.padEnd(20)), `${d.remote} remote → ${d.local} local`, chalk7.red(`(${d.deleted.length} will be deleted)`));
67424
- for (const name of d.deleted) {
67425
- console.log(chalk7.red(` - ${name}`));
67426
- }
67473
+ } catch (err) {
67474
+ spinner.stop();
67475
+ console.log(chalk7.yellow.bold(" ⚠ Could not verify which remote resources would be deleted:"), err instanceof Error ? err.message : String(err));
67476
+ console.log(chalk7.yellow(" Proceeding a destructive sync cannot be ruled out."));
67477
+ console.log();
67478
+ }
67479
+ if (deletions.length > 0) {
67480
+ console.log(chalk7.red.bold(" ⚠ WARNING: this sync will DELETE remote resources:"));
67481
+ console.log();
67482
+ for (const d of deletions) {
67483
+ console.log(chalk7.yellow(` ${d.type}:`.padEnd(20)), `${d.remote} remote → ${d.local} local`, chalk7.red(`(${d.deleted.length} will be deleted)`));
67484
+ for (const name of d.deleted) {
67485
+ console.log(chalk7.red(` - ${name}`));
67427
67486
  }
67428
- console.log();
67429
- console.log(chalk7.gray(" Run"), chalk7.cyan("struere pull"), chalk7.gray("first to download remote resources."));
67430
- console.log();
67487
+ }
67488
+ console.log();
67489
+ console.log(chalk7.gray(" Run"), chalk7.cyan("struere pull"), chalk7.gray("first to download remote resources."));
67490
+ console.log();
67491
+ if (!shouldSkipConfirmation) {
67431
67492
  const shouldContinue = await confirm3({ message: "Continue anyway?", default: false });
67432
67493
  if (!shouldContinue) {
67433
67494
  console.log();
@@ -67435,9 +67496,10 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
67435
67496
  process.exit(0);
67436
67497
  }
67437
67498
  console.log();
67499
+ } else {
67500
+ console.log(chalk7.yellow(" Proceeding without confirmation (non-interactive or --force)."));
67501
+ console.log();
67438
67502
  }
67439
- } catch {
67440
- spinner.stop();
67441
67503
  }
67442
67504
  }
67443
67505
  if (initialSyncOk) {
@@ -67480,7 +67542,7 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
67480
67542
  dirs.routers,
67481
67543
  dirs.workflows,
67482
67544
  dirs.fixtures
67483
- ].filter((p) => existsSync8(p));
67545
+ ].filter((p) => existsSync9(p));
67484
67546
  const watcher = chokidar.watch(watchPaths, {
67485
67547
  ignoreInitial: true,
67486
67548
  ignored: [/node_modules/, /\.struere-tmp-/, /evals\/runs\//],
@@ -67498,6 +67560,23 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
67498
67560
  }
67499
67561
  const syncSpinner = ora6("Syncing...").start();
67500
67562
  try {
67563
+ try {
67564
+ const watchResources = await loadAllResources(cwd);
67565
+ if (watchResources.errors.length === 0) {
67566
+ const deletions = await checkForDeletions(watchResources, project.organization.id, "development");
67567
+ if (deletions.length > 0) {
67568
+ syncSpinner.stop();
67569
+ console.log(chalk7.red.bold(" ⚠ WARNING: this sync will DELETE remote resources:"));
67570
+ for (const d of deletions) {
67571
+ for (const name of d.deleted) {
67572
+ console.log(chalk7.red(` - ${d.type}: ${name}`));
67573
+ }
67574
+ }
67575
+ console.log(chalk7.gray(" Run"), chalk7.cyan("struere pull"), chalk7.gray("to restore them locally."));
67576
+ syncSpinner.start("Syncing...");
67577
+ }
67578
+ }
67579
+ } catch {}
67501
67580
  await performDevSync(cwd, project.organization.id);
67502
67581
  syncSpinner.succeed("Synced");
67503
67582
  } catch (error) {
@@ -67537,7 +67616,7 @@ import chalk8 from "chalk";
67537
67616
  import ora7 from "ora";
67538
67617
  import { confirm as confirm4 } from "@inquirer/prompts";
67539
67618
  init_convex();
67540
- var deployCommand = new Command7("deploy").description("Deploy all resources to production").option("--dry-run", "Show what would be deployed without deploying").option("--force", "Skip destructive sync confirmation").option("--json", "Output results as JSON").option("-v, --verbose", "Show detailed resource information").action(async (options) => {
67619
+ var deployCommand = new Command7("deploy").description("Deploy all resources to production. Syncs .env.production from the project root as secrets.").option("--dry-run", "Show what would be deployed without deploying").option("--force", "Skip destructive sync confirmation").option("--json", "Output results as JSON").option("-v, --verbose", "Show detailed resource information").action(async (options) => {
67541
67620
  const spinner = ora7();
67542
67621
  const cwd = process.cwd();
67543
67622
  const nonInteractive = !isInteractive();
@@ -67781,6 +67860,7 @@ var deployCommand = new Command7("deploy").description("Deploy all resources to
67781
67860
  roles: payload.roles,
67782
67861
  triggers: payload.triggers,
67783
67862
  routers: payload.routers,
67863
+ secrets: readEnvSecrets(cwd, "production"),
67784
67864
  organizationId: project.organization.id,
67785
67865
  environment: "production"
67786
67866
  });
@@ -68529,8 +68609,8 @@ init_credentials();
68529
68609
  import { Command as Command12 } from "commander";
68530
68610
  import chalk13 from "chalk";
68531
68611
  import ora10 from "ora";
68532
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
68533
- import { join as join9 } from "path";
68612
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
68613
+ import { join as join10 } from "path";
68534
68614
  init_convex();
68535
68615
 
68536
68616
  // src/cli/utils/generator.ts
@@ -68950,13 +69030,13 @@ var pullCommand = new Command12("pull").description("Pull remote resources to lo
68950
69030
  const created = [];
68951
69031
  const skipped = [];
68952
69032
  const ensureDir2 = (dir) => {
68953
- if (!existsSync9(dir)) {
69033
+ if (!existsSync10(dir)) {
68954
69034
  mkdirSync6(dir, { recursive: true });
68955
69035
  }
68956
69036
  };
68957
69037
  const writeOrSkip = (relativePath, content) => {
68958
- const fullPath = join9(cwd, relativePath);
68959
- if (existsSync9(fullPath) && !options.force) {
69038
+ const fullPath = join10(cwd, relativePath);
69039
+ if (existsSync10(fullPath) && !options.force) {
68960
69040
  skipped.push(relativePath);
68961
69041
  return false;
68962
69042
  }
@@ -68964,19 +69044,19 @@ var pullCommand = new Command12("pull").description("Pull remote resources to lo
68964
69044
  created.push(relativePath);
68965
69045
  return true;
68966
69046
  }
68967
- ensureDir2(join9(cwd, relativePath.split("/").slice(0, -1).join("/")));
69047
+ ensureDir2(join10(cwd, relativePath.split("/").slice(0, -1).join("/")));
68968
69048
  writeFileSync7(fullPath, content);
68969
69049
  created.push(relativePath);
68970
69050
  return true;
68971
69051
  };
68972
- ensureDir2(join9(cwd, "agents"));
68973
- ensureDir2(join9(cwd, "entity-types"));
68974
- ensureDir2(join9(cwd, "views"));
68975
- ensureDir2(join9(cwd, "roles"));
68976
- ensureDir2(join9(cwd, "triggers"));
68977
- ensureDir2(join9(cwd, "routers"));
68978
- ensureDir2(join9(cwd, "workflows"));
68979
- ensureDir2(join9(cwd, "tools"));
69052
+ ensureDir2(join10(cwd, "agents"));
69053
+ ensureDir2(join10(cwd, "entity-types"));
69054
+ ensureDir2(join10(cwd, "views"));
69055
+ ensureDir2(join10(cwd, "roles"));
69056
+ ensureDir2(join10(cwd, "triggers"));
69057
+ ensureDir2(join10(cwd, "routers"));
69058
+ ensureDir2(join10(cwd, "workflows"));
69059
+ ensureDir2(join10(cwd, "tools"));
68980
69060
  const agentSlugs = [];
68981
69061
  for (const agent of state.agents) {
68982
69062
  if (!agent.systemPrompt && agent.tools.length === 0)
@@ -70018,7 +70098,8 @@ logsCommand.command("view <thread-id>").description("View conversation messages"
70018
70098
  const duration = closestExec.durationMs;
70019
70099
  const inputTokens = closestExec.inputTokens;
70020
70100
  const outputTokens = closestExec.outputTokens;
70021
- console.log(` ${chalk16.dim(` exec: ${statusColor(status)} ${model ?? ""} ${inputTokens ?? 0}in/${outputTokens ?? 0}out ${duration ?? 0}ms`)}`);
70101
+ const agent = closestExec.agentSlug ?? closestExec.agentId ?? "";
70102
+ console.log(` ${chalk16.dim(` exec: ${statusColor(status)} ${chalk16.cyan(agent)} ${model ?? ""} ${inputTokens ?? 0}in/${outputTokens ?? 0}out ${duration ?? 0}ms`)}`);
70022
70103
  if (closestExec.errorMessage) {
70023
70104
  console.log(` ${chalk16.red(` error: ${closestExec.errorMessage}`)}`);
70024
70105
  }
@@ -70033,7 +70114,7 @@ init_credentials();
70033
70114
  import { Command as Command15 } from "commander";
70034
70115
  import chalk17 from "chalk";
70035
70116
  import ora13 from "ora";
70036
- import { join as join10 } from "path";
70117
+ import { join as join11 } from "path";
70037
70118
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
70038
70119
  init_convex();
70039
70120
 
@@ -70471,15 +70552,15 @@ var runCommand = new Command15("run").description("Run an eval suite").argument(
70471
70552
  const agentSlug = suiteSlug;
70472
70553
  const timestamp = new Date().toISOString().replace(/:/g, "-").replace(/\.\d+Z$/, "");
70473
70554
  const folderName = `${timestamp}_${suiteSlug}`;
70474
- const outDir = join10(cwd, "evals", "runs", folderName);
70555
+ const outDir = join11(cwd, "evals", "runs", folderName);
70475
70556
  mkdirSync7(outDir, { recursive: true });
70476
70557
  const summaryMd = generateSummaryMd(suite.name, suite.slug, agentSlug, run, results);
70477
- writeFileSync8(join10(outDir, "_summary.md"), summaryMd);
70558
+ writeFileSync8(join11(outDir, "_summary.md"), summaryMd);
70478
70559
  for (const r of results) {
70479
70560
  const label = statusLabel(r);
70480
70561
  const fileName = `${label}_${slugify3(r.caseName)}.md`;
70481
70562
  const caseMd = generateCaseMd(r);
70482
- writeFileSync8(join10(outDir, fileName), caseMd);
70563
+ writeFileSync8(join11(outDir, fileName), caseMd);
70483
70564
  }
70484
70565
  const relativePath = `evals/runs/${folderName}/`;
70485
70566
  console.log();
@@ -70494,7 +70575,7 @@ evalCommand.addCommand(runCommand);
70494
70575
  init_credentials();
70495
70576
  import { Command as Command16 } from "commander";
70496
70577
  import chalk18 from "chalk";
70497
- import { readFileSync as readFileSync6 } from "fs";
70578
+ import { readFileSync as readFileSync7 } from "fs";
70498
70579
  import { confirm as confirm6 } from "@inquirer/prompts";
70499
70580
 
70500
70581
  // src/cli/utils/whatsapp.ts
@@ -70843,7 +70924,7 @@ templatesCommand.command("create <name>").description("Create a new message temp
70843
70924
  let components;
70844
70925
  if (opts.file) {
70845
70926
  try {
70846
- const fileContent = readFileSync6(opts.file, "utf-8");
70927
+ const fileContent = readFileSync7(opts.file, "utf-8");
70847
70928
  const parsed = JSON.parse(fileContent);
70848
70929
  components = Array.isArray(parsed) ? parsed : parsed.components ?? [parsed];
70849
70930
  } catch (err) {
@@ -70974,7 +71055,7 @@ templatesCommand.command("edit <name>").description("Edit a message template").o
70974
71055
  let components;
70975
71056
  if (opts.file) {
70976
71057
  try {
70977
- const fileContent = readFileSync6(opts.file, "utf-8");
71058
+ const fileContent = readFileSync7(opts.file, "utf-8");
70978
71059
  const parsed = JSON.parse(fileContent);
70979
71060
  components = Array.isArray(parsed) ? parsed : parsed.components ?? [parsed];
70980
71061
  } catch (err) {
@@ -72476,8 +72557,8 @@ init_credentials();
72476
72557
  import { Command as Command19 } from "commander";
72477
72558
  import chalk21 from "chalk";
72478
72559
  import ora15 from "ora";
72479
- import { existsSync as existsSync10, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
72480
- import { join as join11 } from "path";
72560
+ import { existsSync as existsSync11, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
72561
+ import { join as join12 } from "path";
72481
72562
  init_convex();
72482
72563
 
72483
72564
  // src/cli/utils/workflows.ts
@@ -72576,6 +72657,9 @@ async function cancelWorkflowRun(runId, environment, organizationId) {
72576
72657
  async function retryWorkflowRun(runId, environment, organizationId) {
72577
72658
  return convexMutation6("workflows:retryRun", { runId, environment, ...organizationId && { organizationId } });
72578
72659
  }
72660
+ async function retriggerWorkflowRun(runId, environment, organizationId) {
72661
+ return convexMutation6("workflows:retriggerRun", { runId, environment, ...organizationId && { organizationId } });
72662
+ }
72579
72663
  async function toggleWorkflow(slug, enabled, environment, organizationId) {
72580
72664
  const path = enabled ? "workflows:enable" : "workflows:disable";
72581
72665
  return convexMutation6(path, { slug, environment, ...organizationId && { organizationId } });
@@ -72816,8 +72900,8 @@ function renderNodeRuns(nodeRuns, verbose) {
72816
72900
  console.log();
72817
72901
  });
72818
72902
  }
72819
- var workflowsCommand = new Command19("workflows").description("Manage workflows and workflow runs");
72820
- workflowsCommand.command("list", { isDefault: true }).description("List all workflows").option("--env <environment>", "Environment (development|production|eval)", "development").option("--json", "Output raw JSON").option("--failed", "Show only workflows with recent failures").action(async (opts) => {
72903
+ var workflowsCommand = new Command19("workflows").description("Manage graph-based workflows and inspect, fire, and recover their runs");
72904
+ workflowsCommand.command("list", { isDefault: true }).description("List all workflows with trigger, node count, enabled state, and last-run status").option("--env <environment>", "Environment (development|production|eval)", "development").option("--json", "Output raw JSON").option("--failed", "Show only workflows with recent failures").action(async (opts) => {
72821
72905
  await ensureAuth6();
72822
72906
  const spinner = opts.json ? null : ora15();
72823
72907
  try {
@@ -72869,7 +72953,7 @@ workflowsCommand.command("list", { isDefault: true }).description("List all work
72869
72953
  process.exit(1);
72870
72954
  }
72871
72955
  });
72872
- workflowsCommand.command("get <slug>").description("View workflow details").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").action(async (slug, opts) => {
72956
+ workflowsCommand.command("get <slug>").description("View a workflow definition: metadata and the node graph with per-port edges").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").action(async (slug, opts) => {
72873
72957
  await ensureAuth6();
72874
72958
  const spinner = opts.json ? null : ora15();
72875
72959
  try {
@@ -72922,7 +73006,7 @@ workflowsCommand.command("get <slug>").description("View workflow details").opti
72922
73006
  process.exit(1);
72923
73007
  }
72924
73008
  });
72925
- workflowsCommand.command("runs [slug]").description("List workflow runs").option("--env <environment>", "Environment", "development").option("--status <status>", "Filter by status (pending|running|completed|completed_with_errors|failed|dead)").option("--limit <n>", "Maximum results", "20").option("--json", "Output raw JSON").action(async (slug, opts) => {
73009
+ workflowsCommand.command("runs [slug]").description("List workflow runs (optionally for one workflow), filterable by status").option("--env <environment>", "Environment", "development").option("--status <status>", "Filter by status (pending|running|completed|completed_with_errors|failed|dead)").option("--limit <n>", "Maximum results", "20").option("--json", "Output raw JSON").action(async (slug, opts) => {
72926
73010
  await ensureAuth6();
72927
73011
  const spinner = opts.json ? null : ora15();
72928
73012
  try {
@@ -72967,7 +73051,7 @@ workflowsCommand.command("runs [slug]").description("List workflow runs").option
72967
73051
  process.exit(1);
72968
73052
  }
72969
73053
  });
72970
- workflowsCommand.command("run <run-id>").description("View workflow run details").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("-v, --verbose", "Show full error messages").action(async (runId, opts) => {
73054
+ workflowsCommand.command("run <run-id>").description("View a run in detail: status, timing, and the per-node execution timeline").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("-v, --verbose", "Show full error messages").action(async (runId, opts) => {
72971
73055
  await ensureAuth6();
72972
73056
  const spinner = opts.json ? null : ora15();
72973
73057
  try {
@@ -73010,7 +73094,7 @@ workflowsCommand.command("run <run-id>").description("View workflow run details"
73010
73094
  process.exit(1);
73011
73095
  }
73012
73096
  });
73013
- workflowsCommand.command("stats").description("Show workflow run statistics").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").action(async (opts) => {
73097
+ workflowsCommand.command("stats").description("Show run counts grouped by status for an environment").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").action(async (opts) => {
73014
73098
  await ensureAuth6();
73015
73099
  const spinner = opts.json ? null : ora15();
73016
73100
  try {
@@ -73053,7 +73137,7 @@ workflowsCommand.command("stats").description("Show workflow run statistics").op
73053
73137
  process.exit(1);
73054
73138
  }
73055
73139
  });
73056
- workflowsCommand.command("logs [slug]").description("View workflow execution history").option("--env <environment>", "Environment", "development").option("--limit <n>", "Maximum results", "10").option("--json", "Output raw JSON").option("-v, --verbose", "Show full error messages").action(async (slug, opts) => {
73140
+ workflowsCommand.command("logs [slug]").description("List recent execution history (optionally for one workflow)").option("--env <environment>", "Environment", "development").option("--limit <n>", "Maximum results", "10").option("--json", "Output raw JSON").option("-v, --verbose", "Show full error messages").action(async (slug, opts) => {
73057
73141
  await ensureAuth6();
73058
73142
  const spinner = opts.json ? null : ora15();
73059
73143
  try {
@@ -73169,7 +73253,7 @@ workflowsCommand.command("log <identifier>").description("View detailed workflow
73169
73253
  process.exit(1);
73170
73254
  }
73171
73255
  });
73172
- workflowsCommand.command("retry <run-id>").description("Retry a failed or dead run").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (runId, opts) => {
73256
+ workflowsCommand.command("retry <run-id>").description("Retry a failed or dead run, resuming at the dead node (completed branches are not re-run)").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (runId, opts) => {
73173
73257
  await ensureAuth6();
73174
73258
  const spinner = opts.json ? null : ora15();
73175
73259
  const environment = opts.env;
@@ -73184,10 +73268,44 @@ workflowsCommand.command("retry <run-id>").description("Retry a failed or dead r
73184
73268
  }
73185
73269
  try {
73186
73270
  spinner?.start("Retrying run...");
73187
- await retryWorkflowRun(runId, environment, getOrgId4());
73271
+ const res = await retryWorkflowRun(runId, environment, getOrgId4());
73272
+ if (res.outcome === "definition_changed") {
73273
+ spinner?.stop();
73274
+ if (opts.json) {
73275
+ console.log(JSON.stringify({ success: false, outcome: "definition_changed", runId }));
73276
+ return;
73277
+ }
73278
+ console.log(chalk21.yellow("This run was created on an earlier version of the workflow, so it can't be resumed."));
73279
+ if (isInteractive()) {
73280
+ const readline = await import("readline");
73281
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
73282
+ const answer = await new Promise((resolve) => {
73283
+ rl.question(" Run it again from the start with the original input? This re-executes every step (y/N): ", resolve);
73284
+ });
73285
+ rl.close();
73286
+ if (answer.trim().toLowerCase() === "y") {
73287
+ const fresh = await retriggerWorkflowRun(runId, environment, getOrgId4());
73288
+ console.log(chalk21.green(`Started a fresh run: ${fresh.runId}`));
73289
+ } else {
73290
+ console.log(chalk21.dim("Skipped — re-run anytime with: struere workflows fire <slug>"));
73291
+ }
73292
+ } else {
73293
+ console.log(chalk21.dim("Re-run it with: struere workflows fire <slug>"));
73294
+ }
73295
+ return;
73296
+ }
73297
+ if (res.outcome === "nothing_to_retry") {
73298
+ spinner?.stop();
73299
+ if (opts.json) {
73300
+ console.log(JSON.stringify({ success: false, outcome: "nothing_to_retry", runId }));
73301
+ return;
73302
+ }
73303
+ console.log(chalk21.dim("Nothing to retry on this run."));
73304
+ return;
73305
+ }
73188
73306
  spinner?.succeed(chalk21.green(`Run ${runId} queued for retry`));
73189
73307
  if (opts.json) {
73190
- console.log(JSON.stringify({ success: true, runId }));
73308
+ console.log(JSON.stringify({ success: true, outcome: "resumed", runId }));
73191
73309
  }
73192
73310
  } catch (err) {
73193
73311
  const message = err instanceof Error ? err.message : String(err);
@@ -73200,7 +73318,7 @@ workflowsCommand.command("retry <run-id>").description("Retry a failed or dead r
73200
73318
  process.exit(1);
73201
73319
  }
73202
73320
  });
73203
- workflowsCommand.command("cancel <run-id>").description("Cancel a pending or running run").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (runId, opts) => {
73321
+ workflowsCommand.command("cancel <run-id>").description("Cancel a pending or running run (marks it dead)").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (runId, opts) => {
73204
73322
  await ensureAuth6();
73205
73323
  const spinner = opts.json ? null : ora15();
73206
73324
  const environment = opts.env;
@@ -73231,7 +73349,7 @@ workflowsCommand.command("cancel <run-id>").description("Cancel a pending or run
73231
73349
  process.exit(1);
73232
73350
  }
73233
73351
  });
73234
- workflowsCommand.command("enable <slug>").description("Enable a workflow").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (slug, opts) => {
73352
+ workflowsCommand.command("enable <slug>").description("Enable a workflow so it fires on its trigger (schedules next run for cron workflows)").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (slug, opts) => {
73235
73353
  await ensureAuth6();
73236
73354
  const spinner = opts.json ? null : ora15();
73237
73355
  const environment = opts.env;
@@ -73262,7 +73380,7 @@ workflowsCommand.command("enable <slug>").description("Enable a workflow").optio
73262
73380
  process.exit(1);
73263
73381
  }
73264
73382
  });
73265
- workflowsCommand.command("disable <slug>").description("Disable a workflow").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (slug, opts) => {
73383
+ workflowsCommand.command("disable <slug>").description("Disable a workflow so it stops firing (clears next scheduled run for cron workflows)").option("--env <environment>", "Environment", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (slug, opts) => {
73266
73384
  await ensureAuth6();
73267
73385
  const spinner = opts.json ? null : ora15();
73268
73386
  const environment = opts.env;
@@ -73293,7 +73411,7 @@ workflowsCommand.command("disable <slug>").description("Disable a workflow").opt
73293
73411
  process.exit(1);
73294
73412
  }
73295
73413
  });
73296
- workflowsCommand.command("fire <slug>").description("Manually fire a workflow (starts a real run)").option("--env <environment>", "Environment (development|production|eval)", "development").option("--data <json>", "JSON data for the trigger item").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (slug, opts) => {
73414
+ workflowsCommand.command("fire <slug>").description("Manually fire a workflow starts a real run (no dry-run); test safely in dev/eval").option("--env <environment>", "Environment (development|production|eval)", "development").option("--data <json>", "JSON data for the trigger item").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (slug, opts) => {
73297
73415
  await ensureAuth6();
73298
73416
  const spinner = opts.json ? null : ora15();
73299
73417
  const environment = opts.env;
@@ -73373,7 +73491,7 @@ workflowsCommand.command("fire <slug>").description("Manually fire a workflow (s
73373
73491
  process.exit(1);
73374
73492
  }
73375
73493
  });
73376
- workflowsCommand.command("import-trigger <slug>").description("Generate a workflow file from a legacy trigger").option("--env <environment>", "Environment to read the trigger from", "development").option("--force", "Overwrite an existing workflow file").option("--json", "Output raw JSON").action(async (slug, opts) => {
73494
+ workflowsCommand.command("import-trigger <slug>").description("Generate a workflow file (workflows/<slug>.ts) from a legacy linear trigger; leaves the source intact").option("--env <environment>", "Environment to read the trigger from", "development").option("--force", "Overwrite an existing workflow file").option("--json", "Output raw JSON").action(async (slug, opts) => {
73377
73495
  await ensureAuth6();
73378
73496
  const spinner = opts.json ? null : ora15();
73379
73497
  const cwd = process.cwd();
@@ -73388,13 +73506,13 @@ workflowsCommand.command("import-trigger <slug>").description("Generate a workfl
73388
73506
  process.exit(1);
73389
73507
  }
73390
73508
  const source = convertTriggerToWorkflowSource(trigger);
73391
- const workflowsDir = join11(cwd, "workflows");
73392
- if (!existsSync10(workflowsDir)) {
73509
+ const workflowsDir = join12(cwd, "workflows");
73510
+ if (!existsSync11(workflowsDir)) {
73393
73511
  mkdirSync8(workflowsDir, { recursive: true });
73394
73512
  }
73395
73513
  const relativePath = `workflows/${trigger.slug}.ts`;
73396
- const filePath = join11(cwd, relativePath);
73397
- if (existsSync10(filePath) && !opts.force) {
73514
+ const filePath = join12(cwd, relativePath);
73515
+ if (existsSync11(filePath) && !opts.force) {
73398
73516
  spinner?.fail("Workflow file already exists");
73399
73517
  if (opts.json) {
73400
73518
  console.log(JSON.stringify({ success: false, error: `${relativePath} already exists (use --force to overwrite)` }));
@@ -73895,10 +74013,7 @@ threadsCommand.command("view <id>").description("View thread details and message
73895
74013
  if (thread.externalId) {
73896
74014
  console.log(` ${chalk22.gray("External:")} ${chalk22.cyan(thread.externalId)}`);
73897
74015
  }
73898
- console.log(` ${chalk22.gray("Agent:")} ${chalk22.cyan(thread.agentId)}`);
73899
- if (thread.currentAgentId) {
73900
- console.log(` ${chalk22.gray("Current:")} ${chalk22.cyan(thread.currentAgentId)}`);
73901
- }
74016
+ console.log(` ${chalk22.gray("Agent:")} ${chalk22.cyan(thread.agentName)}${thread.currentAgentName ? ` ${chalk22.yellow("(routed)")}` : ""}`);
73902
74017
  console.log(` ${chalk22.gray("Created:")} ${chalk22.cyan(new Date(thread.createdAt).toISOString())}`);
73903
74018
  console.log(` ${chalk22.gray("Updated:")} ${chalk22.cyan(new Date(thread.updatedAt).toISOString())}`);
73904
74019
  if (thread.messages?.length) {
@@ -74008,11 +74123,9 @@ threadsCommand.command("reset").description("Archive a thread by phone number").
74008
74123
  }
74009
74124
  });
74010
74125
  async function resolveOrExit(target, environment, json, spinner) {
74011
- const project = loadProject(process.cwd());
74012
74126
  const resolution = await resolveActiveWhatsAppThread({
74013
74127
  target,
74014
- environment,
74015
- organizationId: project?.organization.id
74128
+ environment
74016
74129
  });
74017
74130
  if (resolution.kind === "none") {
74018
74131
  const error = `No active WhatsApp thread found for "${target}" in ${environment}. Did you mean --env production?`;
@@ -75522,7 +75635,7 @@ function checkWhatsAppConnection(resources) {
75522
75635
  "whatsapp.getConversation",
75523
75636
  "whatsapp.getStatus"
75524
75637
  ]);
75525
- const agentsUsingWhatsapp = resources.agents.filter((a) => a.tools?.some((t2) => whatsappTools.has(t2)));
75638
+ const agentsUsingWhatsapp = resources.agents.filter((a) => a.tools?.some((t2) => whatsappTools.has(typeof t2 === "string" ? t2 : t2.name)));
75526
75639
  if (agentsUsingWhatsapp.length === 0) {
75527
75640
  return {
75528
75641
  id: "whatsapp",
@@ -76194,7 +76307,7 @@ keysCommand.command("revoke <id-or-prefix>").description("Revoke an API key (del
76194
76307
  // package.json
76195
76308
  var package_default = {
76196
76309
  name: "struere",
76197
- version: "0.16.3",
76310
+ version: "0.16.5",
76198
76311
  description: "Build, test, and deploy AI agents",
76199
76312
  keywords: [
76200
76313
  "ai",