@contentrain/mcp 3.3.3 → 3.4.0

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 (35) hide show
  1. package/README.md +55 -0
  2. package/dist/{content-manager-9yUO24W2.d.mts → content-manager-BqaMkMVk.d.mts} +2 -2
  3. package/dist/{content-manager-9yUO24W2.d.mts.map → content-manager-BqaMkMVk.d.mts.map} +1 -1
  4. package/dist/core/config.d.mts +1 -1
  5. package/dist/core/content-manager.d.mts +1 -1
  6. package/dist/core/context.d.mts +1 -1
  7. package/dist/core/contracts/index.d.mts +1 -1
  8. package/dist/core/migration/index.d.mts +122 -0
  9. package/dist/core/migration/index.d.mts.map +1 -0
  10. package/dist/core/migration/index.mjs +150 -0
  11. package/dist/core/migration/index.mjs.map +1 -0
  12. package/dist/core/model-manager.d.mts +1 -1
  13. package/dist/core/ops/index.d.mts +3 -3
  14. package/dist/core/overlay-reader.d.mts +1 -1
  15. package/dist/core/validator/index.d.mts +1 -1
  16. package/dist/git/reconcile.d.mts +1 -1
  17. package/dist/index-A5iw3yDk.d.mts +2 -0
  18. package/dist/{index-_HJVkDJa.d.mts → index-DKZNUqpP.d.mts} +2 -2
  19. package/dist/{index-_HJVkDJa.d.mts.map → index-DKZNUqpP.d.mts.map} +1 -1
  20. package/dist/index.mjs +1 -1
  21. package/dist/providers/github/index.d.mts +1 -1
  22. package/dist/providers/gitlab/index.d.mts +1 -1
  23. package/dist/providers/local/index.d.mts +4 -4
  24. package/dist/providers/local/index.d.mts.map +1 -1
  25. package/dist/server/http/index.d.mts +1 -1
  26. package/dist/server/http/index.mjs +1 -1
  27. package/dist/{server-SDsD0XGM.d.mts → server-CUtmhlno.d.mts} +2 -2
  28. package/dist/{server-SDsD0XGM.d.mts.map → server-CUtmhlno.d.mts.map} +1 -1
  29. package/dist/{server-BgyNDHvp.mjs → server-Do2BcSBg.mjs} +2 -2
  30. package/dist/{server-BgyNDHvp.mjs.map → server-Do2BcSBg.mjs.map} +1 -1
  31. package/dist/server.d.mts +1 -1
  32. package/dist/server.mjs +1 -1
  33. package/dist/testing/memory-provider.d.mts +1 -1
  34. package/package.json +8 -4
  35. package/dist/index-ByrVsRJE.d.mts +0 -2
package/README.md CHANGED
@@ -248,6 +248,7 @@ The package also exposes low-level modules for embedding and advanced use:
248
248
  - `@contentrain/mcp/core/contracts`
249
249
  - `@contentrain/mcp/core/ops` — plan APIs (including `planReconcile` and its `bindRef` reader adapter) plus content-root-relative path helpers: `contentDirPath`, `contentFilePath`, `documentFilePath`, `metaFilePath`
250
250
  - `@contentrain/mcp/core/overlay-reader`
251
+ - `@contentrain/mcp/core/migration` — the allowlisted migration write path: `createMigrationWriter`, `scopeHash`, `decide`. See **Migration writes** below
251
252
  - `@contentrain/mcp/util/detect`
252
253
  - `@contentrain/mcp/util/fs`
253
254
  - `@contentrain/mcp/git/transaction`
@@ -262,6 +263,60 @@ The package also exposes low-level modules for embedding and advanced use:
262
263
 
263
264
  These are intended for Contentrain tooling and advanced integrations, not for direct manual editing of `.contentrain/` files.
264
265
 
266
+ ## Migration Writes
267
+
268
+ A migration does not deliver content, it delivers a codebase: `src/`, `public/`,
269
+ `package.json`, a lockfile, deploy configuration. The content write path was
270
+ never built for any of that, and widening it would quietly move a security
271
+ boundary the whole product rests on — "Contentrain only writes `.contentrain/`"
272
+ is a promise, not an implementation detail.
273
+
274
+ `@contentrain/mcp/core/migration` is the gate instead. It wraps a provider whose
275
+ `applyPlan` can already write any path, and puts consent in front of it.
276
+
277
+ ```ts
278
+ import { createMigrationWriter, scopeHash } from '@contentrain/mcp/core/migration'
279
+
280
+ const scope = {
281
+ allow: ['src/**', 'public/**', 'package.json', 'astro.config.mjs'],
282
+ branch: 'migration/acme',
283
+ base: 'main',
284
+ }
285
+
286
+ // Show the user `scope`, not the hash. Then bind their consent to it:
287
+ const approval = {
288
+ scope_hash: await scopeHash(scope),
289
+ approver: { kind: 'human', id: userId, role: 'owner' },
290
+ approved_at: new Date().toISOString(),
291
+ }
292
+
293
+ const writer = createMigrationWriter(provider, scope, approval)
294
+ await writer.applyPlan({ ...plan, step: 'emit', actor })
295
+ writer.audit // which file, in which step, by which actor, in which commit
296
+ ```
297
+
298
+ What it guarantees:
299
+
300
+ | | |
301
+ |---|---|
302
+ | **Scope** | Only paths an allowlist pattern covers. `.contentrain/**` is not implicit — a migration that wants the store asks for it, and the person approving sees that it does |
303
+ | **Consent** | One approval, bound to the exact scope by `scope_hash`. An approval of `src/**` cannot be replayed against a scope that also has `.github/workflows/**` |
304
+ | **Branch** | A `migration/*` ref. The `contentrain` branch is never a target and never a base — including through `ApplyPlanInput`'s default, which a migration must not inherit |
305
+ | **Trail** | One `MigrationAuditEntry` per file: step, path, action, the pattern that allowed it, actor, timestamp, commit |
306
+ | **Undo** | One branch, delivered as one PR, revertable with one `git revert` |
307
+
308
+ Every refusal happens before the provider is called, and a plan is refused
309
+ whole: a plan with one path outside the scope writes nothing at all, not even
310
+ its allowed part. A half-applied migration is harder to recover from than one
311
+ that never started.
312
+
313
+ The path matcher is a security control and is written to fail closed. Traversal,
314
+ absolute paths, backslash separators, percent-encoding, empty segments and
315
+ control characters are refused before any pattern is consulted; patterns are
316
+ anchored at both ends, so `src/*` does not cover `src/a/b` and nothing matches
317
+ by being a prefix; and regex metacharacters in a pattern are literal, so an
318
+ allowlist entry cannot silently cover more than it reads.
319
+
265
320
  ## Design Constraints
266
321
 
267
322
  Key design decisions in this package:
@@ -1,4 +1,4 @@
1
- import { _ as RepoReader } from "./index-ByrVsRJE.mjs";
1
+ import { _ as RepoReader } from "./index-A5iw3yDk.mjs";
2
2
  import { ContentrainConfig, LocaleStrategy, ModelDefinition, Vocabulary, parseMarkdownFrontmatter as parseFrontmatter, serializeMarkdownFrontmatter as serializeFrontmatter, validateEntryId as validateEntryId$1, validateLocale as validateLocale$1, validateSlug as validateSlug$1 } from "@contentrain/types";
3
3
 
4
4
  //#region src/core/content-manager.d.ts
@@ -73,4 +73,4 @@ declare function readContent(projectRoot: string, model: ModelDefinition, opts:
73
73
  }): Promise<unknown>;
74
74
  //#endregion
75
75
  export { validateEntryId$1 as _, ListOpts as a, writeContent as b, deleteContent as c, readContent as d, resolveContentDir as f, serializeFrontmatter as g, resolveMdFilePath as h, DeleteOpts as i, listContent as l, resolveLocaleStrategy as m, ContentFileModel as n, LocaleStrategyModel as o, resolveJsonFilePath as p, ContentPathModel as r, WriteResult as s, ContentEntry as t, parseFrontmatter as u, validateLocale$1 as v, validateSlug$1 as y };
