canopycms 0.0.67 → 0.0.68-int.101

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 (281) hide show
  1. package/README.md +24 -17
  2. package/dist/ai/handler.js +8 -0
  3. package/dist/api/admin-branch-health.js +17 -20
  4. package/dist/api/admin.d.ts +30 -9
  5. package/dist/api/admin.js +32 -2
  6. package/dist/api/assets.js +84 -76
  7. package/dist/api/branch-create-window.d.ts +12 -0
  8. package/dist/api/branch-create-window.js +13 -0
  9. package/dist/api/branch-review.d.ts +3 -3
  10. package/dist/api/branch-review.js +14 -5
  11. package/dist/api/branch-status.d.ts +5 -5
  12. package/dist/api/branch-status.js +26 -6
  13. package/dist/api/branch-withdraw.d.ts +2 -2
  14. package/dist/api/branch-withdraw.js +7 -2
  15. package/dist/api/branch.d.ts +15 -2
  16. package/dist/api/branch.js +118 -92
  17. package/dist/api/client.d.ts +18 -6
  18. package/dist/api/client.js +79 -12
  19. package/dist/api/content.d.ts +6 -5
  20. package/dist/api/content.js +44 -41
  21. package/dist/api/entries.js +16 -15
  22. package/dist/api/github-sync.d.ts +19 -1
  23. package/dist/api/github-sync.js +57 -10
  24. package/dist/api/index.d.ts +1 -1
  25. package/dist/api/reference-options.js +1 -1
  26. package/dist/api/resolve-references.js +14 -40
  27. package/dist/api/settings-helpers.d.ts +1 -1
  28. package/dist/api/settings-helpers.js +1 -4
  29. package/dist/api/user.d.ts +3 -0
  30. package/dist/api/user.js +3 -0
  31. package/dist/assets/asset-url.d.ts +22 -16
  32. package/dist/assets/asset-url.js +42 -25
  33. package/dist/assets/factory.d.ts +5 -0
  34. package/dist/assets/factory.js +5 -0
  35. package/dist/assets/finalize.js +3 -0
  36. package/dist/assets/index.d.ts +1 -1
  37. package/dist/assets/materialize.d.ts +153 -0
  38. package/dist/assets/materialize.js +402 -0
  39. package/dist/assets/store-local.d.ts +19 -5
  40. package/dist/assets/store-local.js +69 -10
  41. package/dist/assets/store-s3.d.ts +38 -7
  42. package/dist/assets/store-s3.js +128 -22
  43. package/dist/assets/transform-directives.d.ts +52 -13
  44. package/dist/assets/transform-directives.js +87 -27
  45. package/dist/assets/transform.d.ts +3 -3
  46. package/dist/assets/transform.js +13 -8
  47. package/dist/assets/types.d.ts +31 -4
  48. package/dist/auth/file-based-auth-cache.js +7 -8
  49. package/dist/authorization/content.d.ts +5 -4
  50. package/dist/authorization/content.js +5 -5
  51. package/dist/authorization/path.d.ts +11 -6
  52. package/dist/authorization/path.js +11 -6
  53. package/dist/authorization/types.d.ts +2 -2
  54. package/dist/branch-health.d.ts +7 -1
  55. package/dist/branch-health.js +8 -3
  56. package/dist/branch-metadata.d.ts +5 -5
  57. package/dist/branch-metadata.js +44 -36
  58. package/dist/branch-provisioning.d.ts +167 -0
  59. package/dist/branch-provisioning.js +501 -0
  60. package/dist/branch-schema-cache.d.ts +5 -3
  61. package/dist/branch-schema-cache.js +29 -11
  62. package/dist/branch-sparse.d.ts +23 -0
  63. package/dist/branch-sparse.js +118 -0
  64. package/dist/branch-workspace.d.ts +42 -3
  65. package/dist/branch-workspace.js +240 -94
  66. package/dist/build/asset-refs.d.ts +93 -0
  67. package/dist/build/asset-refs.js +251 -0
  68. package/dist/build-identity.d.ts +3 -0
  69. package/dist/build-identity.js +13 -0
  70. package/dist/cli/asset-refs.d.ts +52 -0
  71. package/dist/cli/asset-refs.js +194 -0
  72. package/dist/cli/cli.d.ts +15 -1
  73. package/dist/cli/cli.js +5928 -2195
  74. package/dist/cli/configured-asset-store.d.ts +12 -0
  75. package/dist/cli/configured-asset-store.js +46 -0
  76. package/dist/cli/generate-ai-content.js +2185 -587
  77. package/dist/cli/github-app-manifest.d.ts +4 -3
  78. package/dist/cli/github-app-manifest.js +4 -3
  79. package/dist/cli/init.js +30 -27
  80. package/dist/cli/migrate.js +12 -8
  81. package/dist/cli/sync.js +83 -37
  82. package/dist/cli/template-files/Dockerfile.cms.template +8 -0
  83. package/dist/cli/template-files/cdk-app.ts.template +4 -0
  84. package/dist/cli/template-files/cms-stack.ts.template +23 -12
  85. package/dist/cli/template-files/deploy-cms.yml.template +2 -0
  86. package/dist/client.d.ts +1 -1
  87. package/dist/client.js +1 -1
  88. package/dist/config/helpers.js +1 -2
  89. package/dist/config/schemas/config.d.ts +19 -32
  90. package/dist/config/schemas/config.js +9 -11
  91. package/dist/config/schemas/field.js +1 -0
  92. package/dist/config/schemas/media.d.ts +4 -13
  93. package/dist/config/schemas/media.js +5 -8
  94. package/dist/config/schemas/url.d.ts +4 -20
  95. package/dist/config/schemas/url.js +6 -22
  96. package/dist/config/types.d.ts +30 -20
  97. package/dist/config/validation.d.ts +6 -0
  98. package/dist/config/validation.js +37 -0
  99. package/dist/content-listing.d.ts +12 -12
  100. package/dist/content-listing.js +10 -6
  101. package/dist/content-reader.d.ts +11 -2
  102. package/dist/content-reader.js +22 -14
  103. package/dist/content-store.d.ts +37 -9
  104. package/dist/content-store.js +70 -43
  105. package/dist/content-tree.d.ts +1 -1
  106. package/dist/content-tree.js +2 -2
  107. package/dist/context.d.ts +27 -9
  108. package/dist/context.js +154 -70
  109. package/dist/editor/BranchManager.d.ts +7 -1
  110. package/dist/editor/BranchManager.js +29 -14
  111. package/dist/editor/CanopyEditor.d.ts +1 -1
  112. package/dist/editor/CanopyEditor.js +2 -6
  113. package/dist/editor/Editor.d.ts +4 -3
  114. package/dist/editor/Editor.js +32 -22
  115. package/dist/editor/FormRenderer.js +34 -18
  116. package/dist/editor/PreviewFrame.d.ts +24 -0
  117. package/dist/editor/PreviewFrame.js +140 -0
  118. package/dist/editor/admin/SystemHealthPanel.js +47 -7
  119. package/dist/editor/components/BranchesDrawer.d.ts +9 -0
  120. package/dist/editor/components/BranchesDrawer.js +4 -0
  121. package/dist/editor/components/NoEditPermissionNotice.d.ts +10 -0
  122. package/dist/editor/components/NoEditPermissionNotice.js +17 -0
  123. package/dist/editor/context/AssetContext.d.ts +18 -35
  124. package/dist/editor/context/AssetContext.js +20 -29
  125. package/dist/editor/context/index.d.ts +1 -1
  126. package/dist/editor/context/index.js +1 -1
  127. package/dist/editor/editor-config.d.ts +1 -2
  128. package/dist/editor/editor-config.js +0 -23
  129. package/dist/editor/editor-utils.d.ts +24 -21
  130. package/dist/editor/editor-utils.js +75 -62
  131. package/dist/editor/fields/BlockField.d.ts +1 -0
  132. package/dist/editor/fields/BlockField.js +6 -4
  133. package/dist/editor/fields/CodeField.d.ts +1 -0
  134. package/dist/editor/fields/CodeField.js +2 -2
  135. package/dist/editor/fields/DateTimeField.d.ts +1 -0
  136. package/dist/editor/fields/DateTimeField.js +2 -2
  137. package/dist/editor/fields/FieldDescription.d.ts +16 -0
  138. package/dist/editor/fields/FieldDescription.js +11 -0
  139. package/dist/editor/fields/ImageField.d.ts +1 -0
  140. package/dist/editor/fields/ImageField.js +3 -2
  141. package/dist/editor/fields/InlineGroupField.js +4 -1
  142. package/dist/editor/fields/MarkdownField.d.ts +1 -0
  143. package/dist/editor/fields/MarkdownField.js +115 -20
  144. package/dist/editor/fields/NumberField.d.ts +1 -0
  145. package/dist/editor/fields/NumberField.js +2 -2
  146. package/dist/editor/fields/NumberListField.d.ts +1 -0
  147. package/dist/editor/fields/NumberListField.js +2 -2
  148. package/dist/editor/fields/ObjectField.d.ts +1 -0
  149. package/dist/editor/fields/ObjectField.js +5 -2
  150. package/dist/editor/fields/ReferenceField.d.ts +1 -0
  151. package/dist/editor/fields/ReferenceField.js +5 -4
  152. package/dist/editor/fields/SelectField.d.ts +1 -0
  153. package/dist/editor/fields/SelectField.js +2 -2
  154. package/dist/editor/fields/StringListField.d.ts +1 -0
  155. package/dist/editor/fields/StringListField.js +2 -2
  156. package/dist/editor/fields/TextField.d.ts +1 -0
  157. package/dist/editor/fields/TextField.js +2 -2
  158. package/dist/editor/fields/ToggleField.d.ts +1 -0
  159. package/dist/editor/fields/ToggleField.js +7 -4
  160. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +1 -1
  161. package/dist/editor/fields/entry-link/InsertEntryLink.js +3 -2
  162. package/dist/editor/fields/mdx-jsx-support.d.ts +1 -0
  163. package/dist/editor/fields/mdx-jsx-support.js +137 -0
  164. package/dist/editor/hooks/create-branch-request.d.ts +20 -0
  165. package/dist/editor/hooks/create-branch-request.js +52 -0
  166. package/dist/editor/hooks/useBranchActions.d.ts +13 -2
  167. package/dist/editor/hooks/useBranchActions.js +48 -17
  168. package/dist/editor/hooks/useBranchManager.d.ts +21 -0
  169. package/dist/editor/hooks/useBranchManager.js +204 -37
  170. package/dist/editor/hooks/useBranchesData.d.ts +6 -0
  171. package/dist/editor/hooks/useBranchesData.js +9 -3
  172. package/dist/editor/hooks/useCommentSystem.js +25 -6
  173. package/dist/editor/hooks/useDraftManager.d.ts +16 -1
  174. package/dist/editor/hooks/useDraftManager.js +193 -36
  175. package/dist/editor/hooks/useEntriesData.js +3 -3
  176. package/dist/editor/hooks/useEntryManager.d.ts +2 -1
  177. package/dist/editor/hooks/useEntryManager.js +34 -19
  178. package/dist/editor/media/AssetCard.d.ts +1 -1
  179. package/dist/editor/media/crop-math.d.ts +3 -4
  180. package/dist/editor/media/crop-math.js +11 -22
  181. package/dist/editor/media/editor-image-src.d.ts +7 -0
  182. package/dist/editor/media/editor-image-src.js +12 -0
  183. package/dist/editor/preview-asset-base.d.ts +8 -0
  184. package/dist/editor/preview-asset-base.js +13 -0
  185. package/dist/editor/preview-bridge.d.ts +9 -21
  186. package/dist/editor/preview-bridge.js +19 -139
  187. package/dist/editor/preview-path.d.ts +10 -0
  188. package/dist/editor/preview-path.js +20 -0
  189. package/dist/editor/theme.js +2 -2
  190. package/dist/entry-schema-registry.d.ts +4 -4
  191. package/dist/entry-schema-registry.js +10 -6
  192. package/dist/entry-schema.d.ts +27 -4
  193. package/dist/entry-schema.js +20 -3
  194. package/dist/git-manager.d.ts +173 -6
  195. package/dist/git-manager.js +594 -76
  196. package/dist/github-service.d.ts +15 -0
  197. package/dist/github-service.js +19 -1
  198. package/dist/http/handler.js +37 -13
  199. package/dist/http/index.d.ts +2 -0
  200. package/dist/http/index.js +2 -0
  201. package/dist/http/router.d.ts +2 -0
  202. package/dist/http/router.js +2 -1
  203. package/dist/http/types.d.ts +2 -0
  204. package/dist/http/worker-not-ready.d.ts +10 -0
  205. package/dist/http/worker-not-ready.js +24 -0
  206. package/dist/operating-mode/client-unsafe-strategy.js +0 -6
  207. package/dist/operating-mode/types.d.ts +1 -4
  208. package/dist/paths/branch-name.d.ts +5 -0
  209. package/dist/paths/branch-name.js +9 -0
  210. package/dist/paths/branch.d.ts +6 -2
  211. package/dist/paths/branch.js +16 -10
  212. package/dist/paths/index.d.ts +3 -3
  213. package/dist/paths/index.js +3 -3
  214. package/dist/paths/normalize.d.ts +7 -0
  215. package/dist/paths/normalize.js +9 -0
  216. package/dist/paths/validation.js +0 -2
  217. package/dist/preview.d.ts +8 -0
  218. package/dist/preview.js +7 -0
  219. package/dist/reference-resolver.d.ts +5 -21
  220. package/dist/reference-resolver.js +9 -44
  221. package/dist/resolve-canopy-user.js +2 -1
  222. package/dist/schema/schema-store.d.ts +54 -31
  223. package/dist/schema/schema-store.js +77 -31
  224. package/dist/server.d.ts +64 -9
  225. package/dist/server.js +48 -8
  226. package/dist/services.d.ts +18 -6
  227. package/dist/services.js +85 -70
  228. package/dist/settings-workspace.d.ts +23 -3
  229. package/dist/settings-workspace.js +197 -45
  230. package/dist/static/seo.d.ts +2 -14
  231. package/dist/static/seo.js +2 -25
  232. package/dist/submission-attribution.d.ts +73 -0
  233. package/dist/submission-attribution.js +221 -0
  234. package/dist/sync-core.d.ts +12 -1
  235. package/dist/sync-core.js +31 -13
  236. package/dist/task-queue/worker-status.d.ts +8 -0
  237. package/dist/task-queue/worker-status.js +17 -0
  238. package/dist/types.d.ts +31 -2
  239. package/dist/utils/content-serialize.d.ts +5 -2
  240. package/dist/utils/content-serialize.js +98 -24
  241. package/dist/utils/content-write-lock.d.ts +18 -7
  242. package/dist/utils/content-write-lock.js +18 -9
  243. package/dist/utils/debug.d.ts +8 -0
  244. package/dist/utils/debug.js +10 -2
  245. package/dist/utils/git.d.ts +32 -0
  246. package/dist/utils/git.js +42 -0
  247. package/dist/utils/occ-json-write.js +10 -2
  248. package/dist/utils/provision-log.d.ts +25 -0
  249. package/dist/utils/provision-log.js +49 -0
  250. package/dist/utils/provisioning-lock.d.ts +35 -5
  251. package/dist/utils/provisioning-lock.js +73 -12
  252. package/dist/utils/request-timing.d.ts +25 -0
  253. package/dist/utils/request-timing.js +101 -0
  254. package/dist/utils/sanitize-href.d.ts +8 -22
  255. package/dist/utils/sanitize-href.js +11 -28
  256. package/dist/utils/url-prefix.d.ts +30 -0
  257. package/dist/utils/url-prefix.js +61 -0
  258. package/dist/utils/yaml-source-splice.d.ts +58 -0
  259. package/dist/utils/yaml-source-splice.js +515 -0
  260. package/dist/validation/entry-validator.js +9 -3
  261. package/dist/version.d.ts +1 -0
  262. package/dist/version.js +3 -0
  263. package/dist/worker/canopy-state.d.ts +45 -0
  264. package/dist/worker/canopy-state.js +75 -0
  265. package/dist/worker/cms-worker.d.ts +8 -3
  266. package/dist/worker/cms-worker.js +38 -2
  267. package/dist/worker/git-sync.d.ts +30 -12
  268. package/dist/worker/git-sync.js +253 -58
  269. package/dist/worker/history-rewrite.d.ts +1 -1
  270. package/dist/worker/history-rewrite.js +1 -1
  271. package/dist/worker/provisioned-workspace.d.ts +35 -0
  272. package/dist/worker/provisioned-workspace.js +50 -0
  273. package/dist/worker/rebase.d.ts +1 -1
  274. package/dist/worker/rebase.js +95 -23
  275. package/dist/worker/remote-git-maintenance.d.ts +3 -0
  276. package/dist/worker/remote-git-maintenance.js +11 -0
  277. package/dist/worker/sparse-cone.d.ts +20 -0
  278. package/dist/worker/sparse-cone.js +132 -0
  279. package/dist/worker/task-runner.js +99 -14
  280. package/dist/worker/worker-context.d.ts +6 -1
  281. package/package.json +8 -2
