@mulmoclaude/core 0.2.9 → 0.2.11

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 (113) hide show
  1. package/dist/collection/core/actionVisible.d.ts +34 -0
  2. package/dist/collection/core/calendarGrid.d.ts +120 -0
  3. package/dist/collection/core/deriveAll.d.ts +38 -0
  4. package/dist/collection/core/derivedFormula.d.ts +18 -0
  5. package/dist/collection/core/draft.d.ts +18 -0
  6. package/dist/collection/core/enumColors.d.ts +33 -0
  7. package/dist/collection/core/errorMessage.d.ts +4 -0
  8. package/dist/collection/core/itemLabel.d.ts +12 -0
  9. package/dist/collection/core/presentCollection.d.ts +13 -0
  10. package/dist/collection/core/promptSafety.d.ts +1 -0
  11. package/dist/collection/core/schema.d.ts +403 -0
  12. package/dist/collection/core/shortHexId.d.ts +8 -0
  13. package/dist/collection/core/sortItems.d.ts +29 -0
  14. package/dist/collection/core/uiTypes.d.ts +106 -0
  15. package/dist/collection/index.cjs +794 -0
  16. package/dist/collection/index.cjs.map +1 -0
  17. package/dist/collection/index.d.ts +14 -0
  18. package/dist/collection/paths.cjs +56 -0
  19. package/dist/collection/paths.cjs.map +1 -0
  20. package/dist/collection/paths.js +12 -1
  21. package/dist/collection/paths.js.map +1 -1
  22. package/dist/collection/server/atomic.d.ts +1 -0
  23. package/dist/collection/server/delete.d.ts +38 -0
  24. package/dist/collection/server/derive.d.ts +8 -0
  25. package/dist/collection/server/discoveredCollection.d.ts +18 -0
  26. package/dist/collection/server/discovery.d.ts +240 -0
  27. package/dist/collection/server/host.d.ts +77 -0
  28. package/dist/collection/server/index.cjs +53 -0
  29. package/dist/collection/server/index.js +3 -3
  30. package/dist/collection/server/io.d.ts +147 -0
  31. package/dist/collection/server/paths.d.ts +52 -0
  32. package/dist/collection/server/spawn.d.ts +55 -0
  33. package/dist/collection/server/templatePath.d.ts +9 -0
  34. package/dist/collection/server/util.d.ts +3 -0
  35. package/dist/collection/server/validate.d.ts +19 -0
  36. package/dist/collection/server/views.d.ts +20 -0
  37. package/dist/collection-watchers/config.d.ts +49 -0
  38. package/dist/collection-watchers/index.cjs +553 -0
  39. package/dist/collection-watchers/index.cjs.map +1 -0
  40. package/dist/collection-watchers/index.d.ts +3 -0
  41. package/dist/collection-watchers/index.js +1 -1
  42. package/dist/collection-watchers/reconciler.d.ts +33 -0
  43. package/dist/collection-watchers/watcher.d.ts +34 -0
  44. package/dist/deriveAll-VRWrs3SF.cjs +413 -0
  45. package/dist/deriveAll-VRWrs3SF.cjs.map +1 -0
  46. package/dist/deriveAll-vzIhhKBK.js.map +1 -1
  47. package/dist/feeds/index.cjs +8 -0
  48. package/dist/feeds/index.d.ts +1 -0
  49. package/dist/feeds/ingestTypes.d.ts +57 -0
  50. package/dist/feeds/paths.cjs +48 -0
  51. package/dist/feeds/paths.cjs.map +1 -0
  52. package/dist/feeds/paths.d.ts +22 -0
  53. package/dist/feeds/server/fetch/httpClient.d.ts +8 -0
  54. package/dist/feeds/server/fetch/rssParser.d.ts +12 -0
  55. package/dist/feeds/server/index.cjs +839 -0
  56. package/dist/feeds/server/index.cjs.map +1 -0
  57. package/dist/feeds/server/index.js +5 -6
  58. package/dist/feeds/server/index.js.map +1 -1
  59. package/dist/feeds/server/pathResolver.d.ts +6 -0
  60. package/dist/feeds/server/projectItem.d.ts +8 -0
  61. package/dist/feeds/server/refreshResult.d.ts +17 -0
  62. package/dist/feeds/server/retrievers/httpJson.d.ts +3 -0
  63. package/dist/feeds/server/retrievers/index.d.ts +12 -0
  64. package/dist/feeds/server/retrievers/registerAll.d.ts +0 -0
  65. package/dist/feeds/server/retrievers/rss.d.ts +3 -0
  66. package/dist/feeds/server/scheduledRefresh.d.ts +1 -1
  67. package/dist/file-change/index.cjs +72 -0
  68. package/dist/file-change/index.cjs.map +1 -0
  69. package/dist/file-change/index.d.ts +43 -0
  70. package/dist/ingestTypes-DDOVhOYC.cjs +32 -0
  71. package/dist/ingestTypes-DDOVhOYC.cjs.map +1 -0
  72. package/dist/notifier/engine.d.ts +72 -0
  73. package/dist/notifier/index.cjs +21 -0
  74. package/dist/notifier/index.d.ts +3 -0
  75. package/dist/notifier/store.d.ts +18 -0
  76. package/dist/notifier/types.d.ts +118 -0
  77. package/dist/notifier/validate.d.ts +17 -0
  78. package/dist/notifier-bS8IEeLA.cjs +577 -0
  79. package/dist/notifier-bS8IEeLA.cjs.map +1 -0
  80. package/dist/rolldown-runtime-D6vf50IK.cjs +28 -0
  81. package/dist/scheduler/adapter.d.ts +48 -0
  82. package/dist/scheduler/index.cjs +352 -0
  83. package/dist/scheduler/index.cjs.map +1 -0
  84. package/dist/scheduler/index.d.ts +2 -0
  85. package/dist/scheduler/task-manager.d.ts +51 -0
  86. package/dist/{server-q4WPXtbU.js → server-D2QZuMS2.js} +54 -4
  87. package/dist/server-D2QZuMS2.js.map +1 -0
  88. package/dist/server-DAyIQ5rT.cjs +2049 -0
  89. package/dist/server-DAyIQ5rT.cjs.map +1 -0
  90. package/dist/skill-bridge/index.cjs +88 -0
  91. package/dist/skill-bridge/index.cjs.map +1 -0
  92. package/dist/skill-bridge/index.d.ts +30 -0
  93. package/dist/whisper/client.cjs +241 -0
  94. package/dist/whisper/client.cjs.map +1 -0
  95. package/dist/whisper/client.d.ts +35 -0
  96. package/dist/whisper/ffmpeg.d.ts +6 -0
  97. package/dist/whisper/index.cjs +433 -0
  98. package/dist/whisper/index.cjs.map +1 -0
  99. package/dist/whisper/index.d.ts +5 -0
  100. package/dist/whisper/internal.d.ts +11 -0
  101. package/dist/whisper/models.d.ts +49 -0
  102. package/dist/whisper/sidecar.d.ts +8 -0
  103. package/dist/whisper/whisper.d.ts +28 -0
  104. package/dist/workspace-setup/assets.d.ts +10 -0
  105. package/dist/workspace-setup/index.d.ts +3 -0
  106. package/dist/workspace-setup/index.js +560 -0
  107. package/dist/workspace-setup/index.js.map +1 -0
  108. package/dist/workspace-setup/slug.d.ts +6 -0
  109. package/dist/workspace-setup/slug.js +13 -0
  110. package/dist/workspace-setup/slug.js.map +1 -0
  111. package/dist/workspace-setup/sync.d.ts +94 -0
  112. package/package.json +1 -1
  113. package/dist/server-q4WPXtbU.js.map +0 -1