76
- //# sourceMappingURL=content-manager-9yUO24W2.d.mts.map
76
+ //# sourceMappingURL=content-manager-BqaMkMVk.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"content-manager-9yUO24W2.d.mts","names":[],"sources":["../src/core/content-manager.ts"],"mappings":";;;;KAyBY,gBAAA,GAAmB,IAAA,CAAK,eAAA;AAAA,KACxB,mBAAA,GAAsB,IAAA,CAAK,eAAA;AAAA,KAC3B,gBAAA,GAAmB,IAAA,CAAK,eAAA;AAAA,iBAEpB,iBAAA,CAAkB,WAAA,UAAqB,KAAA,EAAO,gBAAA;AAAA,iBAO9C,qBAAA,CAAsB,KAAA,EAAO,mBAAA,GAAsB,cAAA;;iBAKnD,mBAAA,CAAoB,GAAA,UAAa,KAAA,EAAO,gBAAA,EAAkB,MAAA;;iBAc1D,iBAAA,CAAkB,GAAA,UAAa,KAAA,EAAO,IAAA,CAAK,eAAA,+BAA8C,MAAA,UAAgB,IAAA;AAAA,UAexG,YAAA;EACf,EAAA;EACA,IAAA;EACA,MAAA;EACA,IAAA,EAAM,MAAA;EA7CQ;;;;;EAmDd,UAAA;EAnDqD;EAqDrD,SAAA;AAAA;AAAA,UAGe,WAAA;EACf,MAAA;EACA,EAAA;EACA,IAAA;EACA,MAAA;EACA,UAAA;AAAA;AAAA,UAGe,UAAA;EACf,EAAA;EACA,IAAA;EACA,MAAA;EACA,IAAA;EAxDsE;EA0DtE,aAAA;AAAA;AAAA,UAGe,QAAA;EACf,MAAA;EACA,MAAA,GAAS,MAAA;EACT,OAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,iBAKoB,YAAA,CACpB,WAAA,UACA,KAAA,EAAO,eAAA,EACP,OAAA,EAAS,YAAA,IACT,MAAA,EAAQ,iBAAA,EACR,UAAA,GAAa,UAAA,UACZ,OAAA,CAAQ,WAAA;AAAA,iBAuJW,aAAA,CACpB,WAAA,UACA,KAAA,EAAO,eAAA,EACP,IAAA,EAAM,UAAA,GACL,OAAA;;;;;;;;AA3MH;;;;;;;;iBAwVgB,WAAA,CACd,WAAA,UACA,KAAA,EAAO,eAAA,EACP,IAAA,EAAM,QAAA,EACN,MAAA,EAAQ,iBAAA,GACP,OAAA;AAAA,iBACa,WAAA,CACd,MAAA,EAAQ,UAAA,EACR,KAAA,EAAO,eAAA,EACP,IAAA,EAAM,QAAA,EACN,MAAA,EAAQ,iBAAA,GACP,OAAA;AAAA,iBA0QmB,WAAA,CACpB,WAAA,UACA,KAAA,EAAO,eAAA,EACP,IAAA;EAAQ,MAAA;EAAgB,OAAA;EAAkB,IAAA;AAAA,IACzC,OAAA"}
1
+ {"version":3,"file":"content-manager-BqaMkMVk.d.mts","names":[],"sources":["../src/core/content-manager.ts"],"mappings":";;;;KAyBY,gBAAA,GAAmB,IAAA,CAAK,eAAA;AAAA,KACxB,mBAAA,GAAsB,IAAA,CAAK,eAAA;AAAA,KAC3B,gBAAA,GAAmB,IAAA,CAAK,eAAA;AAAA,iBAEpB,iBAAA,CAAkB,WAAA,UAAqB,KAAA,EAAO,gBAAA;AAAA,iBAO9C,qBAAA,CAAsB,KAAA,EAAO,mBAAA,GAAsB,cAAA;;iBAKnD,mBAAA,CAAoB,GAAA,UAAa,KAAA,EAAO,gBAAA,EAAkB,MAAA;;iBAc1D,iBAAA,CAAkB,GAAA,UAAa,KAAA,EAAO,IAAA,CAAK,eAAA,+BAA8C,MAAA,UAAgB,IAAA;AAAA,UAexG,YAAA;EACf,EAAA;EACA,IAAA;EACA,MAAA;EACA,IAAA,EAAM,MAAA;EA7CQ;;;;;EAmDd,UAAA;EAnDqD;EAqDrD,SAAA;AAAA;AAAA,UAGe,WAAA;EACf,MAAA;EACA,EAAA;EACA,IAAA;EACA,MAAA;EACA,UAAA;AAAA;AAAA,UAGe,UAAA;EACf,EAAA;EACA,IAAA;EACA,MAAA;EACA,IAAA;EAxDsE;EA0DtE,aAAA;AAAA;AAAA,UAGe,QAAA;EACf,MAAA;EACA,MAAA,GAAS,MAAA;EACT,OAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,iBAKoB,YAAA,CACpB,WAAA,UACA,KAAA,EAAO,eAAA,EACP,OAAA,EAAS,YAAA,IACT,MAAA,EAAQ,iBAAA,EACR,UAAA,GAAa,UAAA,UACZ,OAAA,CAAQ,WAAA;AAAA,iBAuJW,aAAA,CACpB,WAAA,UACA,KAAA,EAAO,eAAA,EACP,IAAA,EAAM,UAAA,GACL,OAAA;;;;;;;;AA3MH;;;;;;;;iBAwVgB,WAAA,CACd,WAAA,UACA,KAAA,EAAO,eAAA,EACP,IAAA,EAAM,QAAA,EACN,MAAA,EAAQ,iBAAA,GACP,OAAA;AAAA,iBACa,WAAA,CACd,MAAA,EAAQ,UAAA,EACR,KAAA,EAAO,eAAA,EACP,IAAA,EAAM,QAAA,EACN,MAAA,EAAQ,iBAAA,GACP,OAAA;AAAA,iBA0QmB,WAAA,CACpB,WAAA,UACA,KAAA,EAAO,eAAA,EACP,IAAA;EAAQ,MAAA;EAAgB,OAAA;EAAkB,IAAA;AAAA,IACzC,OAAA"}
@@ -1,4 +1,4 @@
1
- import { _ as RepoReader } from "../index-ByrVsRJE.mjs";
1
+ import { _ as RepoReader } from "../index-A5iw3yDk.mjs";
2
2
  import { ContentrainConfig, Vocabulary } from "@contentrain/types";
3
3
 
4
4
  //#region src/core/config.d.ts
@@ -1,2 +1,2 @@
1
- import { _ as validateEntryId, a as ListOpts, b as writeContent, c as deleteContent, d as readContent, f as resolveContentDir, g as serializeFrontmatter, h as resolveMdFilePath, i as DeleteOpts, l as listContent, m as resolveLocaleStrategy, n as ContentFileModel, o as LocaleStrategyModel, p as resolveJsonFilePath, r as ContentPathModel, s as WriteResult, t as ContentEntry, u as parseFrontmatter, v as validateLocale, y as validateSlug } from "../content-manager-9yUO24W2.mjs";
1
+ import { _ as validateEntryId, a as ListOpts, b as writeContent, c as deleteContent, d as readContent, f as resolveContentDir, g as serializeFrontmatter, h as resolveMdFilePath, i as DeleteOpts, l as listContent, m as resolveLocaleStrategy, n as ContentFileModel, o as LocaleStrategyModel, p as resolveJsonFilePath, r as ContentPathModel, s as WriteResult, t as ContentEntry, u as parseFrontmatter, v as validateLocale, y as validateSlug } from "../content-manager-BqaMkMVk.mjs";
2
2
  export { ContentEntry, ContentFileModel, ContentPathModel, DeleteOpts, ListOpts, LocaleStrategyModel, WriteResult, deleteContent, listContent, parseFrontmatter, readContent, resolveContentDir, resolveJsonFilePath, resolveLocaleStrategy, resolveMdFilePath, serializeFrontmatter, validateEntryId, validateLocale, validateSlug, writeContent };
@@ -1,4 +1,4 @@
1
- import { _ as RepoReader, a as FileChange } from "../index-ByrVsRJE.mjs";
1
+ import { _ as RepoReader, a as FileChange } from "../index-A5iw3yDk.mjs";
2
2
  import { ContextJson, ContextSource } from "@contentrain/types";
3
3
 
4
4
  //#region src/core/context.d.ts
@@ -1,2 +1,2 @@
1
- import { _ as RepoReader, a as FileChange, c as MediaAsset, d as MediaListResult, f as MediaProvider, g as RepoProvider, h as ProviderCapabilities, i as CommitAuthor, l as MediaIngestInput, m as MergeResult, n as Branch, o as FileDiff, p as MediaUpdateInput, r as Commit, s as LOCAL_CAPABILITIES, t as ApplyPlanInput, u as MediaListOptions, v as RepoWriter, y as WriteReadiness } from "../../index-ByrVsRJE.mjs";
1
+ import { _ as RepoReader, a as FileChange, c as MediaAsset, d as MediaListResult, f as MediaProvider, g as RepoProvider, h as ProviderCapabilities, i as CommitAuthor, l as MediaIngestInput, m as MergeResult, n as Branch, o as FileDiff, p as MediaUpdateInput, r as Commit, s as LOCAL_CAPABILITIES, t as ApplyPlanInput, u as MediaListOptions, v as RepoWriter, y as WriteReadiness } from "../../index-A5iw3yDk.mjs";
2
2
  export { ApplyPlanInput, Branch, Commit, CommitAuthor, FileChange, FileDiff, LOCAL_CAPABILITIES, MediaAsset, MediaIngestInput, MediaListOptions, MediaListResult, MediaProvider, MediaUpdateInput, MergeResult, ProviderCapabilities, RepoProvider, RepoReader, RepoWriter, WriteReadiness };
