@keystrokehq/cli 1.0.24 → 1.0.26

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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as __require, t as __commonJSMin } from "./chunk-DiodbrVj.mjs";
3
- import { $n as NEVER, An as ZodType, Bn as literal, Cn as normalizeCredentialList, Fn as array, Gn as preprocess, Hn as number, In as boolean$1, Jn as union, Kn as record, Ln as custom, Lt as ROUTE_MANIFEST_REL_PATH, Mn as _function, Nn as _null, Pn as any, Pt as PromptResponseSchema, Qn as toJSONSchema, Rn as discriminatedUnion, Un as object, Vn as looseObject, Wn as optional, Xn as url, Yn as unknown, Zn as datetime, jn as _enum, kn as number$1, qn as string, xn as credentialInputSchema, zn as intersection } from "./dist-3h33yl2W.mjs";
4
- import "./pack-artifact-NGxvGcXq-B30lSwyC.mjs";
3
+ import { $n as union, Bn as array, Dn as normalizeCredentialList, Fn as ZodType, Gn as literal, Hn as custom, In as _enum, Jn as object, Kn as looseObject, Ln as _function, Lt as ROUTE_MANIFEST_REL_PATH, Pn as number$1, Pt as PromptResponseSchema, Qn as string, Rn as _null, Tn as credentialInputSchema, Un as discriminatedUnion, Vn as boolean$1, Wn as intersection, Xn as preprocess, Yn as optional, Zn as record, er as unknown, ir as NEVER, nr as datetime, qn as number, rr as toJSONSchema, tr as url, zn as any } from "./dist-BUK3crkq.mjs";
4
+ import "./pack-artifact-DVnIKrsg-CZTT2aF_.mjs";
5
5
  import "./chunk-BZUGFHVS-CPWRFwK8.mjs";
6
6
  import "./chunk-DLL7UR66-BUYgzxnR.mjs";
7
7
  import "./chunk-TN7HHBQW-CSB_R-XD.mjs";
@@ -23,9 +23,8 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSy
23
23
  import { readdir, stat } from "node:fs/promises";
24
24
  import "node:child_process";
25
25
  import { pathToFileURL } from "node:url";
26
- import "node:crypto";
26
+ import { createHash } from "node:crypto";
27
27
  import { AsyncLocalStorage } from "node:async_hooks";
28
- import "pg";
29
28
  import { sql } from "drizzle-orm";
30
29
  import "better-sqlite3";
31
30
  import "@aws-sdk/client-s3";