@@ -0,0 +1,3 @@
1
+ import { RetrieveFn } from './index.js';
2
+ declare const retrieveHttpJson: RetrieveFn;
3
+ export { retrieveHttpJson };
@@ -0,0 +1,12 @@
1
+ import { CollectionItem, CollectionSchema } from '../../../collection/index.js';
2
+ import { DeclarativeIngestSpec } from '../../ingestTypes.js';
3
+ import { FeedState } from '../state.js';
4
+ export interface RetrieveResult {
5
+ /** Projected records, keyed by primaryKey (the engine upserts them). */
6
+ items: CollectionItem[];
7
+ /** Updated retriever cursor to persist (incremental fetches). */
8
+ cursor: Record<string, string>;
9
+ }
10
+ export type RetrieveFn = (ingest: DeclarativeIngestSpec, schema: CollectionSchema, state: FeedState) => Promise<RetrieveResult>;
11
+ export declare function registerRetriever(kind: string, retriever: RetrieveFn): void;
12
+ export declare function getRetriever(kind: string): RetrieveFn | undefined;
File without changes
@@ -0,0 +1,3 @@
1
+ import { RetrieveFn } from './index.js';
2
+ declare const retrieveRss: RetrieveFn;
3
+ export { retrieveRss };
@@ -1,6 +1,6 @@
1
1
  import { SystemTaskDef } from '../../scheduler/adapter.js';
2
2
  export declare const FEED_REFRESH_TASK_ID = "system:feed-refresh";