@@ -0,0 +1,122 @@
1
+ import { ActorRef, ApplyPlanInput, Commit, RepoProvider, RepoWriter } from "@contentrain/types";
2
+
3
+ //#region src/core/migration/allowlist.d.ts
4
+ /** A path that is safe to compare against patterns, or undefined. */
5
+ declare function normalizePath(path: string): string | undefined;
6
+ /**
7
+ * Match one normalised path against one pattern.
8
+ *
9
+ * `*` matches within a segment, `**` spans segments. A pattern is anchored at
10
+ * both ends — `src/*` does not cover `src/a/b`, and nothing covers a path by
11
+ * being a prefix of it. That strictness is the point: an allowlist is read by a
12
+ * person deciding whether to approve it, so a pattern has to mean what it looks
13
+ * like.
14
+ */
15
+ declare function matchesPattern(path: string, pattern: string): boolean;
16
+ interface AllowDecision {
17
+ allowed: boolean;
18
+ /** The pattern that permitted the path, when one did. */
19
+ pattern?: string;
20
+ /** Why the path was refused. */
21
+ reason?: 'malformed' | 'not-allowlisted';
22
+ }
23
+ /**
24
+ * Whether a write to `path` is permitted by `patterns`.
25
+ *
26
+ * `.contentrain/**` is NOT implicit. The content engine's own writes go through
27
+ * the content path with its own invariants; a migration that also wants to
28
+ * write the store asks for it explicitly, and the person approving sees that it
29
+ * does.
30
+ */
31
+ declare function decide(path: string, patterns: readonly string[]): AllowDecision;
32
+ //#endregion
33
+ //#region src/core/migration/writer.d.ts
34
+ /** Branch names a migration may write to. */
35
+ declare const MIGRATION_BRANCH_PREFIX = "migration/";
36
+ interface MigrationScope {
37
+ /**
38
+ * Path patterns this migration may write. `*` matches within a segment,
39
+ * `**` spans segments. Nothing is implicit — not even `.contentrain/**`.
40
+ */
41
+ allow: string[];
42
+ /** The branch every write lands on. Must start with `migration/`. */
43
+ branch: string;
44
+ /** Optional base to fork from. Must not be the content branch. */
45
+ base?: string;
46
+ }
47
+ /**
48
+ * A user's consent, bound to the exact scope they were shown.
49
+ *
50
+ * `scope_hash` is what makes the consent meaningful: an approval of "may write
51
+ * `src/**` on `migration/acme`" cannot be replayed against a scope that also
52
+ * includes `.github/workflows/**`. Compute it with {@link scopeHash} and show
53
+ * the user the scope, not the hash.
54
+ */
55
+ interface MigrationApproval {
56
+ scope_hash: string;
57
+ approver: ActorRef;
58
+ /** ISO 8601 UTC. */
59
+ approved_at: string;
60
+ /** ISO 8601 UTC; consent past this instant is not consent. */
61
+ expires_at?: string;
62
+ }
63
+ /** One write, as it happened. The audit trail is a list of these. */
64
+ interface MigrationAuditEntry {
65
+ /** The step of the migration that produced the write. */
66
+ step: string;
67
+ path: string;
68
+ action: 'write' | 'delete';
69
+ /** The allowlist pattern that permitted it. */
70
+ pattern: string;
71
+ actor: ActorRef;
72
+ /** ISO 8601 UTC. */
73
+ at: string;
74
+ /** The commit the write landed in. */
75
+ commit: string;
76
+ branch: string;
77
+ }
78
+ interface MigrationWriteInput extends ApplyPlanInput {
79
+ /** Names the migration step, for the audit trail. */
80
+ step: string;
81
+ /** Who is writing. Recorded on every entry. */
82
+ actor: ActorRef;
83
+ }
84
+ /**
85
+ * The wrapped writer. `applyPlan` asks for `step` and `actor` because an audit
86
+ * trail without them answers "what changed" but not "who did this, and when" —
87
+ * which is the question a trail exists for. It still satisfies `RepoWriter`, so
88
+ * anything that takes a provider's writer takes this.
89
+ */
90
+ interface MigrationWriter extends RepoWriter {
91
+ applyPlan: (input: MigrationWriteInput) => Promise<Commit>;
92
+ readonly audit: readonly MigrationAuditEntry[];
93
+ }
94
+ declare class MigrationWriteError extends Error {
95
+ readonly code: 'scope_not_approved' | 'approval_expired' | 'branch_not_migration' | 'content_branch_target' | 'path_not_allowed';
96
+ /** The paths that caused a `path_not_allowed` refusal. */
97
+ readonly paths?: string[] | undefined;
98
+ constructor(message: string, code: 'scope_not_approved' | 'approval_expired' | 'branch_not_migration' | 'content_branch_target' | 'path_not_allowed', /** The paths that caused a `path_not_allowed` refusal. */
99
+
100
+ paths?: string[] | undefined);
101
+ }
102
+ /**
103
+ * The identity of a scope: the sorted allowlist, the branch and the base.
104
+ *
105
+ * Sorted so that two callers listing the same patterns in a different order get
106
+ * one hash — the user approved a set of permissions, not an ordering. SHA-256
107
+ * over canonical JSON, matching `computePlanHash` in `@contentrain/types`, so a
108
+ * consumer can recompute it from the published contract.
109
+ */
110
+ declare function scopeHash(scope: MigrationScope): Promise<string>;
111
+ /**
112
+ * Wrap a provider so it will only write a migration's approved scope.
113
+ *
114
+ * The returned writer refuses — before touching the provider — a branch that is
115
+ * not the approved one, any target or base that is the content branch, an
116
+ * approval that does not match the scope or has expired, and any change whose
117
+ * path no pattern covers.
118
+ */
119
+ declare function createMigrationWriter(provider: RepoProvider, scope: MigrationScope, approval: MigrationApproval, audit?: MigrationAuditEntry[]): MigrationWriter;
120
+ //#endregion
121
+ export { type AllowDecision, MIGRATION_BRANCH_PREFIX, type MigrationApproval, type MigrationAuditEntry, type MigrationScope, MigrationWriteError, type MigrationWriteInput, type MigrationWriter, createMigrationWriter, decide, matchesPattern, normalizePath, scopeHash };
122
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/core/migration/allowlist.ts","../../../src/core/migration/writer.ts"],"mappings":";;;;iBA4BgB,aAAA,CAAc,IAAA;;AAA9B;;;;;AAyBA;;;iBAAgB,cAAA,CAAe,IAAA,UAAc,OAAA;AAAA,UAiB5B,aAAA;EACf,OAAA;EAD4B;EAG5B,OAAA;EAH4B;EAK5B,MAAA;AAAA;;;;AAWF;;;;;iBAAgB,MAAA,CAAO,IAAA,UAAc,QAAA,sBAA8B,aAAA;;;;cCrDtD,uBAAA;AAAA,UAEI,cAAA;EDPY;;;;ECY3B,KAAA;EDa4B;ECX5B,MAAA;EDW6B;ECT7B,IAAA;AAAA;;;;;;;;;UAWe,iBAAA;EACf,UAAA;EACA,QAAA,EAAU,QAAA;ED6BoE;EC3B9E,WAAA;ED2BmC;ECzBnC,UAAA;AAAA;;UAIe,mBAAA;;EAEf,IAAA;EACA,IAAA;EACA,MAAA;;EAEA,OAAA;EACA,KAAA,EAAO,QAAA;EArCQ;EAuCf,EAAA;;EAEA,MAAA;EACA,MAAA;AAAA;AAAA,UAGe,mBAAA,SAA4B,cAAA;EApCvC;EAsCJ,IAAA;EA3Be;EA6Bf,KAAA,EAAO,QAAA;AAAA;;;;;;;UASQ,eAAA,SAAwB,UAAA;EACvC,SAAA,GAAY,KAAA,EAAO,mBAAA,KAAwB,OAAA,CAAQ,MAAA;EAAA,SAC1C,KAAA,WAAgB,mBAAA;AAAA;AAAA,cAGd,mBAAA,SAA4B,KAAA;EAAA,SAG5B,IAAA;EAlCX;EAAA,SAyCW,KAAA;cART,OAAA,UACS,IAAA,qHA9BX;;EAqCW,KAAA;AAAA;;;;;;AA5Bb;;;iBA2CsB,SAAA,CAAU,KAAA,EAAO,cAAA,GAAiB,OAAA;;;;;;;AA9BxD;;iBAoDgB,qBAAA,CACd,QAAA,EAAU,YAAA,EACV,KAAA,EAAO,cAAA,EACP,QAAA,EAAU,iBAAA,EACV,KAAA,GAAO,mBAAA,KACN,eAAA"}
@@ -0,0 +1,150 @@
1
+ import { CONTENTRAIN_BRANCH, canonicalStringify } from "@contentrain/types";
2
+ //#region src/core/migration/allowlist.ts
3
+ /** Placeholders used while compiling a glob; neither can occur in a path. */
4
+ const GLOBSTAR_SLASH = "\0GS\0";
5
+ const GLOBSTAR = "\0G\0";
6
+ const CONTROL_CHAR_MAX = 31;
7
+ function hasControlCharacter(value) {
8
+ for (let i = 0; i < value.length; i += 1) if (value.charCodeAt(i) <= CONTROL_CHAR_MAX) return true;
9
+ return false;
10
+ }
11
+ /** A path that is safe to compare against patterns, or undefined. */
12
+ function normalizePath(path) {
13
+ if (path === "" || path.length > 4096) return void 0;
14
+ if (hasControlCharacter(path)) return void 0;
15
+ if (path.includes("\\")) return void 0;
16
+ if (path.includes("%")) return void 0;
17
+ if (path.startsWith("/")) return void 0;
18
+ const segments = path.split("/");
19
+ if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) return void 0;
20
+ return segments.join("/");
21
+ }
22
+ /**
23
+ * Match one normalised path against one pattern.
24
+ *
25
+ * `*` matches within a segment, `**` spans segments. A pattern is anchored at
26
+ * both ends — `src/*` does not cover `src/a/b`, and nothing covers a path by
27
+ * being a prefix of it. That strictness is the point: an allowlist is read by a
28
+ * person deciding whether to approve it, so a pattern has to mean what it looks
29
+ * like.
30
+ */
31
+ function matchesPattern(path, pattern) {
32
+ if (normalizePath(pattern.replaceAll("*", "x")) === void 0) return false;
33
+ const escaped = pattern.replace(/[.+^${}()|[\]]/g, "\\$&").replaceAll("**/", GLOBSTAR_SLASH).replaceAll("**", GLOBSTAR).replaceAll("*", "[^/]*").replaceAll(GLOBSTAR_SLASH, "(?:[^/]+/)*").replaceAll(GLOBSTAR, ".*");
34
+ return new RegExp(`^${escaped}$`).test(path);
35
+ }
36
+ /**
37
+ * Whether a write to `path` is permitted by `patterns`.
38
+ *
39
+ * `.contentrain/**` is NOT implicit. The content engine's own writes go through
40
+ * the content path with its own invariants; a migration that also wants to
41
+ * write the store asks for it explicitly, and the person approving sees that it
42
+ * does.
43
+ */
44
+ function decide(path, patterns) {
45
+ const normalized = normalizePath(path);
46
+ if (normalized === void 0) return {
47
+ allowed: false,
48
+ reason: "malformed"
49
+ };
50
+ for (const pattern of patterns) if (matchesPattern(normalized, pattern)) return {
51
+ allowed: true,
52
+ pattern
53
+ };
54
+ return {
55
+ allowed: false,
56
+ reason: "not-allowlisted"
57
+ };
58
+ }
59
+ //#endregion
60
+ //#region src/core/migration/writer.ts
61
+ /** Branch names a migration may write to. */
62
+ const MIGRATION_BRANCH_PREFIX = "migration/";
63
+ var MigrationWriteError = class extends Error {
64
+ constructor(message, code, paths) {
65
+ super(message);
66
+ this.code = code;
67
+ this.paths = paths;
68
+ this.name = "MigrationWriteError";
69
+ }
70
+ };
71
+ /**
72
+ * The identity of a scope: the sorted allowlist, the branch and the base.
73
+ *
74
+ * Sorted so that two callers listing the same patterns in a different order get
75
+ * one hash — the user approved a set of permissions, not an ordering. SHA-256
76
+ * over canonical JSON, matching `computePlanHash` in `@contentrain/types`, so a
77
+ * consumer can recompute it from the published contract.
78
+ */
79
+ async function scopeHash(scope) {
80
+ const payload = canonicalStringify({
81
+ allow: [...new Set(scope.allow)].toSorted(),
82
+ branch: scope.branch,
83
+ base: scope.base
84
+ });
85
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(payload));
86
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
87
+ }
88
+ function isContentBranch(name) {
89
+ return name === CONTENTRAIN_BRANCH;
90
+ }
91
+ /**
92
+ * Wrap a provider so it will only write a migration's approved scope.
93
+ *
94
+ * The returned writer refuses — before touching the provider — a branch that is
95
+ * not the approved one, any target or base that is the content branch, an
96
+ * approval that does not match the scope or has expired, and any change whose
97
+ * path no pattern covers.
98
+ */
99
+ function createMigrationWriter(provider, scope, approval, audit = []) {
100
+ let verified;
101
+ const assertScope = async (now) => {
102
+ verified ??= await scopeHash(scope);
103
+ if (approval.scope_hash !== verified) throw new MigrationWriteError("The approval was given for a different scope. Show the user the current allowlist and branch, and collect consent again.", "scope_not_approved");
104
+ if (approval.expires_at && approval.expires_at <= now) throw new MigrationWriteError(`The approval expired at ${approval.expires_at}.`, "approval_expired");
105
+ if (!scope.branch.startsWith("migration/")) throw new MigrationWriteError(`A migration writes to "${MIGRATION_BRANCH_PREFIX}*"; "${scope.branch}" is not one.`, "branch_not_migration");
106
+ if (isContentBranch(scope.branch) || isContentBranch(scope.base)) throw new MigrationWriteError(`A migration never targets or forks the "${CONTENTRAIN_BRANCH}" branch — that ref is the content state's single source of truth.`, "content_branch_target");
107
+ };
108
+ return {
109
+ get audit() {
110
+ return audit;
111
+ },
112
+ async applyPlan(input) {
113
+ const now = (/* @__PURE__ */ new Date()).toISOString();
114
+ await assertScope(now);
115
+ if (input.branch !== scope.branch) throw new MigrationWriteError(`The approval covers "${scope.branch}"; this write targets "${input.branch}".`, "branch_not_migration");
116
+ if (isContentBranch(input.branch) || isContentBranch(input.base)) throw new MigrationWriteError(`A migration never targets or forks the "${CONTENTRAIN_BRANCH}" branch.`, "content_branch_target");
117
+ const decisions = input.changes.map((change) => ({
118
+ change,
119
+ decision: decide(change.path, scope.allow)
120
+ }));
121
+ const refused = decisions.filter((entry) => !entry.decision.allowed);
122
+ if (refused.length) {
123
+ const paths = refused.map((entry) => entry.change.path);
124
+ throw new MigrationWriteError(`${paths.length} path${paths.length === 1 ? "" : "s"} outside the approved scope: ${paths.slice(0, 5).join(", ")}${paths.length > 5 ? ", …" : ""}`, "path_not_allowed", paths);
125
+ }
126
+ const commit = await provider.applyPlan({
127
+ ...input,
128
+ base: input.base ?? scope.base
129
+ });
130
+ for (const { change, decision } of decisions) audit.push({
131
+ step: input.step ?? "unnamed",
132
+ path: change.path,
133
+ action: change.content === null ? "delete" : "write",
134
+ pattern: decision.pattern,
135
+ actor: input.actor ?? {
136
+ kind: "system",
137
+ id: "migration"
138
+ },
139
+ at: now,
140
+ commit: commit.sha,
141
+ branch: input.branch
142
+ });
143
+ return commit;
144
+ }
145
+ };
146
+ }
147
+ //#endregion
148
+ export { MIGRATION_BRANCH_PREFIX, MigrationWriteError, createMigrationWriter, decide, matchesPattern, normalizePath, scopeHash };
149
+
150
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/core/migration/allowlist.ts","../../../src/core/migration/writer.ts"],"sourcesContent":["// ─── Path allowlisting ───\n//\n// The security boundary for migration writes. A migration writes `src/`,\n// `public/`, `package.json`, a lockfile and deploy configuration — everything\n// the content engine deliberately never touches — so the guarantee cannot come\n// from \"only `.contentrain/`\" any more. It comes from a list the user saw and\n// approved.\n//\n// Which makes this matcher a security control, not a convenience. It is written\n// to fail closed: anything it cannot confidently normalise is denied, and the\n// tricks that defeat naive matchers — traversal, absolute paths, backslashes,\n// percent-encoded separators, a leading `./` — are rejected before a pattern is\n// even consulted.\n\n/** Placeholders used while compiling a glob; neither can occur in a path. */\nconst GLOBSTAR_SLASH = '\u0000GS\u0000'\nconst GLOBSTAR = '\u0000G\u0000'\n\nconst CONTROL_CHAR_MAX = 0x1f\n\nfunction hasControlCharacter(value: string): boolean {\n for (let i = 0; i < value.length; i += 1) {\n if (value.charCodeAt(i) <= CONTROL_CHAR_MAX) return true\n }\n return false\n}\n\n/** A path that is safe to compare against patterns, or undefined. */\nexport function normalizePath(path: string): string | undefined {\n if (path === '' || path.length > 4096) return undefined\n // Control characters, including the NUL that truncates a C-side path.\n if (hasControlCharacter(path)) return undefined\n // A backslash is a separator on Windows and a literal elsewhere; treating it\n // as either makes the two platforms disagree about what a pattern covers.\n if (path.includes('\\\\')) return undefined\n // `%2e%2e` and friends: this layer compares raw repository paths, so a path\n // arriving percent-encoded is either a bug or an attempt.\n if (path.includes('%')) return undefined\n if (path.startsWith('/')) return undefined\n const segments = path.split('/')\n if (segments.some(segment => segment === '' || segment === '.' || segment === '..')) return undefined\n return segments.join('/')\n}\n\n/**\n * Match one normalised path against one pattern.\n *\n * `*` matches within a segment, `**` spans segments. A pattern is anchored at\n * both ends — `src/*` does not cover `src/a/b`, and nothing covers a path by\n * being a prefix of it. That strictness is the point: an allowlist is read by a\n * person deciding whether to approve it, so a pattern has to mean what it looks\n * like.\n */\nexport function matchesPattern(path: string, pattern: string): boolean {\n // A pattern is held to the same shape as a path, with `*` standing in for a\n // name. A pattern that could not be a path cannot describe one.\n if (normalizePath(pattern.replaceAll('*', 'x')) === undefined) return false\n\n const escaped = pattern\n .replace(/[.+^${}()|[\\]]/g, '\\\\$&')\n // `**/` may also match nothing, so `src/**/x.ts` covers `src/x.ts`.\n .replaceAll('**/', GLOBSTAR_SLASH)\n .replaceAll('**', GLOBSTAR)\n .replaceAll('*', '[^/]*')\n .replaceAll(GLOBSTAR_SLASH, '(?:[^/]+/)*')\n .replaceAll(GLOBSTAR, '.*')\n\n return new RegExp(`^${escaped}$`).test(path)\n}\n\nexport interface AllowDecision {\n allowed: boolean\n /** The pattern that permitted the path, when one did. */\n pattern?: string\n /** Why the path was refused. */\n reason?: 'malformed' | 'not-allowlisted'\n}\n\n/**\n * Whether a write to `path` is permitted by `patterns`.\n *\n * `.contentrain/**` is NOT implicit. The content engine's own writes go through\n * the content path with its own invariants; a migration that also wants to\n * write the store asks for it explicitly, and the person approving sees that it\n * does.\n */\nexport function decide(path: string, patterns: readonly string[]): AllowDecision {\n const normalized = normalizePath(path)\n if (normalized === undefined) return { allowed: false, reason: 'malformed' }\n for (const pattern of patterns) {\n if (matchesPattern(normalized, pattern)) return { allowed: true, pattern }\n }\n return { allowed: false, reason: 'not-allowlisted' }\n}\n","// ─── The allowlisted migration write path ───\n//\n// A migration does not deliver content, it delivers a codebase: `src/`,\n// `public/`, `package.json`, a lockfile, deploy configuration. None of that is\n// what the content engine's write path was built for, and widening that path\n// would quietly move a security boundary the whole product rests on.\n//\n// So this is a wrapper, not a provider. `GitHubProvider.applyPlan` can already\n// write any path; what was missing was a gate in front of it. The gate carries\n// every requirement the repository-boundary contract asks for:\n//\n// scope a file allowlist; nothing outside it is written\n// consent one explicit approval, bound to the exact scope it was shown\n// branch a `migration/*` ref; the content branch is never a target or base\n// trail which file, in which step, by which actor, in which commit\n// undo one branch, delivered as one PR, revertable in one `git revert`\n//\n// Every refusal happens before the provider is called. A rejected plan writes\n// nothing at all — not the allowed part of it — because a half-applied\n// migration is harder to reason about than one that did not start.\n\nimport type {\n ActorRef,\n ApplyPlanInput,\n Commit,\n FileChange,\n RepoProvider,\n RepoWriter,\n} from '@contentrain/types'\nimport { CONTENTRAIN_BRANCH, canonicalStringify } from '@contentrain/types'\nimport { decide } from './allowlist.js'\n\n/** Branch names a migration may write to. */\nexport const MIGRATION_BRANCH_PREFIX = 'migration/'\n\nexport interface MigrationScope {\n /**\n * Path patterns this migration may write. `*` matches within a segment,\n * `**` spans segments. Nothing is implicit — not even `.contentrain/**`.\n */\n allow: string[]\n /** The branch every write lands on. Must start with `migration/`. */\n branch: string\n /** Optional base to fork from. Must not be the content branch. */\n base?: string\n}\n\n/**\n * A user's consent, bound to the exact scope they were shown.\n *\n * `scope_hash` is what makes the consent meaningful: an approval of \"may write\n * `src/**` on `migration/acme`\" cannot be replayed against a scope that also\n * includes `.github/workflows/**`. Compute it with {@link scopeHash} and show\n * the user the scope, not the hash.\n */\nexport interface MigrationApproval {\n scope_hash: string\n approver: ActorRef\n /** ISO 8601 UTC. */\n approved_at: string\n /** ISO 8601 UTC; consent past this instant is not consent. */\n expires_at?: string\n}\n\n/** One write, as it happened. The audit trail is a list of these. */\nexport interface MigrationAuditEntry {\n /** The step of the migration that produced the write. */\n step: string\n path: string\n action: 'write' | 'delete'\n /** The allowlist pattern that permitted it. */\n pattern: string\n actor: ActorRef\n /** ISO 8601 UTC. */\n at: string\n /** The commit the write landed in. */\n commit: string\n branch: string\n}\n\nexport interface MigrationWriteInput extends ApplyPlanInput {\n /** Names the migration step, for the audit trail. */\n step: string\n /** Who is writing. Recorded on every entry. */\n actor: ActorRef\n}\n\n/**\n * The wrapped writer. `applyPlan` asks for `step` and `actor` because an audit\n * trail without them answers \"what changed\" but not \"who did this, and when\" —\n * which is the question a trail exists for. It still satisfies `RepoWriter`, so\n * anything that takes a provider's writer takes this.\n */\nexport interface MigrationWriter extends RepoWriter {\n applyPlan: (input: MigrationWriteInput) => Promise<Commit>\n readonly audit: readonly MigrationAuditEntry[]\n}\n\nexport class MigrationWriteError extends Error {\n constructor(\n message: string,\n readonly code:\n | 'scope_not_approved'\n | 'approval_expired'\n | 'branch_not_migration'\n | 'content_branch_target'\n | 'path_not_allowed',\n /** The paths that caused a `path_not_allowed` refusal. */\n readonly paths?: string[],\n ) {\n super(message)\n this.name = 'MigrationWriteError'\n }\n}\n\n/**\n * The identity of a scope: the sorted allowlist, the branch and the base.\n *\n * Sorted so that two callers listing the same patterns in a different order get\n * one hash — the user approved a set of permissions, not an ordering. SHA-256\n * over canonical JSON, matching `computePlanHash` in `@contentrain/types`, so a\n * consumer can recompute it from the published contract.\n */\nexport async function scopeHash(scope: MigrationScope): Promise<string> {\n const payload = canonicalStringify({\n allow: [...new Set(scope.allow)].toSorted(),\n branch: scope.branch,\n base: scope.base,\n })\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(payload))\n return [...new Uint8Array(digest)].map(byte => byte.toString(16).padStart(2, '0')).join('')\n}\n\nfunction isContentBranch(name: string | undefined): boolean {\n return name === CONTENTRAIN_BRANCH\n}\n\n/**\n * Wrap a provider so it will only write a migration's approved scope.\n *\n * The returned writer refuses — before touching the provider — a branch that is\n * not the approved one, any target or base that is the content branch, an\n * approval that does not match the scope or has expired, and any change whose\n * path no pattern covers.\n */\nexport function createMigrationWriter(\n provider: RepoProvider,\n scope: MigrationScope,\n approval: MigrationApproval,\n audit: MigrationAuditEntry[] = [],\n): MigrationWriter {\n let verified: string | undefined\n\n const assertScope = async (now: string): Promise<void> => {\n verified ??= await scopeHash(scope)\n if (approval.scope_hash !== verified) {\n throw new MigrationWriteError(\n 'The approval was given for a different scope. Show the user the current allowlist and branch, and collect consent again.',\n 'scope_not_approved',\n )\n }\n if (approval.expires_at && approval.expires_at <= now) {\n throw new MigrationWriteError(`The approval expired at ${approval.expires_at}.`, 'approval_expired')\n }\n if (!scope.branch.startsWith(MIGRATION_BRANCH_PREFIX)) {\n throw new MigrationWriteError(\n `A migration writes to \"${MIGRATION_BRANCH_PREFIX}*\"; \"${scope.branch}\" is not one.`,\n 'branch_not_migration',\n )\n }\n if (isContentBranch(scope.branch) || isContentBranch(scope.base)) {\n throw new MigrationWriteError(\n `A migration never targets or forks the \"${CONTENTRAIN_BRANCH}\" branch — that ref is the content state's single source of truth.`,\n 'content_branch_target',\n )\n }\n }\n\n return {\n get audit() {\n return audit\n },\n\n async applyPlan(input: MigrationWriteInput): Promise<Commit> {\n const now = new Date().toISOString()\n await assertScope(now)\n\n if (input.branch !== scope.branch) {\n throw new MigrationWriteError(\n `The approval covers \"${scope.branch}\"; this write targets \"${input.branch}\".`,\n 'branch_not_migration',\n )\n }\n if (isContentBranch(input.branch) || isContentBranch(input.base)) {\n throw new MigrationWriteError(\n `A migration never targets or forks the \"${CONTENTRAIN_BRANCH}\" branch.`,\n 'content_branch_target',\n )\n }\n\n // Decide every path before writing any of them. A plan that is partly\n // outside the scope is refused whole: a migration that wrote half its\n // files is worse to recover from than one that wrote none.\n const decisions = input.changes.map((change: FileChange) => ({ change, decision: decide(change.path, scope.allow) }))\n const refused = decisions.filter(entry => !entry.decision.allowed)\n if (refused.length) {\n const paths = refused.map(entry => entry.change.path)\n throw new MigrationWriteError(\n `${paths.length} path${paths.length === 1 ? '' : 's'} outside the approved scope: ${paths.slice(0, 5).join(', ')}${paths.length > 5 ? ', …' : ''}`,\n 'path_not_allowed',\n paths,\n )\n }\n\n // `base` defaults to the content branch on `ApplyPlanInput`, which is\n // exactly what a migration must not fork. Make the intent explicit rather\n // than inheriting an invariant written for content.\n const commit = await provider.applyPlan({ ...input, base: input.base ?? scope.base })\n\n for (const { change, decision } of decisions) {\n audit.push({\n step: input.step ?? 'unnamed',\n path: change.path,\n action: change.content === null ? 'delete' : 'write',\n pattern: decision.pattern!,\n actor: input.actor ?? { kind: 'system', id: 'migration' },\n at: now,\n commit: commit.sha,\n branch: input.branch,\n })\n }\n\n return commit\n },\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { _ as RepoReader } from "../index-ByrVsRJE.mjs";
1
+ import { _ as RepoReader } from "../index-A5iw3yDk.mjs";
2
2
  import { MODEL_FIELD_ORDER, ModelDefinition, ModelSummary, ModelSummary as ModelSummary$1 } from "@contentrain/types";