@@ -1,12 +1,12 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import { simpleGit } from 'simple-git';
3
3
  import { completeTask, dequeueTask, failTask, recoverOrphanedTasks, retryTask, } from '../task-queue/cms-task-queue.js';
4
- import { createOrUpdatePullRequest } from '../github-service.js';
5
- import { BranchMetadataFileManager, getBranchMetadataFileManager } from '../branch-metadata.js';
6
- import { sanitizeBranchName } from '../paths/branch-name.js';
4
+ import { createOrUpdatePullRequest, isRefAlreadyGoneError } from '../github-service.js';
5
+ import { BranchMetadataCorruptError, BranchMetadataFileManager, getBranchMetadataFileManager, } from '../branch-metadata.js';
6
+ import { sanitizeBranchName, RESERVED_SETTINGS_BRANCH_PREFIX } from '../paths/branch-name.js';
7
7
  import { gitNetworkChildEnv } from '../git-manager.js';
8
8
  import { getErrorMessage, redactCredentials } from '../utils/error.js';
9
- import { isNonFastForwardRejection, isStaleLeaseRejection } from '../utils/git.js';
9
+ import { isNonFastForwardRejection, isStaleLeaseRejection, workflowPushRefusalFile, } from '../utils/git.js';
10
10
  import { clearHistoryRewrittenMarker, readPublishedSha } from './history-rewrite.js';
