@ainyc/canonry 4.15.2 → 4.17.1

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.
package/assets/index.html CHANGED
@@ -12,8 +12,8 @@
12
12
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
13
13
  <link rel="apple-touch-icon" href="./apple-touch-icon.png" />
14
14
  <title>Canonry</title>
15
- <script type="module" crossorigin src="./assets/index-Qq_oMI-C.js"></script>
16
- <link rel="stylesheet" crossorigin href="./assets/index-C1WW21tz.css">
15
+ <script type="module" crossorigin src="./assets/index-C5-Gvl6o.js"></script>
16
+ <link rel="stylesheet" crossorigin href="./assets/index-4fWsYFLp.css">
17
17
  </head>
18
18
  <body>
19
19
  <div id="root"></div>
@@ -11,10 +11,11 @@ import {
11
11
  queryBatchRequestSchema,
12
12
  queryGenerateRequestSchema,
13
13
  runTriggerRequestSchema,
14
+ schedulableRunKindSchema,
14
15
  scheduleUpsertRequestSchema,
15
16
  trafficConnectCloudRunRequestSchema,
16
17
  trafficEventKindSchema
17
- } from "./chunk-ONI3TX2A.js";
18
+ } from "./chunk-Q2OED5JQ.js";
18
19
 
19
20
  // src/config.ts
20
21
  import fs from "fs";