3
3
  import { z } from "zod";
4
4
 
@@ -1,6 +1,6 @@
1
- import { _ as RepoReader, a as FileChange } from "../../index-ByrVsRJE.mjs";
2
- import { t as ContentEntry } from "../../content-manager-9yUO24W2.mjs";
3
- import { a as ContentSaveEntryResult, i as ReconcileResult, n as ReconcileInput, o as ContentSavePlan, r as ReconcilePlan, s as OpPlan, t as planReconcile } from "../../index-_HJVkDJa.mjs";
1
+ import { _ as RepoReader, a as FileChange } from "../../index-A5iw3yDk.mjs";
2
+ import { t as ContentEntry } from "../../content-manager-BqaMkMVk.mjs";
3
+ import { a as ContentSaveEntryResult, i as ReconcileResult, n as ReconcileInput, o as ContentSavePlan, r as ReconcilePlan, s as OpPlan, t as planReconcile } from "../../index-DKZNUqpP.mjs";
4
4
  import { ContentrainConfig, ModelDefinition, Vocabulary } from "@contentrain/types";
5
5
 
6
6
  //#region src/core/ops/apply-to-worktree.d.ts
@@ -1,4 +1,4 @@
1
- import { _ as RepoReader, a as FileChange } from "../index-ByrVsRJE.mjs";
1
+ import { _ as RepoReader, a as FileChange } from "../index-A5iw3yDk.mjs";
2
2
 
