@6reduk/workspace-pipeline 0.1.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 (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -0
  3. package/docs/collaboration.md +59 -0
  4. package/docs/config-fields.md +65 -0
  5. package/docs/contracts.md +149 -0
  6. package/docs/doctor.md +165 -0
  7. package/docs/launch.md +50 -0
  8. package/docs/lifecycle-cli.md +141 -0
  9. package/docs/lifecycle.md +42 -0
  10. package/docs/migrations/unity.md +288 -0
  11. package/docs/native-provider-format.md +149 -0
  12. package/docs/provider-bundles.md +81 -0
  13. package/docs/provider-resources.md +37 -0
  14. package/docs/release.md +25 -0
  15. package/docs/remove.md +70 -0
  16. package/docs/repair.md +80 -0
  17. package/docs/repositories.md +209 -0
  18. package/docs/repository-manual-recovery.md +102 -0
  19. package/docs/repository-observations.md +29 -0
  20. package/docs/repository-recovery.md +204 -0
  21. package/docs/repository-retention.md +46 -0
  22. package/docs/repository-transport-budgets.md +26 -0
  23. package/docs/retention.md +237 -0
  24. package/docs/source.md +50 -0
  25. package/docs/switch.md +412 -0
  26. package/package.json +39 -0
  27. package/schemas/common.schema.json +251 -0
  28. package/schemas/inventory.schema.json +15 -0
  29. package/schemas/operation.schema.json +286 -0
  30. package/schemas/pipeline.schema.json +317 -0
  31. package/schemas/state.schema.json +302 -0
  32. package/schemas/workspace.schema.json +67 -0
  33. package/src/cli.js +7 -0
  34. package/src/commands/adopt.js +2 -0
  35. package/src/commands/bootstrap-recovery.js +90 -0
  36. package/src/commands/dispatch.js +394 -0
  37. package/src/commands/init.js +84 -0
  38. package/src/commands/launch.js +69 -0
  39. package/src/commands/migration-apply.js +53 -0
  40. package/src/commands/migration.js +50 -0
  41. package/src/commands/repositories.js +61 -0
  42. package/src/commands/repository-abandon.js +16 -0
  43. package/src/commands/repository-ancestors.js +21 -0
  44. package/src/commands/repository-locks.js +77 -0
  45. package/src/contracts/parse.js +57 -0
  46. package/src/contracts/semantic.js +240 -0
  47. package/src/contracts/validate.js +21 -0
  48. package/src/launch/grok.js +20 -0
  49. package/src/migrations/legacy-unity-begin.js +35 -0
  50. package/src/migrations/legacy-unity-compensate.js +82 -0
  51. package/src/migrations/legacy-unity-deactivate.js +60 -0
  52. package/src/migrations/legacy-unity-deactivation-resume-apply.js +62 -0
  53. package/src/migrations/legacy-unity-deactivation-resume.js +81 -0
  54. package/src/migrations/legacy-unity-finalize.js +88 -0
  55. package/src/migrations/legacy-unity-install-recovery.js +76 -0
  56. package/src/migrations/legacy-unity-install-resume.js +60 -0
  57. package/src/migrations/legacy-unity-install.js +88 -0
  58. package/src/migrations/legacy-unity-lease.js +136 -0
  59. package/src/migrations/legacy-unity-preflight.js +67 -0
  60. package/src/migrations/legacy-unity-preview.js +91 -0
  61. package/src/migrations/legacy-unity-resume-apply.js +39 -0
  62. package/src/migrations/legacy-unity-resume.js +41 -0
  63. package/src/migrations/legacy-unity-resumed-evidence.js +88 -0
  64. package/src/migrations/legacy-unity.js +99 -0
  65. package/src/operations/apply.js +576 -0
  66. package/src/operations/backup.js +94 -0
  67. package/src/operations/bootstrap-lock.js +87 -0
  68. package/src/operations/bootstrap-owner-retirement.js +121 -0
  69. package/src/operations/bundle-update.js +54 -0
  70. package/src/operations/config-fields.js +24 -0
  71. package/src/operations/continuation-lifecycle.js +77 -0
  72. package/src/operations/doctor.js +214 -0
  73. package/src/operations/history.js +170 -0
  74. package/src/operations/installer-identity.js +49 -0
  75. package/src/operations/journal.js +142 -0
  76. package/src/operations/lifecycle.js +133 -0
  77. package/src/operations/lineage-guard.js +20 -0
  78. package/src/operations/lock.js +109 -0
  79. package/src/operations/maintenance.js +62 -0
  80. package/src/operations/migration-pending.js +22 -0
  81. package/src/operations/ownership.js +122 -0
  82. package/src/operations/plan.js +278 -0
  83. package/src/operations/reconciliation.js +112 -0
  84. package/src/operations/recovery-lease.js +66 -0
  85. package/src/operations/remove.js +140 -0
  86. package/src/operations/repair.js +188 -0
  87. package/src/operations/repository-abandon.js +192 -0
  88. package/src/operations/repository-ancestors.js +158 -0
  89. package/src/operations/repository-apply.js +122 -0
  90. package/src/operations/repository-authorization.js +48 -0
  91. package/src/operations/repository-bootstrap-continuation.js +190 -0
  92. package/src/operations/repository-bootstrap-reconcile.js +106 -0
  93. package/src/operations/repository-bootstrap-recover.js +114 -0
  94. package/src/operations/repository-bootstrap.js +82 -0
  95. package/src/operations/repository-clone.js +63 -0
  96. package/src/operations/repository-history.js +108 -0
  97. package/src/operations/repository-inputs.js +41 -0
  98. package/src/operations/repository-journal.js +127 -0
  99. package/src/operations/repository-lock-reconcile.js +401 -0
  100. package/src/operations/repository-pending.js +29 -0
  101. package/src/operations/repository-reconcile.js +182 -0
  102. package/src/operations/repository-resumption-approvals.js +77 -0
  103. package/src/operations/repository-retention-apply.js +75 -0
  104. package/src/operations/repository-retention.js +137 -0
  105. package/src/operations/repository-workspace.js +78 -0
  106. package/src/operations/retention-apply.js +133 -0
  107. package/src/operations/retention-combined-scan.js +30 -0
  108. package/src/operations/retention-combined.js +41 -0
  109. package/src/operations/retention-policy.js +65 -0
  110. package/src/operations/retention-receipts.js +126 -0
  111. package/src/operations/retention-scan.js +86 -0
  112. package/src/operations/retention.js +56 -0
  113. package/src/operations/state.js +210 -0
  114. package/src/operations/switch-activate.js +64 -0
  115. package/src/operations/switch-backups.js +29 -0
  116. package/src/operations/switch-continuation-journal.js +94 -0
  117. package/src/operations/switch-continuation-pending.js +60 -0
  118. package/src/operations/switch-continuation-records.js +109 -0
  119. package/src/operations/switch-continuation-recovery.js +91 -0
  120. package/src/operations/switch-continuation-runtime.js +135 -0
  121. package/src/operations/switch-continuation-store.js +120 -0
  122. package/src/operations/switch-continuation.js +76 -0
  123. package/src/operations/switch-execute.js +68 -0
  124. package/src/operations/switch-inspect.js +45 -0
  125. package/src/operations/switch-journal-store.js +109 -0
  126. package/src/operations/switch-journal.js +71 -0
  127. package/src/operations/switch-lifecycle.js +72 -0
  128. package/src/operations/switch-pending.js +43 -0
  129. package/src/operations/switch-preflight.js +73 -0
  130. package/src/operations/switch-prepare.js +75 -0
  131. package/src/operations/switch-records.js +60 -0
  132. package/src/operations/switch-recovery-store.js +74 -0
  133. package/src/operations/switch.js +52 -0
  134. package/src/operations/toml-fields.js +133 -0
  135. package/src/providers/bundles.js +42 -0
  136. package/src/providers/common-entry.js +16 -0
  137. package/src/providers/grok.js +26 -0
  138. package/src/providers/interface.js +25 -0
  139. package/src/providers/kimi.js +26 -0
  140. package/src/providers/native.js +155 -0
  141. package/src/providers/registry.js +10 -0
  142. package/src/providers/shared.js +51 -0
  143. package/src/providers/source.js +30 -0
  144. package/src/source/git.js +303 -0
  145. package/src/source/inventory.js +87 -0
  146. package/src/source/repository-budget.js +18 -0
  147. package/src/source/snapshot.js +37 -0
  148. package/src/workspace/paths.js +54 -0
  149. package/src/workspace/profiles.js +8 -0
  150. package/src/workspace/repositories.js +57 -0
  151. package/src/workspace/repository-inventory.js +77 -0
  152. package/src/workspace/repository-observation.js +38 -0
  153. package/src/workspace/repository-preflight.js +129 -0
  154. package/src/workspace/repository-preview.js +196 -0
  155. package/src/workspace/repository-tree.js +57 -0
  156. package/src/workspace/reserved.js +11 -0
  157. package/src/workspace/resolve.js +53 -0
@@ -0,0 +1,57 @@
1
+ import path from 'node:path';
2
+ import { performance } from 'node:perf_hooks';
3
+ import { fail } from '../contracts/parse.js';
4
+ import { contractDigest } from '../contracts/semantic.js';
5
+ import { runRepositoryGit,parseListing } from '../source/git.js';
6
+ import { repositoryBudget } from '../source/repository-budget.js';
7
+ import { absoluteRoot,pathBudget } from './paths.js';
8
+
9
+ // User repository paths are not package paths: Unicode and large assets allowed.
10
+ // Keep one portable namespace so a Windows checkout cannot alias another entry.
11
+ export function checkRepositoryTreePaths(entries, destination) {
12
+ destination=absoluteRoot(destination);
13
+ if (entries.length>200000) fail('repository-tree.entries');
14
+ const names=new Map();let total=0;
15
+ for (const entry of entries) {
16
+ if (entry.type!=='blob' || !['100644','100755'].includes(entry.mode)) fail('repository-tree.entry-type');
17
+ if (!Number.isSafeInteger(entry.size) || entry.size<0) fail('repository-tree.size');
18
+ total+=entry.size;if(total>100*1024**3)fail('repository-tree.bytes');
19
+ if (typeof entry.path!=='string' || /[\\\u0000-\u001f\u007f<>:"|?*]/u.test(entry.path)) fail('repository-tree.path');
20
+ const parts=entry.path.split('/');
21
+ if(parts.length>128)fail('repository-tree.depth');
22
+ for(let i=0;i<parts.length;i++) {
23
+ const part=parts[i];
24
+ if (!part || part==='.' || part==='..' || /[. ]$/.test(part) ||
25
+ /^\.git$/i.test(part) || /~[0-9]/.test(part) ||
26
+ /^(?:con|prn|aux|nul|com[1-9¹²³]|lpt[1-9¹²³])(?:\.|$)/i.test(part)) fail('repository-tree.path');
27
+ const spelling=parts.slice(0,i+1).join('/'),key=spelling.normalize('NFC').toLowerCase();
28
+ const kind=i===parts.length-1?'file':'directory',previous=names.get(key);
29
+ if(previous && (previous.spelling!==spelling || previous.kind!==kind || kind==='file'))fail('repository-tree.collision');
30
+ names.set(key,{spelling,kind});
31
+ }
32
+ pathBudget(path.join(destination,...parts));
33
+ }
34
+ return total;
35
+ }
36
+
37
+ // Read committed objects, never checkout/smudge/filter. Connectivity and exact
38
+ // source/staging inventory bindings are supplied by the surrounding preview.
39
+ export async function inspectRepositoryTree(repo, commit, destination, requestedBudget = {}) {
40
+ if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(commit)) fail('repository-tree.commit');
41
+ const budget=repositoryBudget(requestedBudget);
42
+ const options={...budget,deadline:performance.now()+budget.acquisitionMs};
43
+ const entries=parseListing((await runRepositoryGit(repo,['ls-tree','-r','-l','-z','--full-tree',commit],
44
+ {...options,outputLimit:256*1024*1024})).bytes);
45
+ const totalBytes=checkRepositoryTreePaths(entries,destination);
46
+ const seen=new Set();
47
+ for(const entry of entries) {
48
+ // Valid LFS pointers are small. No external LFS client/download is invoked.
49
+ if(entry.size>1024 || seen.has(entry.oid))continue;
50
+ seen.add(entry.oid);
51
+ const bytes=(await runRepositoryGit(repo,['cat-file','blob',entry.oid],{...options,outputLimit:1024})).bytes;
52
+ if(bytes.length!==entry.size)fail('repository-tree.blob-size');
53
+ if(bytes.toString('utf8').startsWith('version https://git-lfs.github.com/spec/v1'))fail('repository-tree.lfs');
54
+ }
55
+ return {commit,entries,totalBytes,digest:contractDigest(entries),
56
+ checkout:'not-performed',filters:'not-run',executionAuthorized:false};
57
+ }
@@ -0,0 +1,11 @@
1
+ import { fail } from '../contracts/parse.js';
2
+
3
+ export const RESERVED = Object.freeze(['.pipeline', '.codex', '.claude', '.kimi-code',
4
+ '.grok', '.agents', '.git', '.mcp.json', 'AGENTS.md', 'CLAUDE.md', 'CLAUDE.local.md', 'GROK.md', 'workspace.yaml']);
5
+ export function assertUnreserved(relative) {
6
+ if (RESERVED.some(name => name.toLowerCase() === relative.split('/')[0].toLowerCase())) fail('layout.reserved');
7
+ }
8
+ export function overlaps(a, b) {
9
+ a = a.toLowerCase(); b = b.toLowerCase();
10
+ return a === b || a.startsWith(b + '/') || b.startsWith(a + '/');
11
+ }
@@ -0,0 +1,53 @@
1
+ import path from 'node:path';
2
+ import { fail } from '../contracts/parse.js';
3
+ import { portablePath } from '../contracts/semantic.js';
4
+ import { selectProfile } from './profiles.js';
5
+ import { assertUnreserved, overlaps } from './reserved.js';
6
+ import { absoluteRoot, pathBudget, resolveChild, inspectDirectory } from './paths.js';
7
+
8
+ // Pure path map. missing/existing Git repos and provider discovery are not certified.
9
+ export function planLayout(pipeline, workspace, wrapper, { adapters, managedPaths = [] } = {}) {
10
+ const selected = selectProfile(pipeline, workspace, adapters);
11
+ const root = absoluteRoot(wrapper); pathBudget(root);
12
+ const { layout } = selected, repositories = {}, projectRoots = {}, warnings = [];
13
+ for (const target of managedPaths) portablePath(target);
14
+ const entries = Object.entries(layout.repositories).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
15
+ const spellings = new Map();
16
+ const registerSpelling = relative => {
17
+ const parts = relative.split('/');
18
+ for (let i = 1; i <= parts.length; i++) {
19
+ const prefix = parts.slice(0, i).join('/'), key = prefix.toLowerCase();
20
+ if (spellings.has(key) && spellings.get(key) !== prefix) fail('layout.case-alias');
21
+ spellings.set(key, prefix);
22
+ }
23
+ };
24
+ for (const target of managedPaths) registerSpelling(target);
25
+ for (const [id, repo] of entries) {
26
+ assertUnreserved(repo.path);
27
+ registerSpelling(repo.path);
28
+ if (Object.values(repositories).some(r => overlaps(r.relative, repo.path)) ||
29
+ managedPaths.some(target => overlaps(target, repo.path))) fail('layout.overlap');
30
+ repositories[id] = { relative: repo.path, path: resolveChild(root, repo.path), role: repo.role };
31
+ if (repo.role === 'documentation' && layout.documentation.repository !== id)
32
+ warnings.push({ code: 'layout.unused-documentation-role', repository: id });
33
+ }
34
+ const reference = ref => {
35
+ const relative = path.posix.join(layout.repositories[ref.repository].path, ref.path);
36
+ portablePath(relative);
37
+ registerSpelling(relative);
38
+ return { repository: ref.repository, relative, path: resolveChild(root, relative) };
39
+ };
40
+ const documentation = reference(layout.documentation);
41
+ for (const id of Object.keys(layout.projectRoots ?? {}).sort()) projectRoots[id] = reference(layout.projectRoots[id]);
42
+ return { ...selected, wrapper: root, repositories, documentation, projectRoots, warnings, filesystem: 'not-inspected', runtime: 'not-run' };
43
+ }
44
+ export async function resolveLayout(pipeline, workspace, wrapper, options) {
45
+ const result = planLayout(pipeline, workspace, wrapper, options);
46
+ // No mkdir/clone/move even for a not-yet-created wrapper. Existing ancestors
47
+ // are inspected; missing paths remain planned paths, never readiness PASS.
48
+ const observations = {};
49
+ const targets = [result.wrapper, ...Object.values(result.repositories).map(r => r.path),
50
+ result.documentation.path, ...Object.values(result.projectRoots).map(r => r.path)];
51
+ for (const target of new Set(targets)) observations[target] = await inspectDirectory(target);
52
+ return { ...result, filesystem: 'inspected', observations };
53
+ }