3
- export declare const DEFAULT_FEED_REFRESH_INTERVAL_MS = 3600000;
3
+ export declare const DEFAULT_FEED_REFRESH_INTERVAL_MS: number;
4
4
  /**
5
5
  * The hourly "refresh due collections" task, shared by every host.
6
6
  *
@@ -0,0 +1,72 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_rolldown_runtime = require("../rolldown-runtime-D6vf50IK.cjs");
3
+ let node_path = require("node:path");
4
+ node_path = require_rolldown_runtime.__toESM(node_path, 1);
5
+ let node_fs_promises = require("node:fs/promises");
6
+ //#region src/file-change/index.ts
7
+ var config = null;
8
+ /** Wire the publisher to a host. Call once at startup, before any write route. */
9
+ function configureFileChangePublisher(cfg) {
10
+ config = cfg;
11
+ }
12
+ /** Clear the binding — test-only. */
13
+ function resetFileChangePublisher() {
14
+ config = null;
15
+ }
16
+ /** The plugin-scoped live-refresh channel a View subscribes to. */
17
+ function pluginFileChannel(scope, posixPath) {
18
+ return `plugin:${scope}:file:${posixPath}`;
19
+ }
20
+ /** Publish a file-change for a workspace-relative path: the primary channel (if any)
21
+ * + every matching plugin scope, with the post-write mtime. No-op until configured. */
22
+ async function publishFileChange(relativePath) {
23
+ const cfg = config;
24
+ if (!cfg) return;
25
+ const root = node_path.default.resolve(cfg.workspaceRoot) + node_path.default.sep;
26
+ const absPath = node_path.default.join(root, relativePath);
27
+ if (!absPath.startsWith(root)) {
28
+ cfg.warn?.("ignoring file-change for path outside workspace", { path: relativePath });
29
+ return;
30
+ }
31
+ let mtimeMs;
32
+ try {
33
+ ({mtimeMs} = await (0, node_fs_promises.stat)(absPath));
34
+ } catch (err) {
35
+ cfg.warn?.("stat failed; falling back to Date.now()", {
36
+ path: relativePath,
37
+ error: errMsg(err)
38
+ });
39
+ mtimeMs = Date.now();
40
+ }
41
+ const posixPath = cfg.toPosix(relativePath);
42
+ const payload = {
43
+ path: posixPath,
44
+ mtimeMs
45
+ };
46
+ if (cfg.primaryChannel) safePublish(cfg, cfg.primaryChannel(posixPath), payload, "primary publish failed");
47
+ for (const { scope, matches } of cfg.pluginScopes ?? []) {
48
+ if (!matches(posixPath)) continue;
49
+ safePublish(cfg, pluginFileChannel(scope, posixPath), payload, `${scope} plugin forward failed`);
50
+ }
51
+ cfg.onPublished?.(posixPath, payload);
52
+ }
53
+ function safePublish(cfg, channel, payload, failMsg) {
54
+ try {
55
+ cfg.publish(channel, payload);
56
+ } catch (err) {
57
+ cfg.warn?.(`${failMsg}; subscribers will miss this event`, {
58
+ channel,
59
+ error: errMsg(err)
60
+ });
61
+ }
62
+ }
63
+ function errMsg(err) {
64
+ return err instanceof Error ? err.message : String(err);
65
+ }
66
+ //#endregion
67
+ exports.configureFileChangePublisher = configureFileChangePublisher;
68
+ exports.pluginFileChannel = pluginFileChannel;
69
+ exports.publishFileChange = publishFileChange;
70
+ exports.resetFileChangePublisher = resetFileChangePublisher;
71
+
72
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../src/file-change/index.ts"],"sourcesContent":["// @mulmoclaude/core/file-change — shared \"this workspace file changed\"\n// broadcaster. A host write route calls `publishFileChange(relPath)` after a\n// successful write; subscribed UI tabs refetch. Extracted so MulmoClaude and\n// MulmoTerminal forward to the SAME plugin-scoped channels (the live-refresh\n// contract the markdown/html Views subscribe to) without duplicating the logic.\n//\n// Everything host-specific is INJECTED via `configureFileChangePublisher` (the\n// pubsub, the workspace root, the path→posix normaliser, the host's own primary\n// channel, the plugin-scope matchers, and any side-effect), so this package owns\n// only the orchestration + the `plugin:<scope>:file:<path>` channel format and\n// touches neither host's pubsub-channel config nor its frontend subscribers.\nimport { stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/** Payload published on every file-change channel. `mtimeMs` is the post-write\n * modified time — monotonic, suitable for cache-busting (`?v=`) and out-of-order\n * drops. */\nexport interface FileChannelPayload {\n path: string;\n mtimeMs: number;\n}\n\n/** A plugin View that wants live-refresh: when `matches(posixPath)` is true, the\n * change is forwarded to `plugin:<scope>:file:<path>` (the channel the View's\n * runtime subscribes to). */\nexport interface FileChangeScope {\n scope: string;\n matches: (posixPath: string) => boolean;\n}\n\nexport interface FileChangePublisherConfig {\n /** Host pubsub publish. */\n publish: (channel: string, payload: FileChannelPayload) => void;\n /** Workspace root — joined with the relative path to stat the post-write mtime. */\n workspaceRoot: string;\n /** Normalise a workspace-relative path to POSIX (drives both `payload.path` and\n * every channel suffix, so they can't drift on mixed separators). */\n toPosix: (relativePath: string) => string;\n /** The host's primary file channel (e.g. a Files explorer's `file:<path>`).\n * Omit when the host has no general file subscriber (e.g. MulmoTerminal). */\n primaryChannel?: (posixPath: string) => string;\n /** Plugin Views to forward to. */\n pluginScopes?: FileChangeScope[];\n /** Optional side-effect after publishing (e.g. a topic-index regen). Receives the\n * posix path + payload; may itself call `publishFileChange` for derived files. */\n onPublished?: (posixPath: string, payload: FileChannelPayload) => void;\n /** Optional warn logger; a publish/stat failure logs but never throws (callers\n * fire-and-forget, so a throw would be an unhandled rejection). */\n warn?: (message: string, data?: Record<string, unknown>) => void;\n}\n\nlet config: FileChangePublisherConfig | null = null;\n\n/** Wire the publisher to a host. Call once at startup, before any write route. */\nexport function configureFileChangePublisher(cfg: FileChangePublisherConfig): void {\n config = cfg;\n}\n\n/** Clear the binding — test-only. */\nexport function resetFileChangePublisher(): void {\n config = null;\n}\n\n/** The plugin-scoped live-refresh channel a View subscribes to. */\nexport function pluginFileChannel(scope: string, posixPath: string): string {\n return `plugin:${scope}:file:${posixPath}`;\n}\n\n/** Publish a file-change for a workspace-relative path: the primary channel (if any)\n * + every matching plugin scope, with the post-write mtime. No-op until configured. */\nexport async function publishFileChange(relativePath: string): Promise<void> {\n const cfg = config;\n if (!cfg) return;\n // `relativePath` comes from the host's write routes. Contain it before doing\n // anything: a path that escapes the workspace is dropped entirely — we\n // neither stat an arbitrary file nor broadcast an out-of-workspace path to\n // subscribers or host side-effects. Defence-in-depth, since this is the\n // shared package both hosts use. `root` carries a trailing separator and the\n // guard is a single `startsWith` early-return — the canonical containment\n // shape (so the check is unambiguous for both readers and static analysis).\n const root = path.resolve(cfg.workspaceRoot) + path.sep;\n const absPath = path.join(root, relativePath);\n if (!absPath.startsWith(root)) {\n cfg.warn?.(\"ignoring file-change for path outside workspace\", { path: relativePath });\n return;\n }\n let mtimeMs: number;\n try {\n ({ mtimeMs } = await stat(absPath));\n } catch (err) {\n cfg.warn?.(\"stat failed; falling back to Date.now()\", { path: relativePath, error: errMsg(err) });\n mtimeMs = Date.now();\n }\n const posixPath = cfg.toPosix(relativePath);\n const payload: FileChannelPayload = { path: posixPath, mtimeMs };\n\n if (cfg.primaryChannel) {\n safePublish(cfg, cfg.primaryChannel(posixPath), payload, \"primary publish failed\");\n }\n for (const { scope, matches } of cfg.pluginScopes ?? []) {\n if (!matches(posixPath)) continue;\n safePublish(cfg, pluginFileChannel(scope, posixPath), payload, `${scope} plugin forward failed`);\n }\n cfg.onPublished?.(posixPath, payload);\n}\n\nfunction safePublish(cfg: FileChangePublisherConfig, channel: string, payload: FileChannelPayload, failMsg: string): void {\n try {\n cfg.publish(channel, payload);\n } catch (err) {\n cfg.warn?.(`${failMsg}; subscribers will miss this event`, { channel, error: errMsg(err) });\n }\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n"],"mappings":";;;;;;AAmDA,IAAI,SAA2C;;AAG/C,SAAgB,6BAA6B,KAAsC;CACjF,SAAS;AACX;;AAGA,SAAgB,2BAAiC;CAC/C,SAAS;AACX;;AAGA,SAAgB,kBAAkB,OAAe,WAA2B;CAC1E,OAAO,UAAU,MAAM,QAAQ;AACjC;;;AAIA,eAAsB,kBAAkB,cAAqC;CAC3E,MAAM,MAAM;CACZ,IAAI,CAAC,KAAK;CAQV,MAAM,OAAO,UAAA,QAAK,QAAQ,IAAI,aAAa,IAAI,UAAA,QAAK;CACpD,MAAM,UAAU,UAAA,QAAK,KAAK,MAAM,YAAY;CAC5C,IAAI,CAAC,QAAQ,WAAW,IAAI,GAAG;EAC7B,IAAI,OAAO,mDAAmD,EAAE,MAAM,aAAa,CAAC;EACpF;CACF;CACA,IAAI;CACJ,IAAI;EACF,CAAC,CAAE,WAAY,OAAA,GAAA,iBAAA,KAAA,CAAW,OAAO;CACnC,SAAS,KAAK;EACZ,IAAI,OAAO,2CAA2C;GAAE,MAAM;GAAc,OAAO,OAAO,GAAG;EAAE,CAAC;EAChG,UAAU,KAAK,IAAI;CACrB;CACA,MAAM,YAAY,IAAI,QAAQ,YAAY;CAC1C,MAAM,UAA8B;EAAE,MAAM;EAAW;CAAQ;CAE/D,IAAI,IAAI,gBACN,YAAY,KAAK,IAAI,eAAe,SAAS,GAAG,SAAS,wBAAwB;CAEnF,KAAK,MAAM,EAAE,OAAO,aAAa,IAAI,gBAAgB,CAAC,GAAG;EACvD,IAAI,CAAC,QAAQ,SAAS,GAAG;EACzB,YAAY,KAAK,kBAAkB,OAAO,SAAS,GAAG,SAAS,GAAG,MAAM,uBAAuB;CACjG;CACA,IAAI,cAAc,WAAW,OAAO;AACtC;AAEA,SAAS,YAAY,KAAgC,SAAiB,SAA6B,SAAuB;CACxH,IAAI;EACF,IAAI,QAAQ,SAAS,OAAO;CAC9B,SAAS,KAAK;EACZ,IAAI,OAAO,GAAG,QAAQ,qCAAqC;GAAE;GAAS,OAAO,OAAO,GAAG;EAAE,CAAC;CAC5F;AACF;AAEA,SAAS,OAAO,KAAsB;CACpC,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD"}
@@ -0,0 +1,43 @@
1
+ /** Payload published on every file-change channel. `mtimeMs` is the post-write
2
+ * modified time — monotonic, suitable for cache-busting (`?v=`) and out-of-order
3
+ * drops. */
4
+ export interface FileChannelPayload {
5
+ path: string;
6
+ mtimeMs: number;
7
+ }
8
+ /** A plugin View that wants live-refresh: when `matches(posixPath)` is true, the
9
+ * change is forwarded to `plugin:<scope>:file:<path>` (the channel the View's
10
+ * runtime subscribes to). */
11
+ export interface FileChangeScope {
12
+ scope: string;
13
+ matches: (posixPath: string) => boolean;
14
+ }
15
+ export interface FileChangePublisherConfig {
16
+ /** Host pubsub publish. */
17
+ publish: (channel: string, payload: FileChannelPayload) => void;
18
+ /** Workspace root — joined with the relative path to stat the post-write mtime. */
19
+ workspaceRoot: string;
20
+ /** Normalise a workspace-relative path to POSIX (drives both `payload.path` and
21
+ * every channel suffix, so they can't drift on mixed separators). */
22
+ toPosix: (relativePath: string) => string;
23
+ /** The host's primary file channel (e.g. a Files explorer's `file:<path>`).
24
+ * Omit when the host has no general file subscriber (e.g. MulmoTerminal). */
25
+ primaryChannel?: (posixPath: string) => string;
26
+ /** Plugin Views to forward to. */
27
+ pluginScopes?: FileChangeScope[];
28
+ /** Optional side-effect after publishing (e.g. a topic-index regen). Receives the
29
+ * posix path + payload; may itself call `publishFileChange` for derived files. */
30
+ onPublished?: (posixPath: string, payload: FileChannelPayload) => void;
31
+ /** Optional warn logger; a publish/stat failure logs but never throws (callers
32
+ * fire-and-forget, so a throw would be an unhandled rejection). */
33
+ warn?: (message: string, data?: Record<string, unknown>) => void;
34
+ }
35
+ /** Wire the publisher to a host. Call once at startup, before any write route. */
36
+ export declare function configureFileChangePublisher(cfg: FileChangePublisherConfig): void;
37
+ /** Clear the binding — test-only. */
38
+ export declare function resetFileChangePublisher(): void;
39
+ /** The plugin-scoped live-refresh channel a View subscribes to. */
40
+ export declare function pluginFileChannel(scope: string, posixPath: string): string;
41
+ /** Publish a file-change for a workspace-relative path: the primary channel (if any)
42
+ * + every matching plugin scope, with the post-write mtime. No-op until configured. */
43
+ export declare function publishFileChange(relativePath: string): Promise<void>;
@@ -0,0 +1,32 @@
1
+ const require_deriveAll = require("./deriveAll-VRWrs3SF.cjs");
2
+ require("./collection/index.cjs");
3
+ //#region src/feeds/ingestTypes.ts
4
+ var AGENT_INGEST_KIND = "agent";
5
+ var FEED_SCHEDULE_SET = new Set(require_deriveAll.FEED_SCHEDULES);
6
+ function isFeedSchedule(value) {
7
+ return typeof value === "string" && FEED_SCHEDULE_SET.has(value);
8
+ }
9
+ /** Default cap on stored records per feed when `ingest.maxItems` is
10
+ * omitted. Keeps high-volume feeds (news / podcasts) bounded. */
11
+ var DEFAULT_FEED_MAX_ITEMS = 100;
12
+ //#endregion
13
+ Object.defineProperty(exports, "AGENT_INGEST_KIND", {
14
+ enumerable: true,
15
+ get: function() {
16
+ return AGENT_INGEST_KIND;
17
+ }
18
+ });
19
+ Object.defineProperty(exports, "DEFAULT_FEED_MAX_ITEMS", {
20
+ enumerable: true,
21
+ get: function() {
22
+ return DEFAULT_FEED_MAX_ITEMS;
23
+ }
24
+ });
25
+ Object.defineProperty(exports, "isFeedSchedule", {
26
+ enumerable: true,
27
+ get: function() {
28
+ return isFeedSchedule;
29
+ }
30
+ });
31
+
32
+ //# sourceMappingURL=ingestTypes-DDOVhOYC.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingestTypes-DDOVhOYC.cjs","names":[],"sources":["../src/feeds/ingestTypes.ts"],"sourcesContent":["// Declarative retrieval config for the \"Feeds\" mechanism. A Feed is a\n// CollectionSchema plus this `ingest` block, registered as data (NOT as\n// a skill) under `<workspace>/feeds/<slug>/schema.json`. The host's\n// retrieval engine reads it to periodically refill the collection's\n// records via the shared collections io layer.\n//\n// The ingest vocab (INGEST_KINDS / FEED_SCHEDULES + their literal-union types)\n// lives in the sibling `../collection` subpath alongside the schema contract, so\n// the package's schema validator can enforce it. Re-exported here so the feeds\n// engine's existing importers resolve them unchanged.\nimport { type CollectionIngest, INGEST_KINDS, FEED_SCHEDULES, type IngestKind, type FeedSchedule } from \"../collection/index.js\";\n//\n// Two flavours: the declarative kinds (`rss`/`atom`/`http-json`) fetch-and-map,\n// and `agent` dispatches a hidden worker. The `code` kind (LLM-generated\n// deterministic transform) is still reserved for a future retriever.\n\n// The agent ingest kind. Defined HERE (not imported from `../collection`) on\n// purpose: the engine only needs the literal to branch, and re-importing it as a\n// VALUE keeps this module free of a value dependency on the collection vocab.\n// Core owns its own copy for the schema validator; this matches the same literal.\nexport const AGENT_INGEST_KIND = \"agent\" as const;\nexport type AgentIngestKind = typeof AGENT_INGEST_KIND;\n\nexport { INGEST_KINDS, FEED_SCHEDULES, type IngestKind, type FeedSchedule };\n\nconst FEED_SCHEDULE_SET: ReadonlySet<string> = new Set(FEED_SCHEDULES);\n\nexport function isFeedSchedule(value: unknown): value is FeedSchedule {\n return typeof value === \"string\" && FEED_SCHEDULE_SET.has(value);\n}\n\n/** Default cap on stored records per feed when `ingest.maxItems` is\n * omitted. Keeps high-volume feeds (news / podcasts) bounded. */\nexport const DEFAULT_FEED_MAX_ITEMS = 100;\n\n/** Declarative field map: target collection field name → source path\n * into the raw item (dot/bracket path, e.g. `\"title\"` or\n * `\"data.name\"`). */\nexport type IngestFieldMap = Record<string, string>;\n\n/** Declarative retrieval (`rss`/`atom`/`http-json`): the host fetches `url`\n * and projects each item through `map`. The canonical schema (in\n * `../collection`) only promises the minimal `CollectionIngest`; this\n * feeds-only subtype narrows those + adds the retrieval fields the engine\n * needs. */\nexport interface DeclarativeIngestSpec extends CollectionIngest {\n /** Which declarative retriever handles this feed. */\n kind: IngestKind;\n /** Endpoint to fetch (http/https). */\n url: string;\n /** Refresh cadence. */\n schedule: FeedSchedule;\n /** `http-json` only: dot/bracket path to the array of items in the\n * response (e.g. `\"hourly[]\"` or `\"data.results[]\"`). Ignored for\n * `rss`/`atom`, which yield items natively. */\n itemsAt?: string;\n /** target field → source path. Projects each raw item into a record\n * whose keys match the schema's `fields`. */\n map: IngestFieldMap;\n /** Optional source path used to derive the primaryKey value when the\n * mapped record's primaryKey is empty (e.g. `\"feedId\"`). Falls back\n * to a content hash of the record. */\n idFrom?: string;\n /** Cap on stored records. After each fetch the feed keeps only the\n * newest `maxItems` (ordered by the schema's first `date` field) and\n * deletes the rest. Defaults to {@link DEFAULT_FEED_MAX_ITEMS} when\n * omitted; `0` disables the cap (keep everything). Pruning is skipped\n * when the schema has no `date` field to order by. */\n maxItems?: number;\n}\n\n/** Agent-performed retrieval (`kind: \"agent\"`). No `url`/`map`: the host seeds\n * a hidden background worker (origin `system`) in `role` with `template` + a\n * summary of every record, and the worker edits the records itself via the\n * collections io layer. Valid on any collection (primarily skill-backed). */\nexport interface AgentIngestSpec extends CollectionIngest {\n kind: AgentIngestKind;\n /** Refresh cadence (same vocabulary as declarative feeds). */\n schedule: FeedSchedule;\n /** Role id the scheduled hidden worker runs in. */\n role: string;\n /** Skill-relative template path (under `templates/`) seeding the worker. */\n template: string;\n}\n\n/** The `ingest` block carried on a `CollectionSchema`, discriminated on `kind`. */\nexport type IngestSpec = DeclarativeIngestSpec | AgentIngestSpec;\n"],"mappings":";;;AAoBA,IAAa,oBAAoB;AAKjC,IAAM,oBAAyC,IAAI,IAAI,kBAAA,cAAc;AAErE,SAAgB,eAAe,OAAuC;CACpE,OAAO,OAAO,UAAU,YAAY,kBAAkB,IAAI,KAAK;AACjE;;;AAIA,IAAa,yBAAyB"}
@@ -0,0 +1,72 @@
1
+ import { WriteJson } from './store.js';
2
+ import { NotifierEntry, NotifierEvent, NotifierHistoryEntry, NotifierSeverity, PublishInput } from './types.js';
3
+ export { NOTIFIER_LIMITS, validatePublishInput } from './validate.js';
4
+ /** Minimal logger the engine needs. The host passes its structured
5
+ * logger; absent one, failures are swallowed (the engine never throws
6
+ * on a fan-out/persist-best-effort path). */
7
+ export interface NotifierLogger {
8
+ warn: (message: string, data?: Record<string, unknown>) => void;
9
+ error: (message: string, data?: Record<string, unknown>) => void;
10
+ }
11
+ export interface NotifierConfig {
12
+ /** Atomic JSON writer (the host's `writeJsonAtomic`). */
13
+ writeJson: WriteJson;
14
+ /** Fan-out sink — the host binds this to `pubsub.publish(channel, event)`. */
15
+ publishEvent: (event: NotifierEvent) => void;
16
+ /** Optional logger. */
17
+ log?: NotifierLogger;
18
+ }
19
+ /** Wire the engine's I/O deps. Call once at startup, before the first
20
+ * mutation. Does NOT set file paths — those are set independently via
21
+ * `setNotifierFilePaths` so a host can bind production paths at module
22
+ * load and a test can override them without re-supplying the deps. */
23
+ export declare function configureNotifier(injected: NotifierConfig): void;
24
+ type NotifierEventListener = (event: NotifierEvent) => void;
25
+ /** Register an in-process listener for engine events. Returns an
26
+ * unsubscribe function the caller can use during teardown. */
27
+ export declare function onEvent(listener: NotifierEventListener): () => void;
28
+ /** Point the engine at its active/history files. Resets the write
29
+ * queue, so callers must not have in-flight mutations. The host calls
30
+ * this once with the workspace paths; tests call it per-case with temp
31
+ * files. */
32
+ export declare function setNotifierFilePaths(paths: {
33
+ active: string;
34
+ history: string;
35
+ }): void;
36
+ /** Test-only: clear config + queue so each suite starts clean. */
37
+ export declare function resetNotifier(): void;
38
+ export declare function publish<TPluginData = unknown>(input: PublishInput<TPluginData>): Promise<{
39
+ id: string;
40
+ }>;
41
+ export declare function clear(entryId: string): Promise<void>;
42
+ export declare function cancel(entryId: string): Promise<void>;
43
+ /** In-place update for an active entry. Only the fields present on
44
+ * `patch` are rewritten; `id`, `pluginPkg`, `lifecycle`, and
45
+ * `createdAt` stay fixed. Emits a single `"updated"` event with the
46
+ * post-mutation entry — no history record is written because the
47
+ * entry is still active, just with refreshed content.
48
+ *
49
+ * No-ops (no throw) when the id is unknown, the entry belongs to a
50
+ * different plugin, or the merged shape would violate
51
+ * `validatePublishInput`. The silent skip matches `clearForPlugin`'s
52
+ * isolation semantics; validation failures are logged for diagnosis. */
53
+ export declare function updateForPlugin<TPluginData = unknown>(pluginPkg: string, entryId: string, patch: {
54
+ severity?: NotifierSeverity;
55
+ title?: string;
56
+ body?: string;
57
+ navigateTarget?: string;
58
+ pluginData?: TPluginData;
59
+ }): Promise<void>;
60
+ /** Plugin-scoped point lookup. Returns the entry by id, but only if it
61
+ * belongs to the caller's plugin; otherwise undefined. Cross-plugin
62
+ * reads return undefined for isolation — same property as
63
+ * `clearForPlugin` / `updateForPlugin`. */
64
+ export declare function getForPlugin(pluginPkg: string, entryId: string): Promise<NotifierEntry | undefined>;
65
+ /** Plugin-scoped clear. Same as `clear` but no-ops if the entry's
66
+ * `pluginPkg` doesn't match the caller's, so a plugin can't dismiss
67
+ * another plugin's notification by guessing or scraping its id. */
68
+ export declare function clearForPlugin(pluginPkg: string, entryId: string): Promise<void>;
69
+ export declare function get(entryId: string): Promise<NotifierEntry | undefined>;
70
+ export declare function listFor(pluginPkg: string): Promise<NotifierEntry[]>;
71
+ export declare function listAll(): Promise<NotifierEntry[]>;
72
+ export declare function listHistory(): Promise<NotifierHistoryEntry[]>;
@@ -0,0 +1,21 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_notifier = require("../notifier-bS8IEeLA.cjs");
3
+ exports.HISTORY_CAP = require_notifier.HISTORY_CAP;
4
+ exports.NOTIFIER_LIFECYCLES = require_notifier.NOTIFIER_LIFECYCLES;
5
+ exports.NOTIFIER_LIMITS = require_notifier.NOTIFIER_LIMITS;
6
+ exports.NOTIFIER_SEVERITIES = require_notifier.NOTIFIER_SEVERITIES;
7
+ exports.cancel = require_notifier.cancel;
8
+ exports.clear = require_notifier.clear;
9
+ exports.clearForPlugin = require_notifier.clearForPlugin;
10
+ exports.configureNotifier = require_notifier.configureNotifier;
11
+ exports.get = require_notifier.get;
12
+ exports.getForPlugin = require_notifier.getForPlugin;
13
+ exports.listAll = require_notifier.listAll;
14
+ exports.listFor = require_notifier.listFor;
15
+ exports.listHistory = require_notifier.listHistory;
16
+ exports.onEvent = require_notifier.onEvent;
17
+ exports.publish = require_notifier.publish;
18
+ exports.resetNotifier = require_notifier.resetNotifier;
19
+ exports.setNotifierFilePaths = require_notifier.setNotifierFilePaths;
20
+ exports.updateForPlugin = require_notifier.updateForPlugin;
21
+ exports.validatePublishInput = require_notifier.validatePublishInput;
@@ -0,0 +1,3 @@
1
+ export * from './types.js';
2
+ export * from './engine.js';
3
+ export type { WriteJson } from './store.js';
@@ -0,0 +1,18 @@
1
+ import { NotifierFile, NotifierHistoryFile } from './types.js';
2
+ /** Injected atomic JSON writer — the host's `writeJsonAtomic`. */
3
+ export type WriteJson = (filePath: string, data: unknown) => Promise<void>;
4
+ /** Read the active-entries file. Returns an empty store when the file
5
+ * doesn't exist yet (first ever call on a fresh workspace). Any other
6
+ * read or parse failure throws — the caller has to decide whether to
7
+ * surface or recover, since silently treating "malformed file" as
8
+ * "no entries" would lose data. */
9
+ export declare function loadActive(filePath: string): Promise<NotifierFile>;
10
+ /** Write the active-entries file via the injected atomic writer so a
11
+ * half-written file is never visible to readers. The caller serialises
12
+ * writes (engine.ts queues mutations) — this function makes no
13
+ * concurrency guarantees of its own. */
14
+ export declare function saveActive(writeJson: WriteJson, filePath: string, state: NotifierFile): Promise<void>;
15
+ /** Read the history file. Empty array on first run. Same parse-error
16
+ * policy as `loadActive`. */
17
+ export declare function loadHistory(filePath: string): Promise<NotifierHistoryFile>;
18
+ export declare function saveHistory(writeJson: WriteJson, filePath: string, state: NotifierHistoryFile): Promise<void>;
@@ -0,0 +1,118 @@
1
+ /** Two notification shapes, distinguished by who fires the close call:
2
+ *
3
+ * `fyi` — informational. The host (bell panel) clears it when the
4
+ * user dismisses the row. No deep-link target.
5
+ * `action` — pending obligation. The plugin clears it when the
6
+ * underlying domain state changes (the user paid the tax,
7
+ * viewed the digest, etc.). The bell row navigates to
8
+ * `navigateTarget` on click.
9
+ *
10
+ * The engine reads `lifecycle` only to enforce two publish-time rules
11
+ * (everything downstream — pubsub fan-out, persistence, history — is
12
+ * lifecycle-blind):
13
+ *
14
+ * 1. `action` requires a non-empty `navigateTarget`. Without one,
15
+ * clicking the row does nothing and the entry is a degraded fyi.
16
+ * 2. `action` cannot use `info` severity. A low-priority obligation
17
+ * is incoherent — fyi if it's a ping, `nudge`/`urgent` if it's a
18
+ * real obligation worth a landing page.
19
+ *
20
+ * Both rules are mirrored in the HTTP layer so plugin-runtime callers
21
+ * and HTTP callers hit the same wall. */
22
+ export declare const NOTIFIER_LIFECYCLES: readonly ["fyi", "action"];
23
+ export type NotifierLifecycle = (typeof NOTIFIER_LIFECYCLES)[number];
24
+ /** Severity drives badge color (gray / amber / red, worst-wins) and
25
+ * in a future iteration channel routing. Mostly stored verbatim by
26
+ * the engine; the one engine-visible interaction is the rule that
27
+ * `action` lifecycle cannot pair with `info` severity (see
28
+ * `NotifierLifecycle` above). */
29
+ export declare const NOTIFIER_SEVERITIES: readonly ["info", "nudge", "urgent"];
30
+ export type NotifierSeverity = (typeof NOTIFIER_SEVERITIES)[number];
31
+ export interface NotifierEntry<TPluginData = unknown> {
32
+ /** Engine-assigned UUID. Generated synchronously inside `publish()`
33
+ * so the caller can use it before persistence completes. */
34
+ id: string;
35
+ /** Plugin namespace (e.g. `"encore"`, `"debug__system"`). The
36
+ * engine never inspects it — used only for `listFor()` filtering
37
+ * and as a UI grouping key. */
38
+ pluginPkg: string;
39
+ severity: NotifierSeverity;
40
+ lifecycle?: NotifierLifecycle;
41
+ title: string;
42
+ body?: string;
43
+ /** Optional in-app deep-link target (relative URL). The bell popup
44
+ * routes here on row click, with `&notificationId=<id>` appended
45
+ * so the landing page can identify which entry to clear. The
46
+ * engine doesn't read this — it's a UI hint stored on the entry. */
47
+ navigateTarget?: string;
48
+ /** Opaque to the engine. Round-trips through JSON unchanged; only
49
+ * the originating plugin's UI knows the shape. */
50
+ pluginData?: TPluginData;
51
+ /** ISO-8601 timestamp set at `publish()` time. */
52
+ createdAt: string;
53
+ }
54
+ /** A history entry — a `NotifierEntry` after it has been cleared or
55
+ * cancelled, with the terminal type and timestamp recorded. The
56
+ * bell popup's "History" section renders these read-only. */
57
+ export interface NotifierHistoryEntry<TPluginData = unknown> extends NotifierEntry<TPluginData> {
58
+ terminalType: "cleared" | "cancelled";
59
+ terminalAt: string;
60
+ }
61
+ /** Caller-supplied input for `publish()`. The engine fills in `id`
62
+ * and `createdAt`; everything else flows through verbatim.
63
+ *
64
+ * Two publish-time rules apply to `action` lifecycle (see
65
+ * `NotifierLifecycle`):
66
+ *
67
+ * - `navigateTarget` MUST be a non-empty string.
68
+ * - `severity` MUST NOT be `"info"`.
69
+ *
70
+ * Violations cause `publish()` to throw. Currently expressed as
71
+ * runtime validation rather than a discriminated-union type, so the
72
+ * fields below are all individually optional / loose at the
73
+ * type-level. */
74
+ export interface PublishInput<TPluginData = unknown> {
75
+ pluginPkg: string;
76
+ severity: NotifierSeverity;
77
+ title: string;
78
+ body?: string;
79
+ lifecycle?: NotifierLifecycle;
80
+ navigateTarget?: string;
81
+ pluginData?: TPluginData;
82
+ }
83
+ /** On-disk shape of `~/mulmoclaude/data/notifier/active.json`. Holds
84
+ * only entries that haven't been cleared or cancelled — the file is
85
+ * a snapshot, not an event log. */
86
+ export interface NotifierFile {
87
+ entries: Record<string, NotifierEntry>;
88
+ }
89
+ /** On-disk shape of `~/mulmoclaude/data/notifier/history.json`. Array
90
+ * of terminated entries newest-first, capped at `HISTORY_CAP` with
91
+ * FIFO eviction (push at index 0, slice from the tail). */
92
+ export interface NotifierHistoryFile {
93
+ entries: NotifierHistoryEntry[];
94
+ }
95
+ /** History size cap. The bell popup's History section renders this
96
+ * many entries; older ones fall off when new terminations land. */
97
+ export declare const HISTORY_CAP = 50;
98
+ /** Pub-sub event published on the host's notifier channel after every
99
+ * successful state change. Discriminated union — subscribers switch
100
+ * on `type` to keep TypeScript narrowing the rest of the payload.
101
+ *
102
+ * `updated` carries the post-mutation entry — the receiver swaps
103
+ * the matching `id` in their local active set. Reserved for in-
104
+ * place edits via `updateForPlugin`; no history record is written
105
+ * because the entry is still active, just with refreshed content. */
106
+ export type NotifierEvent = {
107
+ type: "published";
108
+ entry: NotifierEntry;
109
+ } | {
110
+ type: "cleared";
111
+ id: string;
112
+ } | {
113
+ type: "cancelled";
114
+ id: string;
115
+ } | {
116
+ type: "updated";
117
+ entry: NotifierEntry;
118
+ };
@@ -0,0 +1,17 @@
1
+ import { PublishInput } from './types.js';
2
+ /** Hard caps on publish-input fields. The engine reads each entry on
3
+ * every list/get call (no in-memory cache), so unbounded fields hurt
4
+ * every reader. Caps chosen to be generous for legitimate UX copy
5
+ * while bounding active.json growth: a notification fundamentally is
6
+ * a short blurb, not a document. */
7
+ export declare const NOTIFIER_LIMITS: {
8
+ readonly titleMax: 200;
9
+ readonly bodyMax: 4000;
10
+ readonly navigateTargetMax: 1000;
11
+ readonly pluginDataMaxBytes: number;
12
+ };
13
+ /** Validate a `PublishInput`. Returns `null` if OK, or a
14
+ * human-readable error string. Order matters — shape/size errors are
15
+ * reported before lifecycle/severity coherence errors so the message
16
+ * the caller sees points at the most fundamental problem first. */
17
+ export declare function validatePublishInput(input: PublishInput): string | null;