struere 0.14.2 → 0.14.4

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 (38) hide show
  1. package/dist/bin/struere.js +461 -83
  2. package/dist/cli/commands/chat.d.ts.map +1 -1
  3. package/dist/cli/commands/dev.d.ts.map +1 -1
  4. package/dist/cli/commands/doctor.d.ts.map +1 -1
  5. package/dist/cli/commands/keys.d.ts +3 -0
  6. package/dist/cli/commands/keys.d.ts.map +1 -0
  7. package/dist/cli/commands/triggers.d.ts.map +1 -1
  8. package/dist/cli/index.js +461 -83
  9. package/dist/cli/utils/apiKeys.d.ts +31 -0
  10. package/dist/cli/utils/apiKeys.d.ts.map +1 -0
  11. package/dist/cli/utils/convex.d.ts +1 -0
  12. package/dist/cli/utils/convex.d.ts.map +1 -1
  13. package/dist/cli/utils/extractor.d.ts +1 -0
  14. package/dist/cli/utils/extractor.d.ts.map +1 -1
  15. package/dist/cli/utils/logs.d.ts +4 -3
  16. package/dist/cli/utils/logs.d.ts.map +1 -1
  17. package/dist/cli/utils/plugin.d.ts +1 -0
  18. package/dist/cli/utils/plugin.d.ts.map +1 -1
  19. package/dist/cli/utils/threads.d.ts +1 -0
  20. package/dist/cli/utils/threads.d.ts.map +1 -1
  21. package/dist/cli/utils/triggers.d.ts +12 -10
  22. package/dist/cli/utils/triggers.d.ts.map +1 -1
  23. package/dist/client/chatClient.d.ts +8 -0
  24. package/dist/client/chatClient.d.ts.map +1 -0
  25. package/dist/client/dataClient.d.ts +21 -0
  26. package/dist/client/dataClient.d.ts.map +1 -0
  27. package/dist/client/index.d.ts +13 -0
  28. package/dist/client/index.d.ts.map +1 -0
  29. package/dist/client/index.js +245 -0
  30. package/dist/client/transport.d.ts +35 -0
  31. package/dist/client/transport.d.ts.map +1 -0
  32. package/dist/client/types.d.ts +130 -0
  33. package/dist/client/types.d.ts.map +1 -0
  34. package/dist/define/agent.d.ts.map +1 -1
  35. package/dist/index.js +10 -0
  36. package/dist/types.d.ts +1 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/package.json +6 -2
@@ -680,7 +680,8 @@ async function _fireTrigger(options) {
680
680
  slug: options.slug,
681
681
  environment: options.environment,
682
682
  entityId: options.entityId,
683
- data: options.data
683
+ data: options.data,
684
+ ...options.organizationId && { organizationId: options.organizationId }
684
685
  }
685
686
  }),
686
687
  signal: AbortSignal.timeout(60000)