11
11
  import { writeWorkerStatus } from '../task-queue/worker-status.js';
12
12
  import { workerLog, workerLogError } from './log.js';
@@ -90,6 +90,15 @@ function isRateLimitSignal403(err) {
90
90
  return false;
91
91
  }
92
92
  // Payload validation helpers — fail fast with clear errors instead of silent `as` casts
93
+ /**
94
+ * Whether `branch` carries the reserved settings-branch prefix. Matching on the prefix
95
+ * alone, never on a configured name, is what keeps a content branch out: branch creation
96
+ * rejects the prefix, while a configured name the worker and API disagree on could be a
97
+ * content branch's.
98
+ */
99
+ function isSettingsBranch(branch) {
100
+ return branch.startsWith(RESERVED_SETTINGS_BRANCH_PREFIX);
101
+ }
93
102
  function requireString(payload, key) {
94
103
  const val = payload[key];
95
104
  if (typeof val !== 'string')
@@ -282,6 +291,13 @@ export async function executeTask(ctx, task, signal) {
282
291
  if (sanitizeBranchName(branch) === ctx.sanitizedBaseBranch) {
283
292
  throw new PermanentTaskError(`Refusing to push-and-create-or-update-pr for "${branch}": it is the base branch -- submitting the base branch is never valid`);
284
293
  }
294
+ // The settings branch is an orphan with no history in common with the
295
+ // base, so GitHub 422s a PR for it: push it and stop.
296
+ if (isSettingsBranch(branch)) {
297
+ await ctx.pushBranchToGitHub(branch);
298
+ workerLog(`Pushed settings branch ${branch}; settings branches never get a PR`);
299
+ return { pushed: true };
300
+ }
285
301
  await ctx.pushBranchToGitHub(branch);
286
302
  const result = await createOrUpdatePullRequest({
287
303
  octokit: ctx.octokit(),
@@ -291,9 +307,9 @@ export async function executeTask(ctx, task, signal) {
291
307
  base,
292
308
  title: optionalString(payload, 'title', `Submit ${branch}`),
293
309
  body: optionalString(payload, 'body', ''),
294
- // Content submits (api/github-sync.ts) set this; settings-branch
295
- // syncs (services.ts) deliberately don't.
310
+ // Content submits (api/github-sync.ts) set both.
296
311
  markReadyIfDraft: payload.markReadyIfDraft === true,
312
+ mergeSectionIntoBody: payload.mergeSectionIntoBody === true,
297
313
  signal,
298
314
  });
299
315
  workerLog(result.created
@@ -330,23 +346,73 @@ export async function executeTask(ctx, task, signal) {
330
346
  }
331
347
  case 'delete-remote-branch': {
332
348
  const branch = requireString(payload, 'branch');
333
- await ctx.octokit().git.deleteRef({
334
- owner: ctx.githubOwner,
335
- repo: ctx.githubRepo,
336
- ref: `heads/${branch}`,
337
- request: { signal },
338
- });
349
+ // Defense-in-depth behind the delete handler's own refusal.
350
+ if (sanitizeBranchName(branch) === ctx.sanitizedBaseBranch || isSettingsBranch(branch)) {
351
+ throw new PermanentTaskError(`Refusing to delete "${branch}" on GitHub: the base and settings branches are never deleted`);
352
+ }
353
+ // A branch that reused the name after this task was queued (a requeued task can run long
354
+ // after) owns the GitHub branch once its own submit recorded a different PR. Without one,
355
+ // the ref is taken to be the deleted branch's, which is wrong only if that submit pushed
356
+ // and then lost its PR number. Unparseable metadata keeps the GitHub branch; other read
357
+ // errors retry.
358
+ const deletedPr = typeof payload.pullRequestNumber === 'number' ? payload.pullRequestNumber : undefined;
359
+ let live = null;
360
+ let unreadable = false;
361
+ try {
362
+ live = await BranchMetadataFileManager.loadOnly(ctx.branchWorkspacePath(branch));
363
+ }
364
+ catch (err) {
365
+ if (!(err instanceof BranchMetadataCorruptError))
366
+ throw err;
367
+ unreadable = true;
368
+ }
369
+ if (unreadable ||
370
+ (live !== null && (typeof live.branch !== 'object' || live.branch === null))) {
371
+ workerLog(`Not deleting GitHub branch ${branch}: the branch now under that name is unreadable`);
372
+ return { deleted: false, skipped: 'metadata-unreadable' };
373
+ }
374
+ const livePr = live?.branch.pullRequestNumber;
375
+ if (livePr !== undefined && livePr !== deletedPr) {
376
+ workerLog(`Not deleting GitHub branch ${branch}: a newer branch's PR #${livePr} uses it`);
377
+ return { deleted: false, skipped: 'name-reused' };
378
+ }
379
+ try {
380
+ await ctx.octokit().git.deleteRef({
381
+ owner: ctx.githubOwner,
382
+ repo: ctx.githubRepo,
383
+ ref: `heads/${branch}`,
384
+ request: { signal },
385
+ });
386
+ }
387
+ catch (err) {
388
+ // Already gone is the outcome this task wants; failing it would only park it in failed/.
389
+ if (!isRefAlreadyGoneError(err))
390
+ throw err;
391
+ workerLog(`GitHub branch ${branch} was already deleted`);
392
+ return { deleted: false, alreadyGone: true };
393
+ }
394
+ workerLog(`Deleted GitHub branch ${branch}`);
339
395
  return { deleted: true };
340
396
  }
341
397
  default:
342
398
  throw new PermanentTaskError(`Unknown task action: ${action}`);
343
399
  }
344
400
  }
401
+ /**
402
+ * The branch whose metadata records a task's outcome, or null when none does. A
403
+ * delete-remote-branch task names a branch already deleted here, so a workspace under that
404
+ * name is leftover or a newer branch's that reused the name.
405
+ */
406
+ function metadataBranchOf(task) {
407
+ if (task.action === 'delete-remote-branch')
408
+ return null;
409
+ return typeof task.payload.branch === 'string' ? task.payload.branch : null;
410
+ }
345
411
  /**
346
412
  * Update branch metadata after successful task completion.
347
413
  */
348
414
  export async function updateBranchMetadata(ctx, task, result) {
349
- const branch = typeof task.payload.branch === 'string' ? task.payload.branch : null;
415
+ const branch = metadataBranchOf(task);
350
416
  if (!branch)
351
417
  return;
352
418
  const branchPath = ctx.branchWorkspacePath(branch);
@@ -395,7 +461,7 @@ export async function updateBranchMetadata(ctx, task, result) {
395
461
  * syncFailureReason so the editor can show WHY, not just that it failed.
396
462
  */
397
463
  async function updateBranchMetadataOnFailure(ctx, task, error) {
398
- const branch = typeof task.payload.branch === 'string' ? task.payload.branch : null;
464
+ const branch = metadataBranchOf(task);
399
465
  if (!branch)
400
466
  return;
401
467
  const branchPath = ctx.branchWorkspacePath(branch);
@@ -415,6 +481,23 @@ async function updateBranchMetadataOnFailure(ctx, task, error) {
415
481
  workerLogError(`Failed to update failure metadata for ${branch}:`, err instanceof Error ? err.message : err);
416
482
  }
417
483
  }
484
+ /**
485
+ * Fail fast when GitHub refused the push for adding workflow content it does not already hold: the
486
+ * worker's credential deliberately lacks the workflows permission, so the identical push can never
487
+ * succeed. Rebasing onto a base that changed a workflow does not trigger it; a workflow edit made
488
+ * outside the editor, auto-merged by the rebase with a base change to the same file, does.
489
+ */
490
+ function throwIfWorkflowRefusal(branch, message) {
491
+ const file = workflowPushRefusalFile(message);
492
+ if (file === null)
493
+ return;
494
+ throw new PermanentTaskError(`Push refused for branch "${branch}": it would put a version of ${file} on GitHub that ` +
495
+ `GitHub does not already have, and this deployment's GitHub credential is deliberately not ` +
496
+ `allowed to change workflow files. Such a change usually comes from outside the editor, ` +
497
+ `such as a direct push to this branch. Nothing was pushed, and ` +
498
+ `retrying will not help until a developer with permission to change workflow files ` +
499
+ `resolves it on GitHub.`);
500
+ }
418
501
  export async function pushBranchToGitHub(ctx, branch) {
419
502
  const git = simpleGit({
420
503
  baseDir: ctx.remoteGitPath,
@@ -473,6 +556,7 @@ export async function pushBranchToGitHub(ctx, branch) {
473
556
  }
474
557
  catch (err) {
475
558
  const message = getErrorMessage(err);
559
+ throwIfWorkflowRefusal(branch, message);
476
560
  // A refused lease means GitHub is not at the commit we rewrote, so the
477
561
  // marker is stale -- routine, not exceptional: tasks are re-run after a
478
562
  // crash (recoverOrphanedTasks) and the marker survives any failure to
@@ -494,6 +578,7 @@ export async function pushBranchToGitHub(ctx, branch) {
494
578
  }
495
579
  catch (retryErr) {
496
580
  const retryMessage = getErrorMessage(retryErr);
581
+ throwIfWorkflowRefusal(branch, retryMessage);
497
582
  if (isNonFastForwardRejection(retryMessage)) {
498
583
  throw new PermanentTaskError(`Push rejected for branch "${branch}": GitHub's tip is neither the commit this ` +
499
584
  `deployment last published nor an ancestor of what it is pushing, so the branch ` +
@@ -38,7 +38,12 @@ export interface WorkerContext {
38
38
  readonly sanitizedBaseBranch: SanitizedBranchName;
39
39
  /** `{workspacePath}/.tasks` — the task queue and worker-status.json. */
40
40
  readonly taskDir: string;
41
- /** `{workspacePath}/remote.git` — the shared bare repo. */
41
+ /**
42
+ * `{workspacePath}/remote.git` — the shared bare repo. The worker fetches
43
+ * into and pushes from a workspace clone against this path, never the
44
+ * clone's `origin`: that holds the path the CLONING process saw (usually
45
+ * the Lambda), and the clone's config is Lambda-writable.
46
+ */
42
47
  readonly remoteGitPath: string;
43
48
  /** `{workspacePath}/content-branches` — the branch workspace root. */
44
49
  readonly contentBranchesPath: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "//": "@codemirror/language, @lezer/highlight: workaround — @mdxeditor/editor uses cm6-theme-basic-light which peer-requires these but mdxeditor doesn't declare them as dependencies",
3
3
  "name": "canopycms",
4
- "version": "0.0.67",
4
+ "version": "0.0.68-int.101",
5
5
  "description": "CanopyCMS core package: schema-driven content, branch-aware editing, and editor UI for Next.js.",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -27,6 +27,11 @@
27
27
  "import": "./dist/client.js",
28
28
  "require": "./dist/client.js"
29
29
  },
30
+ "./preview": {
31
+ "types": "./dist/preview.d.ts",
32
+ "import": "./dist/preview.js",
33
+ "require": "./dist/preview.js"
34
+ },
30
35
  "./server": {
31
36
  "types": "./dist/server.d.ts",
32
37
  "import": "./dist/server.js",
@@ -85,8 +90,9 @@
85
90
  "node": ">=22.12.0"
86
91
  },
87
92
  "dependencies": {
88
- "@aws-sdk/client-s3": "^3.600.0",
93
+ "@aws-sdk/client-s3": "^3.1092.0",
89
94
  "@aws-sdk/s3-presigned-post": "^3.600.0",
95
+ "@aws-sdk/s3-request-presigner": "^3.600.0",
90
96
  "@clack/prompts": "^1.1.0",
91
97
  "@codemirror/language": "^6.0.0",
92
98
  "@dnd-kit/core": "^6.3.1",