3
3
  //#region src/core/overlay-reader.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { _ as RepoReader } from "../../index-ByrVsRJE.mjs";
1
+ import { _ as RepoReader } from "../../index-A5iw3yDk.mjs";
2
2
  import { EntryMeta, FieldDef, ModelDefinition, ValidationError, ValidationResult } from "@contentrain/types";
3
3
 
4
4
  //#region src/core/validator/entry.d.ts
@@ -1,4 +1,4 @@
1
- import { r as ReconcilePlan } from "../index-_HJVkDJa.mjs";
1
+ import { r as ReconcilePlan } from "../index-DKZNUqpP.mjs";
2
2
  import { BaseAdvance, ConflictResolution, ContextSource, RemotePush, SyncResult } from "@contentrain/types";
3
3
 
4
4
  //#region src/git/reconcile.d.ts
@@ -0,0 +1,2 @@
1
+ import { ApplyPlanInput as ApplyPlanInput$1, Branch, Commit as Commit$1, CommitAuthor, FileChange, FileDiff, LOCAL_CAPABILITIES, MediaAsset, MediaIngestInput, MediaListOptions, MediaListResult, MediaProvider, MediaUpdateInput, MergeResult, ProviderCapabilities as ProviderCapabilities$1, RepoProvider as RepoProvider$1, RepoReader, RepoWriter as RepoWriter$1, WriteReadiness } from "@contentrain/types";
2
+ export { RepoReader as _, FileChange as a, MediaAsset as c, MediaListResult as d, MediaProvider as f, RepoProvider$1 as g, ProviderCapabilities$1 as h, CommitAuthor as i, MediaIngestInput as l, MergeResult as m, Branch as n, FileDiff as o, MediaUpdateInput as p, Commit$1 as r, LOCAL_CAPABILITIES as s, ApplyPlanInput$1 as t, MediaListOptions as u, RepoWriter$1 as v, WriteReadiness as y };
@@ -1,4 +1,4 @@
1
- import { _ as RepoReader, a as FileChange } from "./index-ByrVsRJE.mjs";
1
+ import { _ as RepoReader, a as FileChange } from "./index-A5iw3yDk.mjs";
2
2
  import { ConflictItem, ConflictResolution, ContextSource } from "@contentrain/types";