@@ -2053,6 +2054,7 @@ export interface AgentConfig {
2053
2054
  systemPrompt: string | (() => string | Promise<string>)
2054
2055
  model?: ModelConfig
2055
2056
  tools?: string[]
2057
+ roles?: string[]
2056
2058
  firstMessageSuggestions?: string[]
2057
2059
  threadContextParams?: Array<{
2058
2060
  name: string
@@ -2070,6 +2072,7 @@ export interface JSONSchemaProperty {
2070
2072
  items?: JSONSchemaProperty
2071
2073
  properties?: Record<string, JSONSchemaProperty>
2072
2074
  required?: string[]
2075
+ references?: string
2073
2076
  }
2074
2077
 
2075
2078
  export interface JSONSchema {
@@ -2115,6 +2118,7 @@ export interface FieldMaskConfig {
2115
2118
  export interface RoleConfig {
2116
2119
  name: string
2117
2120
  description?: string
2121
+ agentAccess?: string[]
2118
2122
  policies: PolicyConfig[]
2119
2123
  scopeRules?: ScopeRuleConfig[]
2120
2124
  fieldMasks?: FieldMaskConfig[]
@@ -2126,26 +2130,37 @@ export interface TriggerAction {
2126
2130
  as?: string
2127
2131
  }
2128
2132
 
2133
+ export interface TriggerOnEntity {
2134
+ entityType: string
2135
+ action: 'created' | 'updated' | 'deleted'
2136
+ condition?: Record<string, unknown>
2137
+ }
2138
+
2139
+ export interface TriggerOnSchedule {
2140
+ schedule: string
2141
+ timezone?: string
2142
+ }
2143
+
2144
+ export interface TriggerSchedule {
2145
+ delay?: number
2146
+ at?: string
2147
+ offset?: number
2148
+ cancelPrevious?: boolean
2149
+ }
2150
+
2151
+ export interface TriggerRetry {
2152
+ maxAttempts?: number
2153
+ backoffMs?: number
2154
+ }
2155
+
2129
2156
  export interface TriggerConfig {
2130
2157
  name: string
2131
2158
  slug: string
2132
2159
  description?: string
2133
- on: {
2134
- entityType: string
2135
- action: 'created' | 'updated' | 'deleted'
2136
- condition?: Record<string, unknown>
2137
- }
2160
+ on: TriggerOnEntity | TriggerOnSchedule
2161
+ schedule?: TriggerSchedule
2162
+ retry?: TriggerRetry
2138
2163
  actions: TriggerAction[]
2139
- schedule?: {
2140
- delay?: number
2141
- at?: string
2142
- offset?: number
2143
- cancelPrevious?: boolean
2144
- }
2145
- retry?: {
2146
- maxAttempts?: number
2147
- backoffMs?: number
2148
- }
2149
2164
  }
2150
2165
 
2151
2166
  export interface RouterAgentRef {
@@ -3150,7 +3165,8 @@ function extractAgentPayload(agent, customToolsMap) {
3150
3165
  maxTokens: agent.model?.maxTokens,
3151
3166
  reasoning: agent.model?.reasoning
3152
3167
  },
3153
- tools
3168
+ tools,
3169
+ roles: agent.roles
3154
3170
  };
3155
3171
  }
3156
3172
  function extractHandlerCode(handler) {
@@ -3633,7 +3649,10 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
3633
3649
  const apiKey = getApiKey();
3634
3650
  const nonInteractive = !isInteractive();
3635
3651
  if (nonInteractive) {
3636
- console.error("Error: struere dev is a long-running watch process. Use struere sync instead.");
3652
+ console.error(chalk7.red("Error:"), "`struere dev` requires an interactive terminal (TTY) to render its watch UI.");
3653
+ console.error(chalk7.gray(" - For a one-shot sync, use:"), chalk7.cyan("struere sync [--env development]"));
3654
+ console.error(chalk7.gray(" - For continuous deployment in CI/CD, use:"), chalk7.cyan("struere deploy"));
3655
+ console.error(chalk7.gray(" - To run dev in the foreground, attach a TTY (avoid backgrounding with `&` or piping output)."));
3637
3656
  process.exit(1);
3638
3657
  }
3639
3658
  console.log();
@@ -5849,7 +5868,8 @@ async function queryThreads(options) {
5849
5868
  environment: options.environment,
5850
5869
  ...options.agentId && { agentId: options.agentId },
5851
5870
  ...options.channel && { channel: options.channel },
5852
- ...options.limit && { limit: options.limit }
5871
+ ...options.limit && { limit: options.limit },
5872
+ ...options.organizationId && { organizationId: options.organizationId }
5853
5873
  });
5854
5874
  if (result.error || !result.data)
5855
5875
  return result;
@@ -5865,20 +5885,21 @@ async function queryThreads(options) {
5865
5885
  }
5866
5886
  return result;
5867
5887
  }
5868
- async function queryThreadDetail(threadId, messageLimit) {
5888
+ async function queryThreadDetail(threadId, messageLimit, organizationId) {
5869
5889
  return convexQuery2("threads:getWithMessages", {
5870
5890
  id: threadId,
5871
- ...messageLimit && { messageLimit }
5891
+ ...messageLimit && { messageLimit },
5892
+ ...organizationId && { organizationId }
5872
5893
  });
5873
5894
  }
5874
- async function queryThreadExecutions(threadId) {
5875
- return convexQuery2("executions:getByThread", { threadId });
5895
+ async function queryThreadExecutions(threadId, organizationId) {
5896
+ return convexQuery2("executions:getByThread", { threadId, ...organizationId && { organizationId } });
5876
5897
  }
5877
- async function resolveThreadId(shortId, environment) {
5898
+ async function resolveThreadId(shortId, environment, organizationId) {
5878
5899
  if (shortId.includes("|") || shortId.length > 20) {
5879
5900
  return { data: shortId };
5880
5901
  }
5881
- const result = await queryThreads({ environment: environment ?? "development", limit: 100 });
5902
+ const result = await queryThreads({ environment: environment ?? "development", limit: 100, organizationId });
5882
5903
  if (result.error || !result.data)
5883
5904
  return { error: result.error ?? "Could not fetch threads" };
5884
5905
  const threads = result.data;
@@ -5887,7 +5908,7 @@ async function resolveThreadId(shortId, environment) {
5887
5908
  for (const env of ["production", "development", "eval"]) {
5888
5909
  if (env === (environment ?? "development"))
5889
5910
  continue;
5890
- const envResult = await queryThreads({ environment: env, limit: 100 });
5911
+ const envResult = await queryThreads({ environment: env, limit: 100, organizationId });
5891
5912
  if (envResult.data) {
5892
5913
  const envThreads = envResult.data;
5893
5914
  const envMatch = envThreads.find((t) => t._id.endsWith(shortId));
@@ -5989,7 +6010,8 @@ logsCommand.command("list", { isDefault: true }).description("List recent conver
5989
6010
  agentId,
5990
6011
  channel: opts.channel,
5991
6012
  phone: opts.phone,
5992
- limit: parseInt(opts.limit, 10)
6013
+ limit: parseInt(opts.limit, 10),
6014
+ organizationId: orgId
5993
6015
  });
5994
6016
  if (error || !data) {
5995
6017
  spinner?.fail("Failed to fetch conversations");
@@ -6026,8 +6048,9 @@ logsCommand.command("list", { isDefault: true }).description("List recent conver
6026
6048
  logsCommand.command("view <thread-id>").description("View conversation messages").option("--env <environment>", "Environment hint for resolving short IDs").option("--exec", "Include execution details").option("--verbose", "Show full tool call arguments and results").option("--tail", "Show most recent messages (use with --limit)").option("--json", "Output raw JSON").option("--limit <n>", "Message limit", "100").action(async (rawThreadId, opts) => {
6027
6049
  await ensureAuth2();
6028
6050
  const spinner = opts.json ? null : ora12();
6051
+ const orgId = getOrgId2();
6029
6052
  spinner?.start("Resolving thread");
6030
- const resolved = await resolveThreadId(rawThreadId, opts.env);
6053
+ const resolved = await resolveThreadId(rawThreadId, opts.env, orgId);
6031
6054
  if (resolved.error || !resolved.data) {
6032
6055
  spinner?.fail("Thread not found");
6033
6056
  console.log(chalk16.red("Error:"), resolved.error || `No thread matched "${rawThreadId}"`);
@@ -6037,7 +6060,7 @@ logsCommand.command("view <thread-id>").description("View conversation messages"
6037
6060
  spinner?.stop();
6038
6061
  spinner?.start("Fetching conversation");
6039
6062
  const fetchLimit = opts.tail ? 1000 : parseInt(opts.limit, 10);
6040
- const { data, error } = await queryThreadDetail(threadId, fetchLimit);
6063
+ const { data, error } = await queryThreadDetail(threadId, fetchLimit, orgId);
6041
6064
  if (error || !data) {
6042
6065
  spinner?.fail("Failed to fetch conversation");
6043
6066
  console.log(chalk16.red("Error:"), error || "Thread not found");
@@ -6047,7 +6070,7 @@ logsCommand.command("view <thread-id>").description("View conversation messages"
6047
6070
  const result = data;
6048
6071
  let executions = [];
6049
6072
  if (opts.exec) {
6050
- const execResult = await queryThreadExecutions(threadId);
6073
+ const execResult = await queryThreadExecutions(threadId, orgId);
6051
6074
  if (execResult.data) {
6052
6075
  executions = execResult.data;
6053
6076
  }
@@ -7666,51 +7689,54 @@ async function convexMutation5(path, args) {
7666
7689
  }
7667
7690
  return json.value;
7668
7691
  }
7669
- async function listTriggers(environment) {
7670
- return convexQuery6("triggers:list", { environment });
7692
+ async function listTriggers(environment, organizationId) {
7693
+ return convexQuery6("triggers:list", { environment, ...organizationId && { organizationId } });
7671
7694
  }
7672
- async function getTrigger(slug, environment) {
7673
- return convexQuery6("triggers:getBySlug", { slug, environment });
7695
+ async function getTrigger(slug, environment, organizationId) {
7696
+ return convexQuery6("triggers:getBySlug", { slug, environment, ...organizationId && { organizationId } });
7674
7697
  }
7675
7698
  async function listTriggerRuns(options) {
7676
7699
  return convexQuery6("triggers:listRuns", {
7677
7700
  environment: options.environment,
7678
7701
  ...options.status && { status: options.status },
7679
7702
  ...options.triggerSlug && { triggerSlug: options.triggerSlug },
7680
- ...options.limit && { limit: options.limit }
7703
+ ...options.limit && { limit: options.limit },
7704
+ ...options.organizationId && { organizationId: options.organizationId }
7681
7705
  });
7682
7706
  }
7683
- async function getTriggerRunDetail(runId, environment) {
7707
+ async function getTriggerRunDetail(runId, environment, organizationId) {
7684
7708
  return convexQuery6("triggers:getRunDetail", {
7685
7709
  runId,
7686
- ...environment && { environment }
7710
+ ...environment && { environment },
7711
+ ...organizationId && { organizationId }
7687
7712
  });
7688
7713
  }
7689
- async function getTriggerRunStats(environment) {
7690
- return convexQuery6("triggers:getRunStats", { environment });
7714
+ async function getTriggerRunStats(environment, organizationId) {
7715
+ return convexQuery6("triggers:getRunStats", { environment, ...organizationId && { organizationId } });
7691
7716
  }
7692
- async function getLastRunStatuses(environment) {
7693
- return convexQuery6("triggers:getLastRunStatuses", { environment });
7717
+ async function getLastRunStatuses(environment, organizationId) {
7718
+ return convexQuery6("triggers:getLastRunStatuses", { environment, ...organizationId && { organizationId } });
7694
7719
  }
7695
- async function cancelTriggerRun(runId, environment) {
7696
- return convexMutation5("triggers:cancelRun", { runId, environment });
7720
+ async function cancelTriggerRun(runId, environment, organizationId) {
7721
+ return convexMutation5("triggers:cancelRun", { runId, environment, ...organizationId && { organizationId } });
7697
7722
  }
7698
- async function retryTriggerRun(runId, environment) {
7699
- return convexMutation5("triggers:retryRun", { runId, environment });
7723
+ async function retryTriggerRun(runId, environment, organizationId) {
7724
+ return convexMutation5("triggers:retryRun", { runId, environment, ...organizationId && { organizationId } });
7700
7725
  }
7701
- async function toggleTrigger(slug, enabled, environment) {
7726
+ async function toggleTrigger(slug, enabled, environment, organizationId) {
7702
7727
  const path = enabled ? "triggers:enable" : "triggers:disable";
7703
- return convexMutation5(path, { slug, environment });
7728
+ return convexMutation5(path, { slug, environment, ...organizationId && { organizationId } });
7704
7729
  }
7705
7730
  async function listTriggerExecutions(options) {
7706
7731
  return convexQuery6("triggers:listExecutions", {
7707
7732
  environment: options.environment,
7708
7733
  ...options.triggerSlug && { triggerSlug: options.triggerSlug },
7709
- ...options.limit && { limit: options.limit }
7734
+ ...options.limit && { limit: options.limit },
7735
+ ...options.organizationId && { organizationId: options.organizationId }
7710
7736
  });
7711
7737
  }
7712
- async function getTriggerExecutionDetail(eventId, environment) {
7713
- return convexQuery6("triggers:getExecutionDetail", { eventId, ...environment && { environment } });
7738
+ async function getTriggerExecutionDetail(eventId, environment, organizationId) {
7739
+ return convexQuery6("triggers:getExecutionDetail", { eventId, ...environment && { environment }, ...organizationId && { organizationId } });
7714
7740
  }
7715
7741
  async function convexAction3(path, args) {
7716
7742
  const token = getToken3();
@@ -7737,11 +7763,15 @@ async function convexAction3(path, args) {
7737
7763
  }
7738
7764
  return json.value;
7739
7765
  }
7740
- async function retryImmediateExecution(eventId, environment) {
7741
- return convexAction3("triggers:retryImmediateExecution", { eventId, environment });
7766
+ async function retryImmediateExecution(eventId, environment, organizationId) {
7767
+ return convexAction3("triggers:retryImmediateExecution", { eventId, environment, ...organizationId && { organizationId } });
7742
7768
  }
7743
7769
 
7744
7770
  // src/cli/commands/triggers.ts
7771
+ function getOrgId3() {
7772
+ const project = loadProject(process.cwd());
7773
+ return project?.organization.id;
7774
+ }
7745
7775
  async function withTriggerAuthRetry(fn) {
7746
7776
  try {
7747
7777
  return await fn();
@@ -7909,10 +7939,11 @@ triggersCommand.command("list", { isDefault: true }).description("List all trigg
7909
7939
  await ensureAuth5();
7910
7940
  const spinner = opts.json ? null : ora14();
7911
7941
  try {
7942
+ const orgId = getOrgId3();
7912
7943
  spinner?.start("Fetching triggers");
7913
7944
  const [triggers, statuses] = await Promise.all([
7914
- listTriggers(opts.env),
7915
- getLastRunStatuses(opts.env)
7945
+ listTriggers(opts.env, orgId),
7946
+ getLastRunStatuses(opts.env, orgId)
7916
7947
  ]);
7917
7948
  let filtered = triggers;
7918
7949
  if (opts.failed) {
@@ -7958,7 +7989,7 @@ triggersCommand.command("get <slug>").description("View trigger details").option
7958
7989
  const spinner = opts.json ? null : ora14();
7959
7990
  try {
7960
7991
  spinner?.start("Fetching trigger");
7961
- const trigger = await getTrigger(slug, opts.env);
7992
+ const trigger = await getTrigger(slug, opts.env, getOrgId3());
7962
7993
  if (!trigger) {
7963
7994
  spinner?.fail("Trigger not found");
7964
7995
  if (opts.json) {
@@ -8041,7 +8072,8 @@ triggersCommand.command("runs [slug]").description("List trigger runs").option("
8041
8072
  environment: opts.env,
8042
8073
  status: opts.status,
8043
8074
  triggerSlug: slug,
8044
- limit: parseInt(opts.limit, 10)
8075
+ limit: parseInt(opts.limit, 10),
8076
+ organizationId: getOrgId3()
8045
8077
  });
8046
8078
  spinner?.succeed(`Found ${runs.length} runs`);
8047
8079
  if (opts.json) {
@@ -8083,7 +8115,7 @@ triggersCommand.command("run <run-id>").description("View trigger run details").
8083
8115
  const spinner = opts.json ? null : ora14();
8084
8116
  try {
8085
8117
  spinner?.start("Fetching run");
8086
- const run = await getTriggerRunDetail(runId, opts.env);
8118
+ const run = await getTriggerRunDetail(runId, opts.env, getOrgId3());
8087
8119
  if (!run) {
8088
8120
  spinner?.fail("Run not found");
8089
8121
  if (opts.json) {
@@ -8127,8 +8159,9 @@ triggersCommand.command("stats").description("Show trigger run statistics").opti
8127
8159
  await ensureAuth5();
8128
8160
  const spinner = opts.json ? null : ora14();
8129
8161
  try {
8162
+ const orgId = getOrgId3();
8130
8163
  spinner?.start("Fetching statistics");
8131
- const stats = await getTriggerRunStats(opts.env);
8164
+ const stats = await getTriggerRunStats(opts.env, orgId);
8132
8165
  spinner?.succeed("Run statistics");
8133
8166
  if (opts.json) {
8134
8167
  console.log(JSON.stringify(stats, null, 2));
@@ -8153,7 +8186,7 @@ triggersCommand.command("stats").description("Show trigger run statistics").opti
8153
8186
  console.log(` ${chalk20.gray("\u2500".repeat(18))}`);
8154
8187
  console.log(` ${chalk20.bold("Total".padEnd(12))} ${chalk20.bold(String(total).padStart(6))}`);
8155
8188
  console.log();
8156
- const executions = await listTriggerExecutions({ environment: opts.env, limit: 50 });
8189
+ const executions = await listTriggerExecutions({ environment: opts.env, limit: 50, organizationId: orgId });
8157
8190
  const recentFailures = executions.filter((e) => e.eventType === "trigger.failed");
8158
8191
  const recentSuccesses = executions.length - recentFailures.length;
8159
8192
  console.log(chalk20.bold(` Recent Executions`));
@@ -8185,7 +8218,8 @@ triggersCommand.command("logs [slug]").description("View trigger execution histo
8185
8218
  const executions = await withTriggerAuthRetry(() => listTriggerExecutions({
8186
8219
  environment: opts.env,
8187
8220
  triggerSlug: slug,
8188
- limit: parseInt(opts.limit, 10)
8221
+ limit: parseInt(opts.limit, 10),
8222
+ organizationId: getOrgId3()
8189
8223
  }));
8190
8224
  spinner?.succeed(`Found ${executions.length} executions`);
8191
8225
  if (opts.json) {
@@ -8245,6 +8279,7 @@ triggersCommand.command("log <identifier>").description("View detailed trigger e
8245
8279
  await ensureAuth5();
8246
8280
  const spinner = opts.json ? null : ora14();
8247
8281
  try {
8282
+ const orgId = getOrgId3();
8248
8283
  const nth = parseInt(opts.nth, 10);
8249
8284
  if (isNaN(nth) || nth < 1) {
8250
8285
  console.error(chalk20.red("Error:"), "--nth must be a positive integer (e.g., --nth 1 for most recent)");
@@ -8257,7 +8292,8 @@ triggersCommand.command("log <identifier>").description("View detailed trigger e
8257
8292
  const executions = await withTriggerAuthRetry(() => listTriggerExecutions({
8258
8293
  environment: opts.env,
8259
8294
  triggerSlug: identifier,
8260
- limit: nth
8295
+ limit: nth,
8296
+ organizationId: orgId
8261
8297
  }));
8262
8298
  if (!executions.length) {
8263
8299
  spinner?.fail(`No executions found for trigger "${identifier}" in ${opts.env}`);
@@ -8275,7 +8311,7 @@ triggersCommand.command("log <identifier>").description("View detailed trigger e
8275
8311
  spinner?.succeed(`Found execution for "${identifier}"`);
8276
8312
  }
8277
8313
  spinner?.start("Fetching execution detail");
8278
- const event = await withTriggerAuthRetry(() => getTriggerExecutionDetail(eventId, opts.env));
8314
+ const event = await withTriggerAuthRetry(() => getTriggerExecutionDetail(eventId, opts.env, orgId));
8279
8315
  if (!event) {
8280
8316
  spinner?.fail("Execution not found");
8281
8317
  if (opts.json) {
@@ -8337,7 +8373,7 @@ triggersCommand.command("retry <run-id>").description("Retry a failed or dead ru
8337
8373
  }
8338
8374
  try {
8339
8375
  spinner?.start("Retrying run...");
8340
- await retryTriggerRun(runId, environment);
8376
+ await retryTriggerRun(runId, environment, getOrgId3());
8341
8377
  spinner?.succeed(chalk20.green(`Run ${runId} queued for retry`));
8342
8378
  if (opts.json) {
8343
8379
  console.log(JSON.stringify({ success: true, runId }));
@@ -8368,7 +8404,7 @@ triggersCommand.command("cancel <run-id>").description("Cancel a pending run").o
8368
8404
  }
8369
8405
  try {
8370
8406
  spinner?.start("Cancelling run...");
8371
- await cancelTriggerRun(runId, environment);
8407
+ await cancelTriggerRun(runId, environment, getOrgId3());
8372
8408
  spinner?.succeed(chalk20.green(`Run ${runId} cancelled`));
8373
8409
  if (opts.json) {
8374
8410
  console.log(JSON.stringify({ success: true, runId }));
@@ -8399,7 +8435,7 @@ triggersCommand.command("enable <slug>").description("Enable a trigger").option(
8399
8435
  }
8400
8436
  try {
8401
8437
  spinner?.start("Enabling trigger...");
8402
- await toggleTrigger(slug, true, environment);
8438
+ await toggleTrigger(slug, true, environment, getOrgId3());
8403
8439
  spinner?.succeed(chalk20.green(`Trigger ${slug} enabled`));
8404
8440
  if (opts.json) {
8405
8441
  console.log(JSON.stringify({ success: true, slug }));
@@ -8430,7 +8466,7 @@ triggersCommand.command("disable <slug>").description("Disable a trigger").optio
8430
8466
  }
8431
8467
  try {
8432
8468
  spinner?.start("Disabling trigger...");
8433
- await toggleTrigger(slug, false, environment);
8469
+ await toggleTrigger(slug, false, environment, getOrgId3());
8434
8470
  spinner?.succeed(chalk20.green(`Trigger ${slug} disabled`));
8435
8471
  if (opts.json) {
8436
8472
  console.log(JSON.stringify({ success: true, slug }));
@@ -8461,7 +8497,7 @@ triggersCommand.command("retry-event <event-id>").description("Retry a failed im
8461
8497
  }
8462
8498
  try {
8463
8499
  spinner?.start("Retrying failed execution...");
8464
- const result = await retryImmediateExecution(eventId, environment);
8500
+ const result = await retryImmediateExecution(eventId, environment, getOrgId3());
8465
8501
  if (result.success) {
8466
8502
  spinner?.succeed(chalk20.green(`Execution retried successfully`));
8467
8503
  } else {
@@ -8509,7 +8545,8 @@ triggersCommand.command("fire <slug>").description("Manually fire a trigger").op
8509
8545
  slug,
8510
8546
  environment,
8511
8547
  entityId: opts.entity,
8512
- data
8548
+ data,
8549
+ organizationId: getOrgId3()
8513
8550
  });
8514
8551
  if (error) {
8515
8552
  spinner?.fail("Trigger execution failed");
@@ -8667,12 +8704,14 @@ async function listThreads(options) {
8667
8704
  return convexQuery7("threads:listWithPreviews", {
8668
8705
  environment: options.environment,
8669
8706
  channel: options.channel,
8670
- limit: options.limit
8707
+ limit: options.limit,
8708
+ ...options.organizationId && { organizationId: options.organizationId }
8671
8709
  });
8672
8710
  }
8673
8711
  async function getThreadWithMessages(options) {
8674
8712
  return convexQuery7("threads:getWithMessages", {
8675
- id: options.threadId
8713
+ id: options.threadId,
8714
+ ...options.organizationId && { organizationId: options.organizationId }
8676
8715
  });
8677
8716
  }
8678
8717
  async function archiveThread(options) {
@@ -9298,7 +9337,7 @@ function printExecutionMeta(meta) {
9298
9337
  console.log(chalk24.dim(`Tool errors: ${chalk24.yellow(String(meta.errorCount))}`));
9299
9338
  }
9300
9339
  }
9301
- var chatCommand = new Command22("chat").description("Chat with an agent or via a router").argument("<slug>", "Agent slug (or router slug when --router is used)").option("--env <environment>", "Environment: development | production | eval", "development").option("--thread <id>", "Continue an existing thread").option("--message <msg>", "Single message mode (send and exit)").option("--json", "Output JSON").option("--channel <channel>", "Channel identifier", "api").option("-v, --verbose", "Show execution metadata (model, duration, tool calls)").option("--confirm", "Skip production warning prompt").option("--router", "Chat via a router instead of directly with an agent").option("--phone <number>", "Sender phone number for routing rules").option("--follow", "Continue in interactive mode after sending a message").action(async (slug, options) => {
9340
+ var chatCommand = new Command22("chat").description("Chat with an agent or via a router").argument("<slug>", "Agent slug (or router slug when --router is used)").option("--env <environment>", "Environment: development | production | eval", "development").option("--thread <id>", "Continue an existing thread").option("--message <msg>", "Single message mode (send and exit)").option("--json", "Output structured JSON including thread ID, message, usage, and per-tool-call execution metadata.").option("--channel <channel>", "Channel identifier", "api").option("-v, --verbose", "Show execution metadata (model, duration, tool calls)").option("--confirm", "Skip production warning prompt").option("--router", "Chat via a router instead of directly with an agent").option("--phone <number>", "Sender phone number for routing rules").option("--follow", "Continue in interactive mode after sending a message").action(async (slug, options) => {
9302
9341
  const spinner = ora18();
9303
9342
  const cwd = process.cwd();
9304
9343
  const nonInteractive = !isInteractive();
@@ -9436,7 +9475,17 @@ var chatCommand = new Command22("chat").description("Chat with an agent or via a
9436
9475
  spinner.succeed("Message sent");
9437
9476
  const routerResult = result;
9438
9477
  if (jsonMode) {
9439
- const output = { message: result.message, threadId: result.threadId, usage: result.usage };
9478
+ const output = { threadId: result.threadId, message: result.message, usage: result.usage };
9479
+ if (result._executionMeta) {
9480
+ output.executionMeta = {
9481
+ iterationCount: result._executionMeta.iterationCount,
9482
+ model: result._executionMeta.model,
9483
+ durationMs: result._executionMeta.durationMs,
9484
+ toolCalls: result._executionMeta.toolCallSummary,
9485
+ errorCount: result._executionMeta.errorCount,
9486
+ permissionDenialCount: result._executionMeta.permissionDenialCount
9487
+ };
9488
+ }
9440
9489
  if (routerResult.routedToAgent) {
9441
9490
  output.routedToAgent = routerResult.routedToAgent;
9442
9491
  output.routedToAgentSlug = routerResult.routedToAgentSlug;
@@ -10450,8 +10499,8 @@ function checkModelConfig(resources) {
10450
10499
  hint: "Check agent model config - temperature should be 0-2"
10451
10500
  };
10452
10501
  }
10453
- async function checkStuckThreads(environment) {
10454
- const threads = await withAuthRetry2(() => listThreads({ environment, limit: 100 }));
10502
+ async function checkStuckThreads(environment, organizationId) {
10503
+ const threads = await withAuthRetry2(() => listThreads({ environment, limit: 100, organizationId }));
10455
10504
  const issues = [];
10456
10505
  const now = Date.now();
10457
10506
  const fiveMinutes = 5 * 60 * 1000;
@@ -10480,8 +10529,8 @@ async function checkStuckThreads(environment) {
10480
10529
  hint: "Run `struere threads archive <id>` to archive stuck threads"
10481
10530
  };
10482
10531
  }
10483
- async function checkTriggerHealth(environment) {
10484
- const executions = await withAuthRetry2(() => listTriggerExecutions({ environment, limit: 100 }));
10532
+ async function checkTriggerHealth(environment, organizationId) {
10533
+ const executions = await withAuthRetry2(() => listTriggerExecutions({ environment, limit: 100, organizationId }));
10485
10534
  if (executions.length === 0) {
10486
10535
  return {
10487
10536
  id: "trigger-health",
@@ -10528,8 +10577,8 @@ async function checkTriggerHealth(environment) {
10528
10577
  hint: "Run `struere triggers logs <slug>` to investigate failures"
10529
10578
  };
10530
10579
  }
10531
- async function checkSyncDrift(resources, environment) {
10532
- const { state, error } = await withAuthRetry2(() => getSyncState(undefined, environment));
10580
+ async function checkSyncDrift(resources, environment, organizationId) {
10581
+ const { state, error } = await withAuthRetry2(() => getSyncState(organizationId, environment));
10533
10582
  if (error || !state) {
10534
10583
  return {
10535
10584
  id: "sync-drift",
@@ -10638,6 +10687,8 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
10638
10687
  const spinner = opts.json ? null : ora20();
10639
10688
  const cwd = process.cwd();
10640
10689
  const environment = opts.env;
10690
+ const project = loadProject(cwd);
10691
+ const organizationId = project?.organization.id;
10641
10692
  try {
10642
10693
  spinner?.start("Loading resources...");
10643
10694
  const resources = await loadAllResources(cwd);
@@ -10648,9 +10699,9 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
10648
10699
  runCheck(() => checkTemplateApprovals(resources)),
10649
10700
  runCheck(() => checkEntityTypeReferences(resources)),
10650
10701
  runCheck(() => checkModelConfig(resources)),
10651
- runCheck(() => checkStuckThreads(environment)),
10652
- runCheck(() => checkTriggerHealth(environment)),
10653
- runCheck(() => checkSyncDrift(resources, environment))
10702
+ runCheck(() => checkStuckThreads(environment, organizationId)),
10703
+ runCheck(() => checkTriggerHealth(environment, organizationId)),
10704
+ runCheck(() => checkSyncDrift(resources, environment, organizationId))
10654
10705
  ]);
10655
10706
  spinner?.stop();
10656
10707
  const checks = results.map((r) => r.status === "fulfilled" ? r.value : { id: "unknown", label: "Unknown", status: "warn", message: `Check failed: ${r.reason}` });
@@ -10704,10 +10755,332 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
10704
10755
  process.exit(1);
10705
10756
  }
10706
10757
  });
10758
+
10759
+ // src/cli/commands/keys.ts
10760
+ init_credentials();
10761
+ import { Command as Command26 } from "commander";
10762
+ import chalk29 from "chalk";
10763
+ import ora21 from "ora";
10764
+ import { input as input3, confirm as confirm8 } from "@inquirer/prompts";
10765
+ init_convex();
10766
+
10767
+ // src/cli/utils/apiKeys.ts
10768
+ init_credentials();
10769
+ init_config();
10770
+ function getToken5() {
10771
+ const credentials = loadCredentials();
10772
+ const apiKey = getApiKey();
10773
+ const token = apiKey || credentials?.token;
10774
+ if (!token)
10775
+ throw new Error("Not authenticated");
10776
+ return token;
10777
+ }
10778
+ async function convexQuery8(path, args) {
10779
+ const token = getToken5();
10780
+ const response = await fetch(`${CONVEX_URL}/api/query`, {
10781
+ method: "POST",
10782
+ headers: {
10783
+ "Content-Type": "application/json",
10784
+ Authorization: `Bearer ${token}`
10785
+ },
10786
+ body: JSON.stringify({ path, args })
10787
+ });
10788
+ const text = await response.text();
10789
+ let json;
10790
+ try {
10791
+ json = JSON.parse(text);
10792
+ } catch {
10793
+ throw new Error(text || `HTTP ${response.status}`);
10794
+ }
10795
+ if (!response.ok) {
10796
+ throw new Error(json.errorData?.message || json.errorMessage || text);
10797
+ }
10798
+ if (json.status === "error") {
10799
+ throw new Error(json.errorMessage || "Unknown error from Convex");
10800
+ }
10801
+ return json.value;
10802
+ }
10803
+ async function convexMutation6(path, args) {
10804
+ const token = getToken5();
10805
+ const response = await fetch(`${CONVEX_URL}/api/mutation`, {
10806
+ method: "POST",
10807
+ headers: {
10808
+ "Content-Type": "application/json",
10809
+ Authorization: `Bearer ${token}`
10810
+ },
10811
+ body: JSON.stringify({ path, args })
10812
+ });
10813
+ const text = await response.text();
10814
+ let json;
10815
+ try {
10816
+ json = JSON.parse(text);
10817
+ } catch {
10818
+ throw new Error(text || `HTTP ${response.status}`);
10819
+ }
10820
+ if (!response.ok) {
10821
+ throw new Error(json.errorData?.message || json.errorMessage || text);
10822
+ }
10823
+ if (json.status === "error") {
10824
+ throw new Error(json.errorMessage || "Unknown error from Convex");
10825
+ }
10826
+ return json.value;
10827
+ }
10828
+ async function listApiKeys() {
10829
+ return convexQuery8("apiKeys:list", {});
10830
+ }
10831
+ async function createApiKey(args) {
10832
+ return convexMutation6("apiKeys:create", {
10833
+ name: args.name,
10834
+ environment: args.environment,
10835
+ permissions: args.permissions ?? ["*"]
10836
+ });
10837
+ }
10838
+ async function removeApiKey(id) {
10839
+ return convexMutation6("apiKeys:remove", { id });
10840
+ }
10841
+
10842
+ // src/cli/commands/keys.ts
10843
+ async function withKeyAuthRetry(fn) {
10844
+ try {
10845
+ return await fn();
10846
+ } catch (err) {
10847
+ const msg = err instanceof Error ? err.message : String(err);
10848
+ if (msg.includes("Unauthenticated") || msg.includes("OIDC") || msg.includes("token") || msg.includes("expired")) {
10849
+ const refreshed = await refreshToken();
10850
+ if (!refreshed)
10851
+ throw err;
10852
+ return fn();
10853
+ }
10854
+ throw err;
10855
+ }
10856
+ }
10857
+ async function ensureAuth9() {
10858
+ const cwd = process.cwd();
10859
+ const nonInteractive = !isInteractive();
10860
+ if (!hasProject(cwd)) {
10861
+ if (nonInteractive) {
10862
+ console.error(chalk29.red("No struere.json found. Run struere init first."));
10863
+ process.exit(1);
10864
+ }
10865
+ console.log(chalk29.yellow("No struere.json found - initializing project..."));
10866
+ console.log();
10867
+ const success = await runInit(cwd);
10868
+ if (!success) {
10869
+ process.exit(1);
10870
+ }
10871
+ console.log();
10872
+ }
10873
+ let credentials = loadCredentials();
10874
+ const apiKey = getApiKey();
10875
+ if (!credentials && !apiKey) {
10876
+ if (nonInteractive) {
10877
+ console.error(chalk29.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
10878
+ process.exit(1);
10879
+ }
10880
+ console.log(chalk29.yellow("Not logged in - authenticating..."));
10881
+ console.log();
10882
+ credentials = await performLogin();
10883
+ if (!credentials) {
10884
+ console.log(chalk29.red("Authentication failed"));
10885
+ process.exit(1);
10886
+ }
10887
+ console.log();
10888
+ }
10889
+ return true;
10890
+ }
10891
+ function relativeTime4(ts) {
10892
+ const diff = Date.now() - ts;
10893
+ const seconds = Math.floor(diff / 1000);
10894
+ if (seconds < 60)
10895
+ return `${seconds}s ago`;
10896
+ const minutes = Math.floor(seconds / 60);
10897
+ if (minutes < 60)
10898
+ return `${minutes}m ago`;
10899
+ const hours = Math.floor(minutes / 60);
10900
+ if (hours < 24)
10901
+ return `${hours}h ago`;
10902
+ const days = Math.floor(hours / 24);
10903
+ return `${days}d ago`;
10904
+ }
10905
+ function maskedPrefix(prefix) {
10906
+ return `${prefix}****`;
10907
+ }
10908
+ function isValidEnvironment(value) {
10909
+ return value === "development" || value === "production" || value === "eval";
10910
+ }
10911
+ var keysCommand = new Command26("keys").description("Manage API keys");
10912
+ keysCommand.command("list", { isDefault: true }).description("List API keys").option("--env <environment>", "Filter by environment (development|production|eval)").option("--json", "Output raw JSON").action(async (opts) => {
10913
+ await ensureAuth9();
10914
+ const spinner = opts.json ? null : ora21();
10915
+ try {
10916
+ spinner?.start("Fetching API keys");
10917
+ let keys = await withKeyAuthRetry(() => listApiKeys());
10918
+ if (opts.env) {
10919
+ if (!isValidEnvironment(opts.env)) {
10920
+ spinner?.fail(`Invalid environment: ${opts.env}`);
10921
+ process.exit(1);
10922
+ }
10923
+ keys = keys.filter((k) => k.environment === opts.env);
10924
+ }
10925
+ spinner?.succeed(`Found ${keys.length} API key${keys.length === 1 ? "" : "s"}${opts.env ? ` (${opts.env})` : ""}`);
10926
+ if (opts.json) {
10927
+ console.log(JSON.stringify(keys, null, 2));
10928
+ return;
10929
+ }
10930
+ console.log();
10931
+ renderTable([
10932
+ { key: "id", label: "ID", width: 14 },
10933
+ { key: "name", label: "Name", width: 24 },
10934
+ { key: "prefix", label: "Prefix", width: 22 },
10935
+ { key: "environment", label: "Env", width: 12 },
10936
+ { key: "created", label: "Created", width: 12 },
10937
+ { key: "lastUsed", label: "Last Used", width: 12 }
10938
+ ], keys.map((k) => ({
10939
+ id: k.id.slice(-12),
10940
+ name: k.name,
10941
+ prefix: maskedPrefix(k.keyPrefix),
10942
+ environment: k.environment,
10943
+ created: relativeTime4(k.createdAt),
10944
+ lastUsed: k.lastUsedAt ? relativeTime4(k.lastUsedAt) : chalk29.gray("-")
10945
+ })));
10946
+ console.log();
10947
+ } catch (err) {
10948
+ const message = err instanceof Error ? err.message : String(err);
10949
+ spinner?.fail("Failed to fetch API keys");
10950
+ if (opts.json) {
10951
+ console.log(JSON.stringify({ success: false, error: message }));
10952
+ } else {
10953
+ console.log(chalk29.red("Error:"), message);
10954
+ }
10955
+ process.exit(1);
10956
+ }
10957
+ });
10958
+ keysCommand.command("create").description("Create a new API key").option("--name <name>", "Key name").option("--env <environment>", "Environment (development|production|eval)", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (opts) => {
10959
+ await ensureAuth9();
10960
+ if (!isValidEnvironment(opts.env)) {
10961
+ const message = `Invalid environment: ${opts.env}`;
10962
+ if (opts.json) {
10963
+ console.log(JSON.stringify({ success: false, error: message }));
10964
+ } else {
10965
+ console.log(chalk29.red("Error:"), message);
10966
+ }
10967
+ process.exit(1);
10968
+ }
10969
+ const environment = opts.env;
10970
+ let name = opts.name;
10971
+ if (!name || !name.trim()) {
10972
+ if (!isInteractive()) {
10973
+ const message = "--name is required in non-interactive mode";
10974
+ if (opts.json) {
10975
+ console.log(JSON.stringify({ success: false, error: message }));
10976
+ } else {
10977
+ console.log(chalk29.red("Error:"), message);
10978
+ }
10979
+ process.exit(1);
10980
+ }
10981
+ name = await input3({
10982
+ message: "API key name:",
10983
+ validate: (v) => v.trim().length > 0 || "Name is required"
10984
+ });
10985
+ }
10986
+ name = name.trim();
10987
+ if (environment === "production" && !opts.confirm && !opts.json && isInteractive()) {
10988
+ const ok = await confirm8({
10989
+ message: chalk29.yellow("Create API key in PRODUCTION environment?"),
10990
+ default: false
10991
+ });
10992
+ if (!ok) {
10993
+ console.log(chalk29.gray("Cancelled"));
10994
+ process.exit(0);
10995
+ }
10996
+ }
10997
+ const spinner = opts.json ? null : ora21();
10998
+ try {
10999
+ spinner?.start("Creating API key");
11000
+ const result = await withKeyAuthRetry(() => createApiKey({ name, environment }));
11001
+ spinner?.succeed(chalk29.green(`Created API key ${chalk29.cyan(result.name)}`));
11002
+ if (opts.json) {
11003
+ console.log(JSON.stringify({
11004
+ id: result.id,
11005
+ name: result.name,
11006
+ prefix: result.keyPrefix,
11007
+ key: result.key,
11008
+ environment,
11009
+ createdAt: result.createdAt
11010
+ }, null, 2));
11011
+ return;
11012
+ }
11013
+ console.log();
11014
+ console.log(chalk29.bold(" " + chalk29.yellow("Save this key now \u2014 it will not be shown again.")));
11015
+ console.log();
11016
+ console.log(` ${chalk29.gray("Key:")} ${chalk29.cyan(result.key)}`);
11017
+ console.log(` ${chalk29.gray("Name:")} ${result.name}`);
11018
+ console.log(` ${chalk29.gray("Prefix:")} ${result.keyPrefix}`);
11019
+ console.log(` ${chalk29.gray("Environment:")} ${environment}`);
11020
+ console.log(` ${chalk29.gray("ID:")} ${result.id}`);
11021
+ console.log();
11022
+ } catch (err) {
11023
+ const message = err instanceof Error ? err.message : String(err);
11024
+ spinner?.fail("Failed to create API key");
11025
+ if (opts.json) {
11026
+ console.log(JSON.stringify({ success: false, error: message }));
11027
+ } else {
11028
+ console.log(chalk29.red("Error:"), message);
11029
+ }
11030
+ process.exit(1);
11031
+ }
11032
+ });
11033
+ keysCommand.command("revoke <id-or-prefix>").description("Revoke an API key (delete)").option("--confirm", "Skip confirmation prompt").option("--json", "Output raw JSON").action(async (idOrPrefix, opts) => {
11034
+ await ensureAuth9();
11035
+ const spinner = opts.json ? null : ora21();
11036
+ try {
11037
+ spinner?.start("Looking up API key");
11038
+ const keys = await withKeyAuthRetry(() => listApiKeys());
11039
+ const target = keys.find((k) => k.id === idOrPrefix) ?? keys.find((k) => k.id.endsWith(idOrPrefix)) ?? keys.find((k) => k.keyPrefix === idOrPrefix) ?? keys.find((k) => k.keyPrefix.startsWith(idOrPrefix));
11040
+ if (!target) {
11041
+ spinner?.fail(`API key not found: ${idOrPrefix}`);
11042
+ if (opts.json) {
11043
+ console.log(JSON.stringify({ success: false, error: "API key not found" }));
11044
+ }
11045
+ process.exit(1);
11046
+ }
11047
+ spinner?.succeed(`Found ${chalk29.cyan(target.name)} (${maskedPrefix(target.keyPrefix)}, ${target.environment})`);
11048
+ if (!opts.confirm && !opts.json) {
11049
+ if (!isInteractive()) {
11050
+ console.log(chalk29.red("Error:"), "Use --confirm to revoke in non-interactive mode");
11051
+ process.exit(1);
11052
+ }
11053
+ const ok = await confirm8({
11054
+ message: `Revoke this key? This cannot be undone.`,
11055
+ default: false
11056
+ });
11057
+ if (!ok) {
11058
+ console.log(chalk29.gray("Cancelled"));
11059
+ process.exit(0);
11060
+ }
11061
+ }
11062
+ const revokeSpinner = opts.json ? null : ora21();
11063
+ revokeSpinner?.start("Revoking API key");
11064
+ await withKeyAuthRetry(() => removeApiKey(target.id));
11065
+ revokeSpinner?.succeed(chalk29.green(`Revoked ${chalk29.cyan(target.name)}`));
11066
+ if (opts.json) {
11067
+ console.log(JSON.stringify({ success: true, id: target.id, name: target.name }));
11068
+ }
11069
+ } catch (err) {
11070
+ const message = err instanceof Error ? err.message : String(err);
11071
+ spinner?.fail("Failed to revoke API key");
11072
+ if (opts.json) {
11073
+ console.log(JSON.stringify({ success: false, error: message }));
11074
+ } else {
11075
+ console.log(chalk29.red("Error:"), message);
11076
+ }
11077
+ process.exit(1);
11078
+ }
11079
+ });
10707
11080
  // package.json
10708
11081
  var package_default = {
10709
11082
  name: "struere",
10710
- version: "0.14.2",
11083
+ version: "0.14.4",
10711
11084
  description: "Build, test, and deploy AI agents",
10712
11085
  keywords: [
10713
11086
  "ai",
@@ -10742,13 +11115,17 @@ var package_default = {
10742
11115
  ".": {
10743
11116
  import: "./dist/index.js",
10744
11117
  types: "./dist/index.d.ts"
11118
+ },
11119
+ "./client": {
11120
+ import: "./dist/client/index.js",
11121
+ types: "./dist/client/index.d.ts"
10745
11122
  }
10746
11123
  },
10747
11124
  files: [
10748
11125
  "dist"
10749
11126
  ],
10750
11127
  scripts: {
10751
- build: "bun build ./src/cli/index.ts --outdir ./dist/cli --target bun --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/bin/struere.ts --outdir ./dist/bin --target bun --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
11128
+ build: "bun build ./src/cli/index.ts --outdir ./dist/cli --target bun --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/client/index.ts --outdir ./dist/client --target browser && bun build ./src/bin/struere.ts --outdir ./dist/bin --target bun --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
10752
11129
  dev: "tsc --watch",
10753
11130
  test: "bun test",
10754
11131
  prepublishOnly: "bun run build"
@@ -10832,4 +11209,5 @@ program.addCommand(chatCommand);
10832
11209
  program.addCommand(whatsappCommand);
10833
11210
  program.addCommand(diffCommand);
10834
11211
  program.addCommand(doctorCommand);
11212
+ program.addCommand(keysCommand);
10835
11213
  program.parse();