@@ -6895,6 +6894,7 @@ var PrimaryKey = class {
6895
6894
  new AsyncLocalStorage();
6896
6895
  new AsyncLocalStorage();
6897
6896
  new AsyncLocalStorage();
6897
+ createRequire(import.meta.url);
6898
6898
  function buildPgSchema$1(registry) {
6899
6899
  return Object.fromEntries(Object.entries(registry).map(([name, { pg }]) => [name, pg]));
6900
6900
  }
@@ -7009,82 +7009,106 @@ const usageRecordsSqlite = sqliteTable("usage_records", {
7009
7009
  metadata: text("metadata", { mode: "json" }),
7010
7010
  createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull()
7011
7011
  }, (table) => [index("usage_records_run_run_kind_idx").on(table.runId, table.runKind), index("usage_records_kind_idx").on(table.kind)]);
7012
- const triggerAttachments = pgTable("trigger_attachments", {
7012
+ const triggers = pgTable("triggers", {
7013
7013
  id: text$1("id").primaryKey(),
7014
7014
  projectId: text$1("project_id").notNull(),
7015
7015
  slug: text$1("slug").notNull(),
7016
+ kind: text$1("kind").$type().notNull(),
7016
7017
  name: text$1("name"),
7017
7018
  description: text$1("description"),
7018
- targetKind: text$1("target_kind").$type().notNull().default("workflow"),
7019
- workflowSlug: text$1("workflow_slug"),
7020
- agentSlug: text$1("agent_slug"),
7021
7019
  moduleFile: text$1("module_file").notNull(),
7022
- source: jsonb("source").$type().notNull(),
7023
7020
  origin: text$1("origin").$type().notNull().default("project"),
7024
- prompt: text$1("prompt"),
7021
+ config: jsonb("config").$type(),
7022
+ schedule: text$1("schedule"),
7023
+ nextRunAt: timestamp("next_run_at", { withTimezone: true }),
7024
+ enabled: integer$1("enabled").notNull().default(1),
7025
7025
  lifecycle: jsonb("lifecycle").$type(),
7026
7026
  createdBySessionId: text$1("created_by_session_id"),
7027
+ triggerHash: text$1("trigger_hash"),
7028
+ overviewMarkdown: text$1("overview_markdown"),
7029
+ overviewHash: text$1("overview_hash"),
7030
+ overviewModel: text$1("overview_model"),
7031
+ overviewStatus: text$1("overview_status").$type().notNull().default("idle"),
7032
+ overviewGeneratedAt: timestamp("overview_generated_at", { withTimezone: true }),
7027
7033
  registeredAt: timestamp("registered_at", { withTimezone: true }).notNull(),
7028
7034
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(),
7029
7035
  deletedAt: timestamp("deleted_at", { withTimezone: true })
7030
- }, (table) => [uniqueIndex$1("trigger_attachments_project_id_slug_idx").on(table.projectId, table.slug)]);
7031
- const triggerAttachmentsSqlite = sqliteTable("trigger_attachments", {
7036
+ }, (table) => [uniqueIndex$1("triggers_project_id_slug_idx").on(table.projectId, table.slug), index$1("triggers_next_run_at_enabled_idx").on(table.nextRunAt, table.enabled)]);
7037
+ const triggersSqlite = sqliteTable("triggers", {
7032
7038
  id: text("id").primaryKey(),
7033
7039
  projectId: text("project_id").notNull(),
7034
7040
  slug: text("slug").notNull(),
7041
+ kind: text("kind").$type().notNull(),
7035
7042
  name: text("name"),
7036
7043
  description: text("description"),
7037
- targetKind: text("target_kind").$type().notNull().default("workflow"),
7038
- workflowSlug: text("workflow_slug"),
7039
- agentSlug: text("agent_slug"),
7040
7044
  moduleFile: text("module_file").notNull(),
7041
- source: text("source", { mode: "json" }).$type().notNull(),
7042
7045
  origin: text("origin").$type().notNull().default("project"),
7043
- prompt: text("prompt"),
7046
+ config: text("config", { mode: "json" }).$type(),
7047
+ schedule: text("schedule"),
7048
+ nextRunAt: integer("next_run_at", { mode: "timestamp_ms" }),
7049
+ enabled: integer("enabled").notNull().default(1),
7044
7050
  lifecycle: text("lifecycle", { mode: "json" }).$type(),
7045
7051
  createdBySessionId: text("created_by_session_id"),
7052
+ triggerHash: text("trigger_hash"),
7053
+ overviewMarkdown: text("overview_markdown"),
7054
+ overviewHash: text("overview_hash"),
7055
+ overviewModel: text("overview_model"),
7056
+ overviewStatus: text("overview_status").$type().notNull().default("idle"),
7057
+ overviewGeneratedAt: integer("overview_generated_at", { mode: "timestamp_ms" }),
7046
7058
  registeredAt: integer("registered_at", { mode: "timestamp_ms" }).notNull(),
7047
7059
  updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(),
7048
7060
  deletedAt: integer("deleted_at", { mode: "timestamp_ms" })
7049
- }, (table) => [uniqueIndex("trigger_attachments_project_id_slug_idx").on(table.projectId, table.slug)]);
7050
- const triggerSchedules = pgTable("trigger_schedules", {
7061
+ }, (table) => [uniqueIndex("triggers_project_id_slug_idx").on(table.projectId, table.slug), index("triggers_next_run_at_enabled_idx").on(table.nextRunAt, table.enabled)]);
7062
+ const triggerAttachments = pgTable("trigger_attachments", {
7051
7063
  id: text$1("id").primaryKey(),
7052
7064
  projectId: text$1("project_id").notNull(),
7053
- attachmentSlug: text$1("attachment_slug").notNull(),
7054
- kind: text$1("kind").$type().notNull(),
7055
- schedule: text$1("schedule").notNull(),
7056
- nextRunAt: timestamp("next_run_at", { withTimezone: true }).notNull(),
7057
- enabled: integer$1("enabled").notNull(),
7058
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull()
7059
- }, (table) => [uniqueIndex$1("trigger_schedules_project_id_attachment_slug_idx").on(table.projectId, table.attachmentSlug), index$1("trigger_schedules_next_run_at_idx").on(table.nextRunAt, table.enabled)]);
7060
- const triggerSchedulesSqlite = sqliteTable("trigger_schedules", {
7065
+ triggerId: text$1("trigger_id").notNull().references(() => triggers.id),
7066
+ slug: text$1("slug").notNull(),
7067
+ targetKind: text$1("target_kind").$type().notNull().default("workflow"),
7068
+ workflowSlug: text$1("workflow_slug"),
7069
+ agentSlug: text$1("agent_slug"),
7070
+ prompt: text$1("prompt"),
7071
+ registeredAt: timestamp("registered_at", { withTimezone: true }).notNull(),
7072
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(),
7073
+ deletedAt: timestamp("deleted_at", { withTimezone: true })
7074
+ }, (table) => [uniqueIndex$1("trigger_attachments_project_id_slug_idx").on(table.projectId, table.slug)]);
7075
+ const triggerAttachmentsSqlite = sqliteTable("trigger_attachments", {
7061
7076
  id: text("id").primaryKey(),
7062
7077
  projectId: text("project_id").notNull(),
7063
- attachmentSlug: text("attachment_slug").notNull(),
7064
- kind: text("kind").$type().notNull(),
7065
- schedule: text("schedule").notNull(),
7066
- nextRunAt: integer("next_run_at", { mode: "timestamp_ms" }).notNull(),
7067
- enabled: integer("enabled").notNull(),
7068
- updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
7069
- }, (table) => [uniqueIndex("trigger_schedules_project_id_attachment_slug_idx").on(table.projectId, table.attachmentSlug), index("trigger_schedules_next_run_at_idx").on(table.nextRunAt, table.enabled)]);
7078
+ triggerId: text("trigger_id").notNull().references(() => triggersSqlite.id),
7079
+ slug: text("slug").notNull(),
7080
+ targetKind: text("target_kind").$type().notNull().default("workflow"),
7081
+ workflowSlug: text("workflow_slug"),
7082
+ agentSlug: text("agent_slug"),
7083
+ prompt: text("prompt"),
7084
+ registeredAt: integer("registered_at", { mode: "timestamp_ms" }).notNull(),
7085
+ updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(),
7086
+ deletedAt: integer("deleted_at", { mode: "timestamp_ms" })
7087
+ }, (table) => [uniqueIndex("trigger_attachments_project_id_slug_idx").on(table.projectId, table.slug)]);
7070
7088
  const triggerRuns = pgTable("trigger_runs", {
7071
7089
  id: text$1("id").primaryKey(),
7072
7090
  projectId: text$1("project_id").notNull(),
7073
- attachmentId: text$1("attachment_id").notNull().references(() => triggerAttachments.id),
7091
+ triggerId: text$1("trigger_id").notNull().references(() => triggers.id),
7074
7092
  sourcePath: text$1("source_path"),
7075
7093
  triggerType: text$1("trigger_type").$type().notNull(),
7094
+ outcome: text$1("outcome").$type().notNull().default("dispatched"),
7095
+ reason: text$1("reason").$type(),
7096
+ detail: text$1("detail"),
7076
7097
  payload: jsonb("payload"),
7077
7098
  triggeredAt: timestamp("triggered_at", { withTimezone: true }).notNull()
7078
- });
7099
+ }, (table) => [index$1("trigger_runs_project_trigger_triggered_idx").on(table.projectId, table.triggerId, table.triggeredAt), index$1("trigger_runs_project_triggered_idx").on(table.projectId, table.triggeredAt)]);
7079
7100
  const triggerRunsSqlite = sqliteTable("trigger_runs", {
7080
7101
  id: text("id").primaryKey(),
7081
7102
  projectId: text("project_id").notNull(),
7082
- attachmentId: text("attachment_id").notNull().references(() => triggerAttachmentsSqlite.id),
7103
+ triggerId: text("trigger_id").notNull().references(() => triggersSqlite.id),
7083
7104
  sourcePath: text("source_path"),
7084
7105
  triggerType: text("trigger_type").$type().notNull(),
7106
+ outcome: text("outcome").$type().notNull().default("dispatched"),
7107
+ reason: text("reason").$type(),
7108
+ detail: text("detail"),
7085
7109
  payload: text("payload", { mode: "json" }),
7086
7110
  triggeredAt: integer("triggered_at", { mode: "timestamp_ms" }).notNull()
7087
- });
7111
+ }, (table) => [index("trigger_runs_project_trigger_triggered_idx").on(table.projectId, table.triggerId, table.triggeredAt), index("trigger_runs_project_triggered_idx").on(table.projectId, table.triggeredAt)]);
7088
7112
  const workflowSubscriptions = pgTable("workflow_subscriptions", {
7089
7113
  id: text$1("id").primaryKey(),
7090
7114
  projectId: text$1("project_id").notNull(),
@@ -7216,7 +7240,8 @@ const credentialInstances = pgTable("credential_instances", {
7216
7240
  sharedConnectionId: text$1("shared_connection_id"),
7217
7241
  grantedScopes: jsonb("granted_scopes").$type(),
7218
7242
  createdAt: timestamp("created_at", { withTimezone: true }).notNull(),
7219
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull()
7243
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(),
7244
+ lastUsedAt: timestamp("last_used_at", { withTimezone: true })
7220
7245
  }, (table) => [uniqueIndex$1("credential_instances_project_scope_slug_unique").on(table.projectId, table.scopeType, table.scopeId, table.slug), index$1("credential_instances_project_app_slug_scope_idx").on(table.projectId, table.appSlug, table.scopeType, table.scopeId)]);
7221
7246
  const credentialInstancesSqlite = sqliteTable("credential_instances", {
7222
7247
  id: text("id").primaryKey(),
@@ -7235,7 +7260,8 @@ const credentialInstancesSqlite = sqliteTable("credential_instances", {
7235
7260
  sharedConnectionId: text("shared_connection_id"),
7236
7261
  grantedScopes: text("granted_scopes", { mode: "json" }).$type(),
7237
7262
  createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
7238
- updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
7263
+ updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(),
7264
+ lastUsedAt: integer("last_used_at", { mode: "timestamp_ms" })
7239
7265
  }, (table) => [uniqueIndex("credential_instances_project_scope_slug_unique").on(table.projectId, table.scopeType, table.scopeId, table.slug), index("credential_instances_project_app_slug_scope_idx").on(table.projectId, table.appSlug, table.scopeType, table.scopeId)]);
7240
7266
  const credentialAssignments = pgTable("credential_assignments", {
7241
7267
  id: text$1("id").primaryKey(),
@@ -7468,14 +7494,14 @@ const tableRegistry$1 = {
7468
7494
  pg: usageRecords,
7469
7495
  sqlite: usageRecordsSqlite
7470
7496
  },
7497
+ triggers: {
7498
+ pg: triggers,
7499
+ sqlite: triggersSqlite
7500
+ },
7471
7501
  triggerAttachments: {
7472
7502
  pg: triggerAttachments,
7473
7503
  sqlite: triggerAttachmentsSqlite
7474
7504
  },
7475
- triggerSchedules: {
7476
- pg: triggerSchedules,
7477
- sqlite: triggerSchedulesSqlite
7478
- },
7479
7505
  triggerRuns: {
7480
7506
  pg: triggerRuns,
7481
7507
  sqlite: triggerRunsSqlite
@@ -7665,8 +7691,7 @@ const authVerificationsSqlite = sqliteTable("verification", {
7665
7691
  createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
7666
7692
  updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
7667
7693
  }, (table) => [index("verification_identifier_idx").on(table.identifier)]);
7668
- //#endregion
7669
- //#region ../../packages/platform-database/dist/index.mjs
7694
+ createRequire(import.meta.url);
7670
7695
  function buildPgSchema(registry) {
7671
7696
  return Object.fromEntries(Object.entries(registry).map(([name, { pg }]) => [name, pg]));
7672
7697
  }
@@ -7797,6 +7822,7 @@ const apps = pgTable("apps", {
7797
7822
  authKind: text$1("auth_kind").$type().notNull(),
7798
7823
  oauthScopes: jsonb("oauth_scopes").$type().notNull(),
7799
7824
  credentialFields: jsonb("credential_fields").$type().notNull().default({}),
7825
+ credentialScheme: text$1("credential_scheme").$type(),
7800
7826
  source: text$1("source").$type().notNull(),
7801
7827
  createdAt: timestamp("created_at", { withTimezone: true }).notNull(),
7802
7828
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull()
@@ -7812,6 +7838,7 @@ const appsSqlite = sqliteTable("apps", {
7812
7838
  authKind: text("auth_kind").$type().notNull(),
7813
7839
  oauthScopes: text("oauth_scopes", { mode: "json" }).$type().notNull(),
7814
7840
  credentialFields: text("credential_fields", { mode: "json" }).$type().notNull().default({}),
7841
+ credentialScheme: text("credential_scheme").$type(),
7815
7842
  source: text("source").$type().notNull(),
7816
7843
  createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
7817
7844
  updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull()
@@ -14806,7 +14833,8 @@ const triggerSourceSchema$1 = preprocess(normalizeLegacySlugFields$1, discrimina
14806
14833
  passes: _function()
14807
14834
  })
14808
14835
  ]));
14809
- preprocess(normalizeLegacySlugFields$1, discriminatedUnion("target", [object({
14836
+ /** Runtime validation for an unbranded trigger attachment. */
14837
+ const triggerAttachmentCoreSchema$1 = preprocess(normalizeLegacySlugFields$1, discriminatedUnion("target", [object({
14810
14838
  slug: slugField$1,
14811
14839
  name: optionalTextField$1,
14812
14840
  description: optionalTextField$1,
@@ -14823,6 +14851,16 @@ preprocess(normalizeLegacySlugFields$1, discriminatedUnion("target", [object({
14823
14851
  agent: agentSchema$1,
14824
14852
  prompt: promptSchema$1
14825
14853
  })]));
14854
+ const TRIGGER_ATTACHMENT$1 = Symbol.for("keystroke.triggerAttachment");
14855
+ /**
14856
+ * Validates brand + shape via `triggerAttachmentCoreSchema` so server discovery
14857
+ * rejects unbranded or malformed attachments.
14858
+ */
14859
+ function isTriggerAttachment(value) {
14860
+ if (typeof value !== "object" || value === null) return false;
14861
+ if (!(TRIGGER_ATTACHMENT$1 in value) || value[TRIGGER_ATTACHMENT$1] !== true) return false;
14862
+ return triggerAttachmentCoreSchema$1.safeParse(value).success;
14863
+ }
14826
14864
  /** Read slug from a trigger source (`slug` preferred, legacy `key` fallback). */
14827
14865
  function sourceSlugFrom(source) {
14828
14866
  const slug = source.slug?.trim() || source.key?.trim();
@@ -14843,6 +14881,23 @@ function triggerMetaFromAttachment(attachment) {
14843
14881
  ...description !== void 0 ? { description } : {}
14844
14882
  };
14845
14883
  }
14884
+ /**
14885
+ * Hidden link from a chained `.attach(...)` result back to every attachment in the
14886
+ * same chain. Lets discovery expand a single default export into all fan-out targets.
14887
+ */
14888
+ const TRIGGER_ATTACHMENT_SIBLINGS = Symbol.for("keystroke.triggerAttachmentSiblings");
14889
+ /**
14890
+ * Flatten a trigger module's default export into its individual attachments —
14891
+ * a single `.attach(...)`, a `.attach(...).attach(...)` chain, or an array of either.
14892
+ */
14893
+ function triggerAttachmentsFromExport(value) {
14894
+ if (Array.isArray(value)) return value.flatMap(triggerAttachmentsFromExport);
14895
+ if (value && typeof value === "object") {
14896
+ const siblings = value[TRIGGER_ATTACHMENT_SIBLINGS];
14897
+ if (Array.isArray(siblings)) return siblings;
14898
+ }
14899
+ return isTriggerAttachment(value) ? [value] : [];
14900
+ }
14846
14901
  //#endregion
14847
14902
  //#region ../../packages/manifest/dist/index.mjs
14848
14903
  function isManifestAgent(value) {
@@ -15048,6 +15103,7 @@ function serializeRouteManifest(manifest) {
15048
15103
  entries.push({
15049
15104
  kind: entry.kind,
15050
15105
  attachmentId: entry.attachmentId,
15106
+ attachmentIds: entry.attachmentIds,
15051
15107
  moduleFile: entry.moduleFile,
15052
15108
  schedule: entry.schedule,
15053
15109
  ...entry.name !== void 0 ? { name: entry.name } : {},
@@ -15187,44 +15243,35 @@ async function discoverTriggerAttachments(triggersDir, options) {
15187
15243
  shouldDiscoverFile: (filePath) => shouldDiscoverTriggerFile(triggersDir, filePath)
15188
15244
  });
15189
15245
  const attachments = [];
15190
- for (const { filePath, moduleFile } of files) {
15191
- const attachment = await importTriggerAttachment(filePath, options);
15192
- const slug = attachmentSlugFromRecord(attachment);
15193
- attachments.push({
15194
- slug,
15195
- filePath,
15196
- moduleFile,
15197
- attachment
15198
- });
15199
- }
15246
+ for (const { filePath, moduleFile } of files) for (const attachment of await importTriggerAttachments(filePath, options)) attachments.push({
15247
+ slug: attachmentSlugFromRecord(attachment),
15248
+ filePath,
15249
+ moduleFile,
15250
+ attachment
15251
+ });
15200
15252
  return attachments;
15201
15253
  }
15202
15254
  function validateImportedTriggerAttachment(def, filePath) {
15203
15255
  return validateManifestTriggerAttachment(def, filePath);
15204
15256
  }
15205
- async function importTriggerAttachment(filePath, options) {
15257
+ async function loadTriggerModuleDefault(filePath, options) {
15206
15258
  const href = pathToFileURL(filePath).href;
15207
- return validateImportedTriggerAttachment((await (options?.reload ? import(`${href}?keystroke=${Date.now()}`) : import(href))).default, filePath);
15259
+ return (await (options?.reload ? import(`${href}?keystroke=${Date.now()}`) : import(href))).default;
15260
+ }
15261
+ /**
15262
+ * Import every attachment a trigger file declares — a single `.attach(...)`, a
15263
+ * `.attach(...).attach(...)` chain, or an array of either.
15264
+ */
15265
+ async function importTriggerAttachments(filePath, options) {
15266
+ const candidates = triggerAttachmentsFromExport(await loadTriggerModuleDefault(filePath, options));
15267
+ if (candidates.length === 0) return [validateImportedTriggerAttachment(void 0, filePath)];
15268
+ return candidates.map((candidate) => validateImportedTriggerAttachment(candidate, filePath));
15208
15269
  }
15209
15270
  function pollGroupId(discovered) {
15210
15271
  const source = discovered.attachment.source;
15211
15272
  if (source.kind !== "poll") throw new Error(`Attachment "${discovered.slug}" is not a poll trigger`);
15212
15273
  return source.id ?? sourceSlugFrom(source);
15213
15274
  }
15214
- function buildPollGroups(attachments) {
15215
- const byId = /* @__PURE__ */ new Map();
15216
- for (const discovered of attachments) {
15217
- if (discovered.attachment.source.kind !== "poll") continue;
15218
- const id = pollGroupId(discovered);
15219
- const group = byId.get(id) ?? [];
15220
- group.push(discovered);
15221
- byId.set(id, group);
15222
- }
15223
- return [...byId.entries()].map(([id, groupAttachments]) => ({
15224
- id,
15225
- attachments: groupAttachments
15226
- }));
15227
- }
15228
15275
  function buildWebhookBindingsByRoute(attachments, handlerOptionsFor) {
15229
15276
  const webhookBindingsByRoute = /* @__PURE__ */ new Map();
15230
15277
  for (const discovered of attachments) {
@@ -15316,6 +15363,13 @@ function resolveDistModuleDirs(projectRoot) {
15316
15363
  function toPosix(path) {
15317
15364
  return path.split(sep).join("/");
15318
15365
  }
15366
+ function hashFileContents(absPath) {
15367
+ try {
15368
+ return createHash("sha256").update(readFileSync(absPath)).digest("hex");
15369
+ } catch {
15370
+ return;
15371
+ }
15372
+ }
15319
15373
  /**
15320
15374
  * Resolve manifest moduleFile values to project-root-relative source paths.
15321
15375
  *
@@ -15350,6 +15404,23 @@ var SourceModuleFileResolver = class {
15350
15404
  if (!id) return fallback;
15351
15405
  return (await this.sourceMapFor(kindDir, nestedEntry)).get(id) ?? fallback;
15352
15406
  }
15407
+ /**
15408
+ * Like `resolve`, but also returns the sha256 of the resolved source file. The hash
15409
+ * is only present when a real `src/` file is matched (absent for dist-only artifacts),
15410
+ * and feeds the trigger overview change-detection (`triggers.triggerHash`).
15411
+ */
15412
+ async resolveSource(kindDir, nestedEntry, distDir, distFilePath) {
15413
+ const fallback = toPosix(relative(distDir, distFilePath));
15414
+ const id = entryIdFromFile(distDir, distFilePath, { nestedEntry });
15415
+ if (!id) return { moduleFile: fallback };
15416
+ const sourceRel = (await this.sourceMapFor(kindDir, nestedEntry)).get(id);
15417
+ if (!sourceRel) return { moduleFile: fallback };
15418
+ const sourceHash = hashFileContents(join(this.projectRoot, sourceRel));
15419
+ return {
15420
+ moduleFile: sourceRel,
15421
+ ...sourceHash ? { sourceHash } : {}
15422
+ };
15423
+ }
15353
15424
  };
15354
15425
  /** Build a stored route manifest from compiled dist/ modules without starting a server. */
15355
15426
  async function buildStoredRouteManifestForProject(projectRoot, options) {
@@ -15381,68 +15452,82 @@ async function buildStoredRouteManifestForProject(projectRoot, options) {
15381
15452
  }
15382
15453
  const attachments = await discoverTriggerAttachments(dirs.triggersDir, reload);
15383
15454
  const discoveredBySlug = new Map(attachments.map((attachment) => [attachment.slug, attachment]));
15384
- const pollGroups = buildPollGroups(attachments);
15385
- for (const discovered of discoveredBySlug.values()) {
15455
+ const cronByTriggerSlug = /* @__PURE__ */ new Map();
15456
+ const pollByGroupId = /* @__PURE__ */ new Map();
15457
+ for (const discovered of attachments) {
15386
15458
  const source = discovered.attachment.source;
15387
- const moduleFile = await sourcePaths.resolve("triggers", "trigger", dirs.triggersDir, discovered.filePath);
15388
15459
  if (source.kind === "cron") {
15389
- const meta = triggerMetaFromAttachment(discovered.attachment);
15390
- manifest.push({
15391
- kind: "cron-schedule",
15392
- attachmentId: discovered.slug,
15393
- moduleFile,
15394
- schedule: source.schedule,
15395
- ...meta
15396
- });
15460
+ const triggerSlug = sourceSlugFrom(source);
15461
+ const group = cronByTriggerSlug.get(triggerSlug) ?? [];
15462
+ group.push(discovered);
15463
+ cronByTriggerSlug.set(triggerSlug, group);
15397
15464
  continue;
15398
15465
  }
15399
15466
  if (source.kind === "poll") {
15400
- const meta = triggerMetaFromAttachment(discovered.attachment);
15401
- manifest.push({
15402
- kind: "trigger-poll",
15403
- attachmentId: discovered.slug,
15404
- moduleFile,
15405
- schedule: source.schedule,
15406
- ...meta,
15407
- response: PromptResponseSchema
15408
- });
15409
- continue;
15410
- }
15411
- if (source.kind === "webhook") {
15412
- const route = webhookRouteFromEndpoint(source.endpoint);
15413
- const bindings = buildWebhookBindingsByRoute(discoveredBySlug.values(), () => ({
15414
- execution: { attachmentSlug: discovered.slug },
15415
- attachmentSlug: discovered.slug
15416
- })).get(route) ?? [{
15417
- discovered,
15418
- options: { attachmentSlug: discovered.slug }
15419
- }];
15420
- const attachmentMeta = Object.fromEntries(bindings.flatMap(({ discovered: row }) => {
15421
- const meta = triggerMetaFromAttachment(row.attachment);
15422
- return Object.keys(meta).length > 0 ? [[row.slug, meta]] : [];
15423
- }));
15424
- manifest.push({
15425
- kind: "trigger-webhook",
15426
- endpoint: source.endpoint,
15427
- attachmentIds: bindings.map(({ discovered: row }) => row.slug),
15428
- moduleFile,
15429
- request: webhookMatchSchemaForBindings(bindings),
15430
- attachmentSchemas: webhookManifestAttachmentSchemasFromBindings(bindings),
15431
- ...Object.keys(attachmentMeta).length > 0 ? { attachmentMeta } : {},
15432
- response: PromptResponseSchema
15433
- });
15467
+ const groupId = pollGroupId(discovered);
15468
+ const group = pollByGroupId.get(groupId) ?? [];
15469
+ group.push(discovered);
15470
+ pollByGroupId.set(groupId, group);
15434
15471
  }
15435
15472
  }
15436
- for (const group of pollGroups) {
15437
- if (group.attachments.length <= 1) continue;
15438
- const first = group.attachments[0];
15473
+ for (const [triggerSlug, group] of cronByTriggerSlug) {
15474
+ const first = group[0];
15475
+ const source = first.attachment.source;
15476
+ if (source.kind !== "cron") continue;
15477
+ const meta = triggerMetaFromAttachment(first.attachment);
15478
+ const { moduleFile, sourceHash } = await sourcePaths.resolveSource("triggers", "trigger", dirs.triggersDir, first.filePath);
15479
+ manifest.push({
15480
+ kind: "cron-schedule",
15481
+ attachmentId: triggerSlug,
15482
+ attachmentIds: group.map((attachment) => attachment.slug),
15483
+ moduleFile,
15484
+ ...sourceHash ? { sourceHash } : {},
15485
+ schedule: source.schedule,
15486
+ ...meta
15487
+ });
15488
+ }
15489
+ for (const [groupId, group] of pollByGroupId) {
15490
+ const first = group[0];
15439
15491
  const source = first.attachment.source;
15492
+ if (source.kind !== "poll") continue;
15493
+ const meta = triggerMetaFromAttachment(first.attachment);
15494
+ const { moduleFile, sourceHash } = await sourcePaths.resolveSource("triggers", "trigger", dirs.triggersDir, first.filePath);
15440
15495
  manifest.push({
15441
- kind: "trigger-poll-group",
15442
- pollId: group.id,
15443
- attachmentIds: group.attachments.map((attachment) => attachment.slug),
15444
- moduleFile: await sourcePaths.resolve("triggers", "trigger", dirs.triggersDir, first.filePath),
15445
- schedule: source.kind === "poll" ? source.schedule : "",
15496
+ kind: "trigger-poll",
15497
+ attachmentId: groupId,
15498
+ attachmentIds: group.map((attachment) => attachment.slug),
15499
+ moduleFile,
15500
+ ...sourceHash ? { sourceHash } : {},
15501
+ schedule: source.schedule,
15502
+ ...meta,
15503
+ response: PromptResponseSchema
15504
+ });
15505
+ }
15506
+ for (const discovered of discoveredBySlug.values()) {
15507
+ const source = discovered.attachment.source;
15508
+ if (source.kind !== "webhook") continue;
15509
+ const { moduleFile, sourceHash } = await sourcePaths.resolveSource("triggers", "trigger", dirs.triggersDir, discovered.filePath);
15510
+ const route = webhookRouteFromEndpoint(source.endpoint);
15511
+ const bindings = buildWebhookBindingsByRoute(discoveredBySlug.values(), () => ({
15512
+ execution: { attachmentSlug: discovered.slug },
15513
+ attachmentSlug: discovered.slug
15514
+ })).get(route) ?? [{
15515
+ discovered,
15516
+ options: { attachmentSlug: discovered.slug }
15517
+ }];
15518
+ const attachmentMeta = Object.fromEntries(bindings.flatMap(({ discovered: row }) => {
15519
+ const meta = triggerMetaFromAttachment(row.attachment);
15520
+ return Object.keys(meta).length > 0 ? [[row.slug, meta]] : [];
15521
+ }));
15522
+ manifest.push({
15523
+ kind: "trigger-webhook",
15524
+ endpoint: source.endpoint,
15525
+ attachmentIds: bindings.map(({ discovered: row }) => row.slug),
15526
+ moduleFile,
15527
+ ...sourceHash ? { sourceHash } : {},
15528
+ request: webhookMatchSchemaForBindings(bindings),
15529
+ attachmentSchemas: webhookManifestAttachmentSchemasFromBindings(bindings),
15530
+ ...Object.keys(attachmentMeta).length > 0 ? { attachmentMeta } : {},
15446
15531
  response: PromptResponseSchema
15447
15532
  });
15448
15533
  }
@@ -15462,6 +15547,6 @@ async function emitStoredRouteManifestForProject(projectRoot) {
15462
15547
  persistStoredRouteManifest(projectRoot, await buildStoredRouteManifestForProject(projectRoot));
15463
15548
  }
15464
15549
  //#endregion
15465
- export { packAssetDirs as A, toStoredRouteManifest as C, webhookMatchSchemaForBindings as D, webhookManifestAttachmentSchemasFromBindings as E, shouldSkipKeystrokeModuleFile as F, walkTypeScriptFiles as I, discoverModuleFileEntries as M, entryIdFromFile as N, webhookRouteFromEndpoint as O, readKeystrokeIgnoreDirective as P, serializeRouteManifest as S, validateImportedWorkflowDefinition as T, importTriggerAttachment as _, buildStoredRouteManifestFromContext as a, pollGroupId as b, collectAgentToolSlugs as c, discoverSkillManifestEntries as d, discoverTriggerAttachments as f, importAgentDefinition as g, emitStoredRouteManifestForProject as h, buildStoredRouteManifestForProject as i, discoverEntries as j, workflowRouteFromKey as k, countAgentCredentials as l, discoverWorkflows as m, agentRouteFromKey as n, buildWebhookBindingsByRoute as o, discoverWorkflowEntries as p, buildPollGroups as r, collectAgentAppSlugs as s, agentManifestEntry as t, discoverAgentEntries as u, importWorkflowDefinition as v, validateImportedTriggerAttachment as w, schemaToJson as x, persistStoredRouteManifest as y };
15550
+ export { discoverEntries as A, validateImportedTriggerAttachment as C, webhookRouteFromEndpoint as D, webhookMatchSchemaForBindings as E, walkTypeScriptFiles as F, entryIdFromFile as M, readKeystrokeIgnoreDirective as N, workflowRouteFromKey as O, shouldSkipKeystrokeModuleFile as P, toStoredRouteManifest as S, webhookManifestAttachmentSchemasFromBindings as T, importWorkflowDefinition as _, buildWebhookBindingsByRoute as a, schemaToJson as b, countAgentCredentials as c, discoverTriggerAttachments as d, discoverWorkflowEntries as f, importTriggerAttachments as g, importAgentDefinition as h, buildStoredRouteManifestFromContext as i, discoverModuleFileEntries as j, packAssetDirs as k, discoverAgentEntries as l, emitStoredRouteManifestForProject as m, agentRouteFromKey as n, collectAgentAppSlugs as o, discoverWorkflows as p, buildStoredRouteManifestForProject as r, collectAgentToolSlugs as s, agentManifestEntry as t, discoverSkillManifestEntries as u, persistStoredRouteManifest as v, validateImportedWorkflowDefinition as w, serializeRouteManifest as x, pollGroupId as y };
15466
15551
 
15467
- //# sourceMappingURL=dist-BIENrk9m.mjs.map
15552
+ //# sourceMappingURL=dist-6-Bdz4wY.mjs.map