@@ -547,11 +548,13 @@ var ApiClient = class {
547
548
  async putSchedule(project, body) {
548
549
  return this.request("PUT", `/projects/${encodeURIComponent(project)}/schedule`, body);
549
550
  }
550
- async getSchedule(project) {
551
- return this.request("GET", `/projects/${encodeURIComponent(project)}/schedule`);
551
+ async getSchedule(project, kind) {
552
+ const qs = kind ? `?kind=${encodeURIComponent(kind)}` : "";
553
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/schedule${qs}`);
552
554
  }
553
- async deleteSchedule(project) {
554
- await this.request("DELETE", `/projects/${encodeURIComponent(project)}/schedule`);
555
+ async deleteSchedule(project, kind) {
556
+ const qs = kind ? `?kind=${encodeURIComponent(kind)}` : "";
557
+ await this.request("DELETE", `/projects/${encodeURIComponent(project)}/schedule${qs}`);
555
558
  }
556
559
  async createNotification(project, body) {
557
560
  return this.request("POST", `/projects/${encodeURIComponent(project)}/notifications`, body);
@@ -1147,6 +1150,10 @@ var scheduleSetInputSchema = z2.object({
1147
1150
  project: projectNameSchema,
1148
1151
  schedule: scheduleUpsertRequestSchema
1149
1152
  });
1153
+ var scheduleReadInputSchema = z2.object({
1154
+ project: projectNameSchema,
1155
+ kind: schedulableRunKindSchema.optional().describe('Schedulable run kind. Defaults to "answer-visibility" if omitted.')
1156
+ });
1150
1157
  var agentWebhookAttachInputSchema = z2.object({
1151
1158
  project: projectNameSchema,
1152
1159
  url: z2.string().url()
@@ -1498,13 +1505,13 @@ var canonryMcpTools = [
1498
1505
  defineTool({
1499
1506
  name: "canonry_schedule_get",
1500
1507
  title: "Get schedule",
1501
- description: "Get the scheduled run configuration for a Canonry project.",
1508
+ description: 'Get the scheduled run configuration for a Canonry project. Pass `kind` to read a non-default schedule (e.g. "traffic-sync"); defaults to "answer-visibility".',
1502
1509
  access: "read",
1503
1510
  tier: "setup",
1504
- inputSchema: projectInputSchema,
1511
+ inputSchema: scheduleReadInputSchema,
1505
1512
  annotations: readAnnotations(),
1506
1513
  openApiOperations: ["GET /api/v1/projects/{name}/schedule"],
1507
- handler: (client, input) => client.getSchedule(input.project)
1514
+ handler: (client, input) => client.getSchedule(input.project, input.kind)
1508
1515
  }),
1509
1516
  defineTool({
1510
1517
  name: "canonry_backlinks_latest_release",
@@ -1966,14 +1973,14 @@ var canonryMcpTools = [
1966
1973
  defineTool({
1967
1974
  name: "canonry_schedule_delete",
1968
1975
  title: "Delete schedule",
1969
- description: "Delete the scheduled run configuration for a Canonry project.",
1976
+ description: 'Delete the scheduled run configuration for a Canonry project. Pass `kind` to delete a non-default schedule (e.g. "traffic-sync"); defaults to "answer-visibility".',
1970
1977
  access: "write",
1971
1978
  tier: "setup",
1972
- inputSchema: projectInputSchema,
1979
+ inputSchema: scheduleReadInputSchema,
1973
1980
  annotations: writeAnnotations({ idempotentHint: false, destructiveHint: true }),
1974
1981
  openApiOperations: ["DELETE /api/v1/projects/{name}/schedule"],
1975
1982
  handler: async (client, input) => {
1976
- await client.deleteSchedule(input.project);
1983
+ await client.deleteSchedule(input.project, input.kind);
1977
1984
  }
1978
1985
  }),
1979
1986
  defineTool({
@@ -8,7 +8,7 @@ import {
8
8
  categoryLabel,
9
9
  determineAnswerMentioned,
10
10
  normalizeProjectDomain
11
- } from "./chunk-ONI3TX2A.js";
11
+ } from "./chunk-Q2OED5JQ.js";
12
12
 
13
13
  // src/intelligence-service.ts
14
14
  import { eq, desc, asc, and, or, inArray } from "drizzle-orm";
@@ -166,17 +166,24 @@ var apiKeys = sqliteTable("api_keys", {
166
166
  var schedules = sqliteTable("schedules", {
167
167
  id: text("id").primaryKey(),
168
168
  projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
169
+ // Run kind dispatched by this schedule. Must be a value of `RunKinds` —
170
+ // currently 'answer-visibility' and 'traffic-sync' are user-facing schedulable kinds.
171
+ // Defaults to 'answer-visibility' for backward compatibility with rows
172
+ // created before migration 53.
173
+ kind: text("kind").notNull().default("answer-visibility"),
169
174
  cronExpr: text("cron_expr").notNull(),
170
175
  preset: text("preset"),
171
176
  timezone: text("timezone").notNull().default("UTC"),
172
177
  enabled: integer("enabled").notNull().default(1),
173
178
  providers: text("providers").notNull().default("[]"),
179
+ /** Optional traffic-source UUID for traffic-sync schedules. Null for other kinds. */
180
+ sourceId: text("source_id"),
174
181
  lastRunAt: text("last_run_at"),
175
182
  nextRunAt: text("next_run_at"),
176
183
  createdAt: text("created_at").notNull(),
177
184
  updatedAt: text("updated_at").notNull()
178
185
  }, (table) => [
179
- uniqueIndex("idx_schedules_project").on(table.projectId)
186
+ uniqueIndex("idx_schedules_project_kind").on(table.projectId, table.kind)
180
187
  ]);
181
188
  var notifications = sqliteTable("notifications", {
182
189
  id: text("id").primaryKey(),
@@ -800,7 +807,10 @@ CREATE TABLE IF NOT EXISTS notifications (
800
807
 
801
808
  CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(key_prefix);
802
809
  CREATE INDEX IF NOT EXISTS idx_usage_scope_period ON usage_counters(scope, period);
803
- CREATE UNIQUE INDEX IF NOT EXISTS idx_schedules_project ON schedules(project_id);
810
+ -- NOTE: the (project_id) UNIQUE INDEX that used to live here was replaced by
811
+ -- v53's (project_id, kind) index. MIGRATION_SQL re-runs on every boot, so we
812
+ -- must NOT recreate the single-column index \u2014 it would conflict with v53 and
813
+ -- break traffic-sync schedule creation.
804
814
  CREATE INDEX IF NOT EXISTS idx_notifications_project ON notifications(project_id);
805
815
 
806
816
  -- Migration tracking: records which version has been applied.
@@ -1611,6 +1621,66 @@ var MIGRATION_VERSIONS = [
1611
1621
  // sync window (or any overlapping re-sync) cannot double-count.
1612
1622
  `ALTER TABLE traffic_sources ADD COLUMN last_event_ids TEXT`
1613
1623
  ]
1624
+ },
1625
+ {
1626
+ version: 53,
1627
+ name: "schedules-kind-and-source",
1628
+ // The legacy schedules table carries an inline `UNIQUE(project_id)`
1629
+ // constraint (see MIGRATION_SQL). SQLite doesn't support dropping inline
1630
+ // table constraints, so we use the canonical table-rebuild pattern:
1631
+ // create a new table with the desired schema, copy the data, drop the
1632
+ // old, rename. All 4 statements run inside the migration runner's
1633
+ // single transaction so a partial failure rolls everything back.
1634
+ statements: [
1635
+ // (project_id, kind) uniqueness is enforced by the explicit
1636
+ // `CREATE UNIQUE INDEX idx_schedules_project_kind` below — that's the
1637
+ // canonical drizzle-side index name (see schema.ts), so don't duplicate
1638
+ // it as an inline UNIQUE() in CREATE TABLE.
1639
+ `CREATE TABLE IF NOT EXISTS schedules_v53 (
1640
+ id TEXT PRIMARY KEY,
1641
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
1642
+ kind TEXT NOT NULL DEFAULT 'answer-visibility',
1643
+ cron_expr TEXT NOT NULL,
1644
+ preset TEXT,
1645
+ timezone TEXT NOT NULL DEFAULT 'UTC',
1646
+ enabled INTEGER NOT NULL DEFAULT 1,
1647
+ providers TEXT NOT NULL DEFAULT '[]',
1648
+ source_id TEXT,
1649
+ last_run_at TEXT,
1650
+ next_run_at TEXT,
1651
+ created_at TEXT NOT NULL,
1652
+ updated_at TEXT NOT NULL
1653
+ )`,
1654
+ `INSERT INTO schedules_v53 (
1655
+ id, project_id, kind, cron_expr, preset, timezone, enabled,
1656
+ providers, source_id, last_run_at, next_run_at, created_at, updated_at
1657
+ )
1658
+ SELECT id, project_id, 'answer-visibility', cron_expr, preset, timezone, enabled,
1659
+ providers, NULL, last_run_at, next_run_at, created_at, updated_at
1660
+ FROM schedules`,
1661
+ `DROP TABLE schedules`,
1662
+ `ALTER TABLE schedules_v53 RENAME TO schedules`,
1663
+ // The legacy single-column unique index doesn't survive the table
1664
+ // rename, but explicitly DROP IF EXISTS to keep the migration
1665
+ // idempotent across edge-case re-runs.
1666
+ `DROP INDEX IF EXISTS idx_schedules_project`,
1667
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_schedules_project_kind ON schedules(project_id, kind)`
1668
+ ]
1669
+ },
1670
+ {
1671
+ version: 54,
1672
+ name: "drop-resurrected-schedules-project-index",
1673
+ // v53 dropped `idx_schedules_project`, but `MIGRATION_SQL` (which runs on
1674
+ // every boot, before versioned migrations) was still creating it. On any
1675
+ // boot AFTER the one that applied v53, Phase 1 re-created the legacy
1676
+ // single-column UNIQUE index, which then collided with the new
1677
+ // (project_id, kind) semantics and broke traffic-sync schedule creation
1678
+ // (`UNIQUE constraint failed: schedules.project_id`). MIGRATION_SQL no
1679
+ // longer creates that index; this migration removes it from any DB that
1680
+ // already booted past v53 with the resurrected index.
1681
+ statements: [
1682
+ `DROP INDEX IF EXISTS idx_schedules_project`
1683
+ ]
1614
1684
  }
1615
1685
  ];