3
3
 
4
4
  //#region src/core/ops/types.d.ts
@@ -82,4 +82,4 @@ interface ReconcilePlan extends OpPlan<ReconcileResult> {
82
82
  declare function planReconcile(input: ReconcileInput): Promise<ReconcilePlan>;
83
83
  //#endregion
84
84
  export { ContentSaveEntryResult as a, ReconcileResult as i, ReconcileInput as n, ContentSavePlan as o, ReconcilePlan as r, OpPlan as s, planReconcile as t };
85
- //# sourceMappingURL=index-_HJVkDJa.d.mts.map
85
+ //# sourceMappingURL=index-DKZNUqpP.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-_HJVkDJa.d.mts","names":[],"sources":["../src/core/ops/types.ts","../src/core/ops/reconcile/types.ts","../src/core/ops/reconcile/plan.ts"],"mappings":";;;;;;;AAUA;;;;;UAAiB,MAAA;EAEN;EAAT,OAAA,EAAS,UAAA;EAED;EAAR,MAAA,EAAQ,OAAA;EAEE;EAAV,UAAA;AAAA;AAAA,UAGe,sBAAA;EACf,MAAA;EACA,EAAA;EACA,IAAA;EACA,MAAA;EACA,UAAA;AAAA;AAAA,KAGU,eAAA,GAAkB,MAAA,CAAO,sBAAA;;;AAjBrC;;;;;;;;AAAA,UCGiB,cAAA;EACf,IAAA,EAAM,UAAA;EACN,IAAA,EAAM,UAAA;EACN,MAAA,EAAQ,UAAA;EDGO;ECDf,WAAA,GAAc,kBAAA;;EAEd,MAAA,GAAS,aAAA;AAAA;AAAA,UAGM,eAAA;EDDf;ECGA,YAAA;EDDA;ECGA,kBAAA;EACA,oBAAA;EDDU;ECGV,oBAAA;;EAEA,WAAA;AAAA;;;;AAnBF;;;UA4BiB,aAAA,SAAsB,MAAA,CAAO,eAAA;EAC5C,SAAA,EAAW,YAAA;AAAA;;;;;;ADhCb;;;;;;;;;iBEkDsB,aAAA,CAAc,KAAA,EAAO,cAAA,GAAiB,OAAA,CAAQ,aAAA"}
1
+ {"version":3,"file":"index-DKZNUqpP.d.mts","names":[],"sources":["../src/core/ops/types.ts","../src/core/ops/reconcile/types.ts","../src/core/ops/reconcile/plan.ts"],"mappings":";;;;;;;AAUA;;;;;UAAiB,MAAA;EAEN;EAAT,OAAA,EAAS,UAAA;EAED;EAAR,MAAA,EAAQ,OAAA;EAEE;EAAV,UAAA;AAAA;AAAA,UAGe,sBAAA;EACf,MAAA;EACA,EAAA;EACA,IAAA;EACA,MAAA;EACA,UAAA;AAAA;AAAA,KAGU,eAAA,GAAkB,MAAA,CAAO,sBAAA;;;AAjBrC;;;;;;;;AAAA,UCGiB,cAAA;EACf,IAAA,EAAM,UAAA;EACN,IAAA,EAAM,UAAA;EACN,MAAA,EAAQ,UAAA;EDGO;ECDf,WAAA,GAAc,kBAAA;;EAEd,MAAA,GAAS,aAAA;AAAA;AAAA,UAGM,eAAA;EDDf;ECGA,YAAA;EDDA;ECGA,kBAAA;EACA,oBAAA;EDDU;ECGV,oBAAA;;EAEA,WAAA;AAAA;;;;AAnBF;;;UA4BiB,aAAA,SAAsB,MAAA,CAAO,eAAA;EAC5C,SAAA,EAAW,YAAA;AAAA;;;;;;ADhCb;;;;;;;;;iBEkDsB,aAAA,CAAc,KAAA,EAAO,cAAA,GAAiB,OAAA,CAAQ,aAAA"}
package/dist/index.mjs CHANGED
@@ -15,7 +15,7 @@ import "./transaction-BR2jg4uj.mjs";
15
15
  import "./local-DbPlR0Br.mjs";
16
16
  import "./detect-CI32ynpq.mjs";
17
17
  import "./annotations-CXHPnS-M.mjs";
18
- import { n as createServer } from "./server-BgyNDHvp.mjs";
18
+ import { n as createServer } from "./server-Do2BcSBg.mjs";
19
19
  import "./errors-BmLE-JSC.mjs";
20
20
  import "./reconcile-EANDIp_T.mjs";
21
21
  import "./scan-config-C5zzygeH.mjs";
@@ -1,4 +1,4 @@
1
- import { _ as RepoReader, a as FileChange, g as RepoProvider, h as ProviderCapabilities, i as CommitAuthor, m as MergeResult, n as Branch, o as FileDiff, r as Commit, t as ApplyPlanInput } from "../../index-ByrVsRJE.mjs";
1
+ import { _ as RepoReader, a as FileChange, g as RepoProvider, h as ProviderCapabilities, i as CommitAuthor, m as MergeResult, n as Branch, o as FileDiff, r as Commit, t as ApplyPlanInput } from "../../index-A5iw3yDk.mjs";
2
2
  import { Octokit } from "@octokit/rest";
3
3
 
4
4
  //#region src/providers/github/app-auth.d.ts
@@ -1,4 +1,4 @@
1
- import { _ as RepoReader, g as RepoProvider, h as ProviderCapabilities, i as CommitAuthor, m as MergeResult, n as Branch, o as FileDiff, r as Commit, t as ApplyPlanInput } from "../../index-ByrVsRJE.mjs";
1
+ import { _ as RepoReader, g as RepoProvider, h as ProviderCapabilities, i as CommitAuthor, m as MergeResult, n as Branch, o as FileDiff, r as Commit, t as ApplyPlanInput } from "../../index-A5iw3yDk.mjs";
2
2
  import { Gitlab } from "@gitbeaker/rest";
3
3
 
4
4
  //#region src/providers/gitlab/capabilities.d.ts
@@ -1,4 +1,4 @@
1
- import { _ as RepoReader, a as FileChange, g as RepoProvider, h as ProviderCapabilities$1, i as CommitAuthor, m as MergeResult, n as Branch, o as FileDiff, r as Commit, y as WriteReadiness } from "../../index-ByrVsRJE.mjs";
1
+ import { _ as RepoReader, a as FileChange, g as RepoProvider$1, h as ProviderCapabilities$1, i as CommitAuthor, m as MergeResult, n as Branch, o as FileDiff, r as Commit$1, y as WriteReadiness } from "../../index-A5iw3yDk.mjs";
2
2
  import { BaseAdvance, RemotePush, SyncResult, WorkflowMode } from "@contentrain/types";
3
3
 
4
4
  //#region src/providers/local/types.d.ts
@@ -34,7 +34,7 @@ interface LocalApplyPlanInput {
34
34
  /** Override workflow for this call; defaults to the project's configured workflow. */
35
35
  workflowOverride?: WorkflowMode;
36
36
  }
37
- interface LocalApplyResult extends Commit {
37
+ interface LocalApplyResult extends Commit$1 {
38
38
  /**
39
39
  * `auto-merged` — changes fast-forwarded onto the base branch and the
40
40
  * developer's working tree was selectively re-synced.
@@ -72,7 +72,7 @@ interface LocalApplyResult extends Commit {
72
72
  * `update-ref` in `transaction.mergeBranch`, so arbitrary merge targets
73
73
  * would bypass that invariant.
74
74
  */
75
- declare class LocalProvider implements RepoProvider {
75
+ declare class LocalProvider implements RepoProvider$1 {
76
76
  readonly projectRoot: string;
77
77
  readonly capabilities: ProviderCapabilities$1;
78
78
  private readonly reader;
@@ -109,7 +109,7 @@ declare class LocalProvider implements RepoProvider {
109
109
  changes: FileChange[];
110
110
  message: string;
111
111
  author: CommitAuthor;
112
- }): Promise<Commit>;
112
+ }): Promise<Commit$1>;
113
113
  }
114
114
  //#endregion
115
115
  //#region src/providers/local/reader.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/providers/local/types.ts","../../../src/providers/local/provider.ts","../../../src/providers/local/reader.ts"],"mappings":";;;;;UAIiB,kBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;EACA,OAAA;AAAA;;;;;;AAYF;;;;UAAiB,mBAAA;EAYL;EAVV,MAAA;EAY+B;EAV/B,OAAA,EAAS,UAAA;EAFT;EAIA,OAAA;EAFS;EAIT,MAAA,GAAS,YAAA;EAAT;EAEA,IAAA;EAAA;EAEA,OAAA,GAAU,kBAAA;EAAA;EAEV,gBAAA,GAAmB,YAAA;AAAA;AAAA,UAGJ,gBAAA,SAAyB,MAAA;EAHT;AAGjC;;;;;EAOE,cAAA;EAPwC;EASxC,IAAA,GAAO,UAAA;EATuC;EAW9C,OAAA;EAJA;;;;;EAUA,YAAA,GAAe,WAAA;EAEf;EAAA,WAAA,GAAc,UAAA;AAAA;;;;;AApDhB;;;;;;;;;;AAgBA;;;cC8Ba,aAAA,YAAyB,YAAA;EAAA,SAIR,WAAA;EAAA,SAHnB,YAAA,EAAc,sBAAA;EAAA,iBACN,MAAA;cAEW,WAAA;EAI5B,QAAA,CAAS,IAAA,UAAc,GAAA,YAAe,OAAA;EAItC,aAAA,CAAc,IAAA,UAAc,GAAA,YAAe,OAAA;EAI3C,UAAA,CAAW,IAAA,UAAc,GAAA,YAAe,OAAA;ED1C/B;;;;;ECmDH,mBAAA,CAAA,GAAuB,OAAA,CAAQ,cAAA;EAO/B,SAAA,CAAU,KAAA,EAAO,mBAAA,GAAsB,OAAA,CAAQ,gBAAA;EA6BrD,YAAA,CAAa,MAAA,YAAkB,OAAA,CAAQ,MAAA;EAIjC,YAAA,CAAa,IAAA,UAAc,OAAA,YAAmB,OAAA;EAKpD,YAAA,CAAa,IAAA,WAAe,OAAA;EAI5B,aAAA,CAAc,MAAA,UAAgB,IAAA,YAAgB,OAAA,CAAQ,QAAA;EAKtD,WAAA,CAAY,MAAA,UAAgB,IAAA,WAAe,OAAA,CAAQ,WAAA;EAInD,QAAA,CAAS,MAAA,UAAgB,IAAA,YAAgB,OAAA;EAKzC,gBAAA,CAAA,GAAoB,OAAA;EAId,YAAA,CAAa,IAAA,UAAc,IAAA,WAAe,OAAA;EDxFjC;;;;;;;ECwGT,iBAAA,CAAkB,KAAA;IACtB,MAAA;IACA,IAAA;IACA,MAAA;IACA,OAAA,EAAS,UAAA;IACT,OAAA;IACA,MAAA,EAAQ,YAAA;EAAA,IACN,OAAA,CAAQ,MAAA;AAAA;;;;;;ADjKd;;;;;;;;cEWa,WAAA,YAAuB,UAAA;EAAA,SACN,WAAA;cAAA,WAAA;EAEtB,QAAA,CAAS,IAAA,UAAc,IAAA,YAAgB,OAAA;EAIvC,aAAA,CAAc,IAAA,UAAc,IAAA,YAAgB,OAAA;EAQ5C,UAAA,CAAW,IAAA,UAAc,IAAA,YAAgB,OAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/providers/local/types.ts","../../../src/providers/local/provider.ts","../../../src/providers/local/reader.ts"],"mappings":";;;;;UAIiB,kBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;EACA,OAAA;AAAA;;;;;;AAYF;;;;UAAiB,mBAAA;EAYL;EAVV,MAAA;EAY+B;EAV/B,OAAA,EAAS,UAAA;EAFT;EAIA,OAAA;EAFS;EAIT,MAAA,GAAS,YAAA;EAAT;EAEA,IAAA;EAAA;EAEA,OAAA,GAAU,kBAAA;EAAA;EAEV,gBAAA,GAAmB,YAAA;AAAA;AAAA,UAGJ,gBAAA,SAAyB,QAAA;EAHT;AAGjC;;;;;EAOE,cAAA;EAPwC;EASxC,IAAA,GAAO,UAAA;EATuC;EAW9C,OAAA;EAJA;;;;;EAUA,YAAA,GAAe,WAAA;EAEf;EAAA,WAAA,GAAc,UAAA;AAAA;;;;;AApDhB;;;;;;;;;;AAgBA;;;cC8Ba,aAAA,YAAyB,cAAA;EAAA,SAIR,WAAA;EAAA,SAHnB,YAAA,EAAc,sBAAA;EAAA,iBACN,MAAA;cAEW,WAAA;EAI5B,QAAA,CAAS,IAAA,UAAc,GAAA,YAAe,OAAA;EAItC,aAAA,CAAc,IAAA,UAAc,GAAA,YAAe,OAAA;EAI3C,UAAA,CAAW,IAAA,UAAc,GAAA,YAAe,OAAA;ED1C/B;;;;;ECmDH,mBAAA,CAAA,GAAuB,OAAA,CAAQ,cAAA;EAO/B,SAAA,CAAU,KAAA,EAAO,mBAAA,GAAsB,OAAA,CAAQ,gBAAA;EA6BrD,YAAA,CAAa,MAAA,YAAkB,OAAA,CAAQ,MAAA;EAIjC,YAAA,CAAa,IAAA,UAAc,OAAA,YAAmB,OAAA;EAKpD,YAAA,CAAa,IAAA,WAAe,OAAA;EAI5B,aAAA,CAAc,MAAA,UAAgB,IAAA,YAAgB,OAAA,CAAQ,QAAA;EAKtD,WAAA,CAAY,MAAA,UAAgB,IAAA,WAAe,OAAA,CAAQ,WAAA;EAInD,QAAA,CAAS,MAAA,UAAgB,IAAA,YAAgB,OAAA;EAKzC,gBAAA,CAAA,GAAoB,OAAA;EAId,YAAA,CAAa,IAAA,UAAc,IAAA,WAAe,OAAA;EDxFjC;;;;;;;ECwGT,iBAAA,CAAkB,KAAA;IACtB,MAAA;IACA,IAAA;IACA,MAAA;IACA,OAAA,EAAS,UAAA;IACT,OAAA;IACA,MAAA,EAAQ,YAAA;EAAA,IACN,OAAA,CAAQ,QAAA;AAAA;;;;;;ADjKd;;;;;;;;cEWa,WAAA,YAAuB,UAAA;EAAA,SACN,WAAA;cAAA,WAAA;EAEtB,QAAA,CAAS,IAAA,UAAc,IAAA,YAAgB,OAAA;EAIvC,aAAA,CAAc,IAAA,UAAc,IAAA,YAAgB,OAAA;EAQ5C,UAAA,CAAW,IAAA,UAAc,IAAA,YAAgB,OAAA;AAAA"}
@@ -1,4 +1,4 @@
1
- import { r as ToolProvider } from "../../server-SDsD0XGM.mjs";
1
+ import { r as ToolProvider } from "../../server-CUtmhlno.mjs";
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import http from "node:http";
4
4
 
@@ -14,7 +14,7 @@ import "../../transaction-BR2jg4uj.mjs";
14
14
  import "../../local-DbPlR0Br.mjs";
15
15
  import "../../detect-CI32ynpq.mjs";
16
16
  import "../../annotations-CXHPnS-M.mjs";
17
- import { n as createServer } from "../../server-BgyNDHvp.mjs";
17
+ import { n as createServer } from "../../server-Do2BcSBg.mjs";
18
18
  import "../../errors-BmLE-JSC.mjs";
19
19
  import "../../reconcile-EANDIp_T.mjs";
20
20
  import "../../scan-config-C5zzygeH.mjs";
@@ -1,4 +1,4 @@
1
- import { g as RepoProvider } from "./index-ByrVsRJE.mjs";
1
+ import { g as RepoProvider } from "./index-A5iw3yDk.mjs";
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
 
4
4
  //#region src/server.d.ts
@@ -62,4 +62,4 @@ declare function createServer(projectRoot: string): McpServer;
62
62
  declare function createServer(opts: CreateServerOptions): McpServer;
63
63
  //#endregion
64
64
  export { createServer as i, DEFAULT_INSTRUCTIONS as n, ToolProvider as r, CreateServerOptions as t };
65
- //# sourceMappingURL=server-SDsD0XGM.d.mts.map
65
+ //# sourceMappingURL=server-CUtmhlno.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-SDsD0XGM.d.mts","names":[],"sources":["../src/server.ts"],"mappings":";;;;;;AAYA;;;;;KAAY,YAAA,GAAe,YAAA;;;;;AA6B3B;cAVa,oBAAA;AAAA,UAUI,mBAAA;EAOQ;;;;;;EAAvB,QAAA,GAAW,YAAA;EAiEG;;;;;AAChB;EA3DE,WAAA;;;;;;EAMA,YAAA;AAAA;;;;;;;;;;;;;;;;;;;;;iBAoDc,YAAA,CAAa,WAAA,WAAsB,SAAA;AAAA,iBACnC,YAAA,CAAa,IAAA,EAAM,mBAAA,GAAsB,SAAA"}
1
+ {"version":3,"file":"server-CUtmhlno.d.mts","names":[],"sources":["../src/server.ts"],"mappings":";;;;;;AAYA;;;;;KAAY,YAAA,GAAe,YAAA;;;;;AA6B3B;cAVa,oBAAA;AAAA,UAUI,mBAAA;EAOQ;;;;;;EAAvB,QAAA,GAAW,YAAA;EAiEG;;;;;AAChB;EA3DE,WAAA;;;;;;EAMA,YAAA;AAAA;;;;;;;;;;;;;;;;;;;;;iBAoDc,YAAA,CAAa,WAAA,WAAsB,SAAA;AAAA,iBACnC,YAAA,CAAa,IAAA,EAAM,mBAAA,GAAsB,SAAA"}
@@ -3022,7 +3022,7 @@ function registerMediaTools(server, provider, _projectRoot) {
3022
3022
  }
3023
3023
  //#endregion
3024
3024
  //#region package.json
3025
- var version = "3.3.3";
3025
+ var version = "3.4.0";
3026
3026
  //#endregion
3027
3027
  //#region src/server.ts
3028
3028
  /**
@@ -3097,4 +3097,4 @@ function resolveServerContext(input) {
3097
3097
  //#endregion
3098
3098
  export { createServer as n, DEFAULT_INSTRUCTIONS as t };
3099
3099
 
3100
- //# sourceMappingURL=server-BgyNDHvp.mjs.map
3100
+ //# sourceMappingURL=server-Do2BcSBg.mjs.map