1616
1686
  function isDuplicateColumnError(err) {
@@ -1022,25 +1022,35 @@ var snapshotReportSchema = z9.object({
1022
1022
 
1023
1023
  // ../contracts/src/schedule.ts
1024
1024
  import { z as z10 } from "zod";
1025
+ var schedulableRunKindSchema = z10.enum(["answer-visibility", "traffic-sync"]);
1026
+ var SchedulableRunKinds = schedulableRunKindSchema.enum;
1025
1027
  var scheduleDtoSchema = z10.object({
1026
1028
  id: z10.string(),
1027
1029
  projectId: z10.string(),
1030
+ /** Run kind dispatched when this schedule fires. Defaults to 'answer-visibility' for legacy rows. */
1031
+ kind: schedulableRunKindSchema,
1028
1032
  cronExpr: z10.string(),
1029
1033
  preset: z10.string().nullable().optional(),
1030
1034
  timezone: z10.string().default("UTC"),
1031
1035
  enabled: z10.boolean().default(true),
1032
1036
  providers: z10.array(providerNameSchema).default([]),
1037
+ /** Traffic-source UUID for `kind === 'traffic-sync'` schedules. Null otherwise. */
1038
+ sourceId: z10.string().nullable().optional(),
1033
1039
  lastRunAt: z10.string().nullable().optional(),
1034
1040
  nextRunAt: z10.string().nullable().optional(),
1035
1041
  createdAt: z10.string(),
1036
1042
  updatedAt: z10.string()
1037
1043
  });
1038
1044
  var scheduleUpsertRequestSchema = z10.object({
1045
+ /** Run kind. Defaults to 'answer-visibility' so existing callers don't have to change. */
1046
+ kind: schedulableRunKindSchema.optional(),
1039
1047
  preset: z10.string().optional(),
1040
1048
  cron: z10.string().optional(),
1041
1049
  timezone: z10.string().optional().default("UTC"),
1042
1050
  enabled: z10.boolean().optional().default(true),
1043
- providers: z10.array(providerNameSchema).optional().default([])
1051
+ providers: z10.array(providerNameSchema).optional().default([]),
1052
+ /** Required when kind === 'traffic-sync'. Forbidden for other kinds. Validated server-side. */
1053
+ sourceId: z10.string().optional()
1044
1054
  }).refine(
1045
1055
  (data) => data.preset && !data.cron || !data.preset && data.cron,
1046
1056
  { message: 'Exactly one of "preset" or "cron" must be provided' }
@@ -2406,6 +2416,8 @@ export {
2406
2416
  formatRunErrorOneLine,
2407
2417
  snapshotRequestSchema,
2408
2418
  resolveSnapshotRequestQueries,
2419
+ schedulableRunKindSchema,
2420
+ SchedulableRunKinds,
2409
2421
  scheduleUpsertRequestSchema,
2410
2422
  parseWindow,
2411
2423
  windowCutoff,