@aopslabs/aops 0.2.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 (171) hide show
  1. package/LICENSE +19 -0
  2. package/NOTICE +7 -0
  3. package/README.md +203 -0
  4. package/assets/agent-assets/core/SKILL.md +49 -0
  5. package/assets/agent-assets/core/references/agentspace/SKILL.md +78 -0
  6. package/assets/agent-assets/core/references/aops-cli-core/SKILL.md +108 -0
  7. package/assets/agent-assets/core/references/chatv3/SKILL.md +48 -0
  8. package/assets/agent-assets/core/references/collaborative-work/SKILL.md +85 -0
  9. package/assets/agent-assets/core/references/discuss/SKILL.md +66 -0
  10. package/assets/agent-assets/core/references/docman/SKILL.md +69 -0
  11. package/assets/agent-assets/core/references/hosted-chat/SKILL.md +64 -0
  12. package/assets/agent-assets/core/references/mission/SKILL.md +54 -0
  13. package/assets/agent-assets/core/references/projectman/SKILL.md +86 -0
  14. package/assets/agent-assets/core/references/sys/SKILL.md +36 -0
  15. package/assets/agent-assets/core/references/view/SKILL.md +58 -0
  16. package/assets/agent-assets/core/references/working-disciplines/SKILL.md +69 -0
  17. package/assets/agent-assets/core/user-guides/agent-assets.md +81 -0
  18. package/assets/agent-assets/core/user-guides/agentspace.md +1098 -0
  19. package/assets/agent-assets/core/user-guides/aops-cli.md +1107 -0
  20. package/assets/agent-assets/core/user-guides/aops-system.md +63 -0
  21. package/assets/agent-assets/core/user-guides/chatv3.md +113 -0
  22. package/assets/agent-assets/core/user-guides/docman.md +162 -0
  23. package/assets/agent-assets/core/user-guides/projectman.md +212 -0
  24. package/assets/agent-assets/core/user-guides/sys.md +94 -0
  25. package/assets/agent-assets/core/user-guides/working-disciplines.md +588 -0
  26. package/assets/agent-assets/gateway/aops/SKILL.md +14 -0
  27. package/assets/r6-remote-session-baseline.json +19 -0
  28. package/assets/skills/aops-install/SKILL.md +231 -0
  29. package/assets/skills/aops-install/agents/openai.yaml +4 -0
  30. package/dist/commands/activity.js +352 -0
  31. package/dist/commands/agent-profile.js +653 -0
  32. package/dist/commands/agent.js +710 -0
  33. package/dist/commands/agents-md.js +125 -0
  34. package/dist/commands/api.js +84 -0
  35. package/dist/commands/archive.js +873 -0
  36. package/dist/commands/artifact.js +535 -0
  37. package/dist/commands/assets.js +1414 -0
  38. package/dist/commands/auth/device-login.js +1 -0
  39. package/dist/commands/auth/legacy-export.js +177 -0
  40. package/dist/commands/auth/login.js +426 -0
  41. package/dist/commands/chat.js +1363 -0
  42. package/dist/commands/chatv3.js +1684 -0
  43. package/dist/commands/checkpoint.js +549 -0
  44. package/dist/commands/commercial-artifact.js +475 -0
  45. package/dist/commands/commercial-license.js +594 -0
  46. package/dist/commands/community-auth.js +62 -0
  47. package/dist/commands/community-cockpit.js +185 -0
  48. package/dist/commands/community-console.js +109 -0
  49. package/dist/commands/community-doctor.js +335 -0
  50. package/dist/commands/community-server.js +2062 -0
  51. package/dist/commands/community-setup.js +284 -0
  52. package/dist/commands/discuss.js +1448 -0
  53. package/dist/commands/doc.js +5219 -0
  54. package/dist/commands/experience.js +922 -0
  55. package/dist/commands/host.js +1127 -0
  56. package/dist/commands/init.js +118 -0
  57. package/dist/commands/memory.js +3129 -0
  58. package/dist/commands/mission.js +562 -0
  59. package/dist/commands/official-catalog.js +134 -0
  60. package/dist/commands/plan.js +84 -0
  61. package/dist/commands/playbook.js +415 -0
  62. package/dist/commands/pm/context.js +6 -0
  63. package/dist/commands/pm/index.js +701 -0
  64. package/dist/commands/pm/projectman.js +6266 -0
  65. package/dist/commands/project.js +1080 -0
  66. package/dist/commands/prompt.js +1024 -0
  67. package/dist/commands/repo-sync.js +1907 -0
  68. package/dist/commands/resource.js +527 -0
  69. package/dist/commands/skill.js +1211 -0
  70. package/dist/commands/start-disciplines.js +510 -0
  71. package/dist/commands/start.js +1343 -0
  72. package/dist/commands/target.js +309 -0
  73. package/dist/commands/version.js +42 -0
  74. package/dist/commands/view.js +1852 -0
  75. package/dist/launchers/cockpit.js +28 -0
  76. package/dist/launchers/options.js +74 -0
  77. package/dist/launchers/server.js +41 -0
  78. package/dist/lib/agent-assets/envelope.js +83 -0
  79. package/dist/lib/agent-assets/gateway.js +15 -0
  80. package/dist/lib/agent-assets/guards.js +23 -0
  81. package/dist/lib/agent-assets/hosted-discovery.js +148 -0
  82. package/dist/lib/agent-assets/hosted-package-input.js +154 -0
  83. package/dist/lib/agent-assets/legacy-pointer-migration.js +677 -0
  84. package/dist/lib/agent-assets/native-fs.js +589 -0
  85. package/dist/lib/agent-assets/package-manifest.js +324 -0
  86. package/dist/lib/agent-assets/portable-path.js +67 -0
  87. package/dist/lib/agent-assets/release-input.js +177 -0
  88. package/dist/lib/agent-assets/roots.js +45 -0
  89. package/dist/lib/agent-assets/runtime-binding-reader.js +545 -0
  90. package/dist/lib/agent-assets/runtime-targets.js +50 -0
  91. package/dist/lib/agent-assets/store-reader.js +1212 -0
  92. package/dist/lib/agent-assets/store-types.js +1 -0
  93. package/dist/lib/agent-assets/store-writer.js +1484 -0
  94. package/dist/lib/agent-assets/types.js +1 -0
  95. package/dist/lib/agent-assets/unicode-case-folding-v15-1.generated.js +1548 -0
  96. package/dist/lib/commercial-final-readiness.js +504 -0
  97. package/dist/lib/commercial-lifecycle-smoke-isolation.js +236 -0
  98. package/dist/lib/commercial-managed-postgres-docker-adapter.js +274 -0
  99. package/dist/lib/commercial-setup-readiness-adapter.js +179 -0
  100. package/dist/lib/community-client-contract.js +164 -0
  101. package/dist/lib/community-cockpit-lifecycle.js +601 -0
  102. package/dist/lib/community-command-abort.js +59 -0
  103. package/dist/lib/community-diagnostic.js +237 -0
  104. package/dist/lib/community-docker-adapter.js +498 -0
  105. package/dist/lib/community-home.js +31 -0
  106. package/dist/lib/community-instance-contract.js +148 -0
  107. package/dist/lib/community-instance-layout.js +97 -0
  108. package/dist/lib/community-lifecycle.js +1631 -0
  109. package/dist/lib/community-migration-snapshot.js +770 -0
  110. package/dist/lib/community-native-application-recovery.js +425 -0
  111. package/dist/lib/community-native-child.js +240 -0
  112. package/dist/lib/community-native-database-recovery.js +456 -0
  113. package/dist/lib/community-native-lifecycle.js +2458 -0
  114. package/dist/lib/community-native-migration.js +339 -0
  115. package/dist/lib/community-native-postgres.js +596 -0
  116. package/dist/lib/community-operation-journal.js +773 -0
  117. package/dist/lib/community-operation-lock.js +596 -0
  118. package/dist/lib/community-release-resolver.js +291 -0
  119. package/dist/lib/community-release-verifier.js +475 -0
  120. package/dist/lib/community-repo-discovery.js +252 -0
  121. package/dist/lib/community-setup-server-env.js +203 -0
  122. package/dist/lib/external-url.js +110 -0
  123. package/dist/lib/official-catalog-gateway.js +108 -0
  124. package/dist/lib/official-catalog.js +364 -0
  125. package/dist/lib/server-bootstrap.js +240 -0
  126. package/dist/lib/setup-agent-assets-bridge.js +176 -0
  127. package/dist/lib/setup-agent-assets-release.js +52 -0
  128. package/dist/lib/setup-external-postgres.js +108 -0
  129. package/dist/lib/setup-init-orchestrator.js +990 -0
  130. package/dist/lib/setup-install-guide.js +52 -0
  131. package/dist/lib/setup-installer-release.js +47 -0
  132. package/dist/lib/setup-local-postgres.js +329 -0
  133. package/dist/lib/setup-official-catalog-bridge.js +148 -0
  134. package/dist/lib/setup-readiness.js +888 -0
  135. package/dist/lib/tui-launcher.js +59 -0
  136. package/dist/main.js +150 -0
  137. package/dist/seeds/agents-md.js +54 -0
  138. package/dist/utils/agent-gateway.js +275 -0
  139. package/dist/utils/agents-md.js +281 -0
  140. package/dist/utils/api.js +255 -0
  141. package/dist/utils/chatv3-session-store.js +185 -0
  142. package/dist/utils/command.js +67 -0
  143. package/dist/utils/community-host-runtime.js +376 -0
  144. package/dist/utils/config.js +1015 -0
  145. package/dist/utils/discussion-workspace.js +552 -0
  146. package/dist/utils/experience-workspace.js +326 -0
  147. package/dist/utils/guide-paths.js +67 -0
  148. package/dist/utils/hosted-sugar.js +56 -0
  149. package/dist/utils/hosted-workspace.js +229 -0
  150. package/dist/utils/memory-workspace.js +537 -0
  151. package/dist/utils/operator-runtime.js +199 -0
  152. package/dist/utils/password-crypto.js +39 -0
  153. package/dist/utils/playbook-workspace.js +479 -0
  154. package/dist/utils/project-context.js +255 -0
  155. package/dist/utils/prompts.js +70 -0
  156. package/dist/utils/refresh-race.js +28 -0
  157. package/dist/utils/repo-config.js +109 -0
  158. package/dist/utils/repo-first-projectman.js +194 -0
  159. package/dist/utils/repo-first-storage.js +13 -0
  160. package/dist/utils/repo-first-view.js +305 -0
  161. package/dist/utils/session-guidance.js +278 -0
  162. package/dist/utils/session-state.js +137 -0
  163. package/dist/utils/table.js +39 -0
  164. package/dist/utils/transient-fs.js +49 -0
  165. package/dist/utils/workspace-root.js +1 -0
  166. package/launchers/aops-cockpit.ps1 +15 -0
  167. package/launchers/aops-cockpit.sh +16 -0
  168. package/launchers/aops-server.ps1 +15 -0
  169. package/launchers/aops-server.sh +16 -0
  170. package/native/tui/win32-x64/aops-tui.exe +0 -0
  171. package/package.json +81 -0
@@ -0,0 +1,456 @@
1
+ import { createHash, randomBytes, randomUUID } from 'node:crypto';
2
+ import { closeSync, constants, existsSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync, } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { compareCanonicalText } from '@aopslabs/artifact-trust-contracts';
5
+ import { createCommunityNativeApplicationReferenceV1, readCommunityNativeApplicationUpdate, sameCommunityNativeApplicationContent, } from './community-native-application-recovery.js';
6
+ import { assertCommunityNativeRestoredSnapshotStateV1, cleanupCommunityNativeManagedRestoreRescueV1, readCommunityNativeSnapshotEvidenceV1, restoreCommunityNativeManagedSnapshotV1, } from './community-migration-snapshot.js';
7
+ import { inspectCommunityNativeSource, loadExternalPostgresUrl, } from './community-native-lifecycle.js';
8
+ import { planCommunityNativeMigration } from './community-native-migration.js';
9
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
10
+ const RAW_SHA256 = /^[a-f0-9]{64}$/;
11
+ const PREFIXED_SHA256 = /^sha256:[a-f0-9]{64}$/;
12
+ const MAX_RECORD_BYTES = 1024 * 1024;
13
+ function sha256Json(value) {
14
+ return createHash('sha256').update(JSON.stringify(value)).digest('hex');
15
+ }
16
+ function samePath(left, right) {
17
+ const a = path.resolve(left);
18
+ const b = path.resolve(right);
19
+ return process.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b;
20
+ }
21
+ function fsyncDirectoryBestEffortOnWindows(directory) {
22
+ let descriptor;
23
+ try {
24
+ descriptor = openSync(directory, 'r');
25
+ fsyncSync(descriptor);
26
+ }
27
+ catch (error) {
28
+ const code = String(error.code);
29
+ if (process.platform !== 'win32' || !['EACCES', 'EBADF', 'EINVAL', 'EISDIR', 'EPERM'].includes(code))
30
+ throw error;
31
+ }
32
+ finally {
33
+ if (descriptor !== undefined)
34
+ closeSync(descriptor);
35
+ }
36
+ }
37
+ function ensureRestoreRoot(paths) {
38
+ const root = path.resolve(paths.databaseRestoreRoot);
39
+ if (!samePath(path.dirname(root), paths.runtimeRoot)) {
40
+ throw new Error('community_native_database_restore_root_unsafe');
41
+ }
42
+ const existed = existsSync(root);
43
+ mkdirSync(root, { recursive: true, mode: 0o700 });
44
+ const stats = lstatSync(root);
45
+ if (!stats.isDirectory() || stats.isSymbolicLink() || !samePath(realpathSync.native(root), root)) {
46
+ throw new Error('community_native_database_restore_root_unsafe');
47
+ }
48
+ if (!existed)
49
+ fsyncDirectoryBestEffortOnWindows(paths.runtimeRoot);
50
+ return root;
51
+ }
52
+ function writeExclusive(paths, fileName, value) {
53
+ const root = ensureRestoreRoot(paths);
54
+ const target = path.join(root, fileName);
55
+ if (!samePath(path.dirname(target), root) || existsSync(target)) {
56
+ throw new Error('community_native_database_restore_record_already_exists');
57
+ }
58
+ const temp = path.join(root, `.${fileName}.${process.pid}.${randomBytes(8).toString('hex')}.tmp`);
59
+ let descriptor;
60
+ try {
61
+ descriptor = openSync(temp, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 0o600);
62
+ writeFileSync(descriptor, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8' });
63
+ fsyncSync(descriptor);
64
+ closeSync(descriptor);
65
+ descriptor = undefined;
66
+ try {
67
+ linkSync(temp, target);
68
+ }
69
+ catch (error) {
70
+ if (error.code === 'EEXIST') {
71
+ throw new Error('community_native_database_restore_record_already_exists');
72
+ }
73
+ throw error;
74
+ }
75
+ fsyncDirectoryBestEffortOnWindows(root);
76
+ rmSync(temp, { force: true });
77
+ fsyncDirectoryBestEffortOnWindows(root);
78
+ return target;
79
+ }
80
+ finally {
81
+ if (descriptor !== undefined)
82
+ closeSync(descriptor);
83
+ rmSync(temp, { force: true });
84
+ }
85
+ }
86
+ function readRecord(paths, fileName) {
87
+ const root = ensureRestoreRoot(paths);
88
+ const target = path.join(root, fileName);
89
+ if (!existsSync(target))
90
+ return null;
91
+ const stats = lstatSync(target, { bigint: true });
92
+ if (!stats.isFile() || stats.isSymbolicLink() || stats.size < 1n || stats.size > BigInt(MAX_RECORD_BYTES) ||
93
+ !samePath(realpathSync.native(target), target) || !samePath(path.dirname(target), root)) {
94
+ throw new Error('community_native_database_restore_record_unsafe');
95
+ }
96
+ let descriptor;
97
+ try {
98
+ const noFollow = typeof constants.O_NOFOLLOW === 'number' ? constants.O_NOFOLLOW : 0;
99
+ descriptor = openSync(target, constants.O_RDONLY | noFollow);
100
+ const before = fstatSync(descriptor, { bigint: true });
101
+ if (before.dev !== stats.dev || before.ino !== stats.ino || before.size !== stats.size || before.nlink !== 1n) {
102
+ throw new Error('community_native_database_restore_record_unsafe');
103
+ }
104
+ const value = JSON.parse(readFileSync(descriptor, 'utf8'));
105
+ const after = fstatSync(descriptor, { bigint: true });
106
+ if (after.dev !== before.dev || after.ino !== before.ino || after.size !== before.size ||
107
+ after.nlink !== before.nlink || after.mtimeNs !== before.mtimeNs) {
108
+ throw new Error('community_native_database_restore_record_unsafe');
109
+ }
110
+ return value;
111
+ }
112
+ catch {
113
+ throw new Error('community_native_database_restore_record_invalid');
114
+ }
115
+ finally {
116
+ if (descriptor !== undefined)
117
+ closeSync(descriptor);
118
+ }
119
+ }
120
+ function isRecord(value) {
121
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
122
+ }
123
+ function exactKeys(value, expected, code) {
124
+ const actual = Object.keys(value).sort();
125
+ const wanted = [...expected].sort();
126
+ if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index]))
127
+ throw new Error(code);
128
+ }
129
+ function assertPrepared(value) {
130
+ if (!isRecord(value))
131
+ throw new Error('community_native_database_restore_prepared_invalid');
132
+ exactKeys(value, [
133
+ 'schemaVersion', 'status', 'restoreId', 'updateId', 'instanceName', 'installId', 'createdAt',
134
+ 'recoveryOwner', 'evidenceKind', 'evidencePath', 'evidenceSha256',
135
+ 'sourceApplicationContentSha256', 'targetApplicationContentSha256',
136
+ 'sourceMigrationStateFingerprintSha256',
137
+ ], 'community_native_database_restore_prepared_invalid');
138
+ if (value.schemaVersion !== 1 || value.status !== 'community-native-database-restore-prepared' ||
139
+ !UUID.test(String(value.restoreId)) || !UUID.test(String(value.updateId)) || !UUID.test(String(value.installId)) ||
140
+ typeof value.instanceName !== 'string' || value.instanceName.length < 1 || Number.isNaN(Date.parse(String(value.createdAt))) ||
141
+ !['managed', 'external'].includes(String(value.recoveryOwner)) ||
142
+ !['managed-verified-backup', 'external-snapshot-attestation'].includes(String(value.evidenceKind)) ||
143
+ !path.isAbsolute(String(value.evidencePath)) ||
144
+ ![value.evidenceSha256, value.sourceMigrationStateFingerprintSha256].every((item) => RAW_SHA256.test(String(item))) ||
145
+ ![value.sourceApplicationContentSha256, value.targetApplicationContentSha256]
146
+ .every((item) => PREFIXED_SHA256.test(String(item)))) {
147
+ throw new Error('community_native_database_restore_prepared_invalid');
148
+ }
149
+ if ((value.recoveryOwner === 'managed') !== (value.evidenceKind === 'managed-verified-backup')) {
150
+ throw new Error('community_native_database_restore_prepared_invalid');
151
+ }
152
+ return value;
153
+ }
154
+ function assertCompleted(value) {
155
+ if (!isRecord(value))
156
+ throw new Error('community_native_database_restore_completed_invalid');
157
+ exactKeys(value, [
158
+ 'schemaVersion', 'status', 'restoreId', 'updateId', 'instanceName', 'completedAt', 'recoveryOwner',
159
+ 'evidenceKind', 'evidenceSha256', 'restoredLineageId', 'restoredMigrationStateFingerprintSha256',
160
+ 'restoredSchemaFingerprintSha256', 'restoredReceiptFingerprintSha256', 'restoredDataFingerprintSha256',
161
+ 'restoredStateFingerprintSha256', 'rescueDatabase',
162
+ ], 'community_native_database_restore_completed_invalid');
163
+ if (value.schemaVersion !== 1 || value.status !== 'community-native-database-restored' ||
164
+ !UUID.test(String(value.restoreId)) || !UUID.test(String(value.updateId)) ||
165
+ typeof value.instanceName !== 'string' || value.instanceName.length < 1 ||
166
+ Number.isNaN(Date.parse(String(value.completedAt))) ||
167
+ !['managed', 'external'].includes(String(value.recoveryOwner)) ||
168
+ !['managed-verified-backup', 'external-snapshot-attestation'].includes(String(value.evidenceKind)) ||
169
+ !RAW_SHA256.test(String(value.evidenceSha256)) || typeof value.restoredLineageId !== 'string' ||
170
+ value.restoredLineageId.length < 1 ||
171
+ ![
172
+ value.restoredMigrationStateFingerprintSha256, value.restoredSchemaFingerprintSha256,
173
+ value.restoredReceiptFingerprintSha256, value.restoredDataFingerprintSha256,
174
+ value.restoredStateFingerprintSha256,
175
+ ].every((item) => RAW_SHA256.test(String(item))) ||
176
+ (value.rescueDatabase !== null && typeof value.rescueDatabase !== 'string')) {
177
+ throw new Error('community_native_database_restore_completed_invalid');
178
+ }
179
+ return value;
180
+ }
181
+ function findPrepared(paths, updateId) {
182
+ const root = ensureRestoreRoot(paths);
183
+ const matches = readdirSync(root)
184
+ .filter((name) => name.endsWith('.prepared.json'))
185
+ .map((name) => assertPrepared(readRecord(paths, name)))
186
+ .filter((record) => record.updateId === updateId);
187
+ if (matches.length > 1)
188
+ throw new Error('community_native_database_restore_prepared_ambiguous');
189
+ return matches[0] ?? null;
190
+ }
191
+ export function inspectCommunityNativeDatabaseRecoveryStatus(paths) {
192
+ if (!existsSync(paths.databaseRestoreRoot))
193
+ return null;
194
+ const root = ensureRestoreRoot(paths);
195
+ const preparedRecords = readdirSync(root)
196
+ .filter((name) => name.endsWith('.prepared.json'))
197
+ .map((name) => assertPrepared(readRecord(paths, name)))
198
+ .sort((left, right) => {
199
+ const byTime = Date.parse(right.createdAt) - Date.parse(left.createdAt);
200
+ return byTime === 0 ? compareCanonicalText(right.restoreId, left.restoreId) : byTime;
201
+ });
202
+ const prepared = preparedRecords[0];
203
+ if (!prepared)
204
+ return null;
205
+ const completedRaw = readRecord(paths, `${prepared.restoreId}.completed.json`);
206
+ const completed = completedRaw === null ? null : assertCompleted(completedRaw);
207
+ if (completed && (completed.restoreId !== prepared.restoreId ||
208
+ completed.updateId !== prepared.updateId ||
209
+ completed.instanceName !== prepared.instanceName ||
210
+ completed.recoveryOwner !== prepared.recoveryOwner ||
211
+ completed.evidenceKind !== prepared.evidenceKind ||
212
+ completed.evidenceSha256 !== prepared.evidenceSha256)) {
213
+ throw new Error('community_native_database_restore_completed_conflict');
214
+ }
215
+ return {
216
+ restoreId: prepared.restoreId,
217
+ updateId: prepared.updateId,
218
+ recoveryOwner: prepared.recoveryOwner,
219
+ evidenceKind: prepared.evidenceKind,
220
+ status: completed?.status ?? prepared.status,
221
+ preparedAt: prepared.createdAt,
222
+ completedAt: completed?.completedAt ?? null,
223
+ actionRequired: completed === null,
224
+ };
225
+ }
226
+ function writePhase(params) {
227
+ const fileName = `${params.prepared.restoreId}.${params.phase}.json`;
228
+ const existing = readRecord(params.paths, fileName);
229
+ if (existing !== null) {
230
+ if (!isRecord(existing) || existing.schemaVersion !== 1 || existing.status !== 'community-native-database-restore-phase' ||
231
+ existing.restoreId !== params.prepared.restoreId || existing.updateId !== params.prepared.updateId ||
232
+ existing.phase !== params.phase) {
233
+ throw new Error('community_native_database_restore_phase_conflict');
234
+ }
235
+ return;
236
+ }
237
+ writeExclusive(params.paths, fileName, {
238
+ schemaVersion: 1,
239
+ status: 'community-native-database-restore-phase',
240
+ restoreId: params.prepared.restoreId,
241
+ updateId: params.prepared.updateId,
242
+ phase: params.phase,
243
+ recordedAt: params.now().toISOString(),
244
+ });
245
+ }
246
+ function assertPriorSource(params) {
247
+ const source = inspectCommunityNativeSource(params.sourceRoot);
248
+ const expected = params.prepared.prior.content;
249
+ if (source.releaseVersion !== expected.releaseVersion || source.packageSha256 !== expected.packageSha256 ||
250
+ source.lockfileSha256 !== expected.lockfileSha256 || source.sourceFileCount !== expected.sourceFileCount ||
251
+ source.sourceInventorySha256 !== expected.sourceInventorySha256) {
252
+ throw new Error('community_native_database_restore_prior_source_mismatch');
253
+ }
254
+ }
255
+ function evidenceFileName(kind, planSha256) {
256
+ return `${kind === 'managed-verified-backup' ? 'managed' : 'external'}-${planSha256}.json`;
257
+ }
258
+ function completedResult(prepared, completed) {
259
+ return { prepared, completed, actionRequired: false, dataRewound: true, externalAction: null };
260
+ }
261
+ export async function restoreCommunityNativeDatabaseForUpdate(params, dependencies = {}) {
262
+ if (!UUID.test(params.updateId) || !path.isAbsolute(params.sourceRoot) || params.confirmDataRewind !== true) {
263
+ throw new Error('community_native_database_restore_confirmation_required');
264
+ }
265
+ if (params.signal?.aborted)
266
+ throw new Error('community_operation_aborted');
267
+ const now = dependencies.now ?? (() => new Date());
268
+ const update = readCommunityNativeApplicationUpdate(params.paths, params.updateId);
269
+ if (update.prepared.instanceName !== params.state.instanceName || update.prepared.installId !== params.state.installId) {
270
+ throw new Error('community_native_database_restore_update_instance_mismatch');
271
+ }
272
+ if (!update.outcome || !update.outcome.migration || update.outcome.migration.action !== 'migrate' ||
273
+ !update.outcome.migration.snapshotEvidenceKind ||
274
+ !update.outcome.migration.snapshotEvidenceSha256) {
275
+ throw new Error('community_native_database_restore_migrating_update_required');
276
+ }
277
+ assertPriorSource({ sourceRoot: params.sourceRoot, prepared: update.prepared });
278
+ const migration = update.outcome.migration;
279
+ const evidenceKind = migration.snapshotEvidenceKind;
280
+ const evidenceSha256 = migration.snapshotEvidenceSha256;
281
+ if (evidenceKind === null || evidenceSha256 === null) {
282
+ throw new Error('community_native_database_restore_migrating_update_required');
283
+ }
284
+ const evidencePath = path.join(params.paths.migrationEvidenceRoot, evidenceFileName(evidenceKind, migration.acceptedPlanSha256));
285
+ const evidenceRead = readCommunityNativeSnapshotEvidenceV1({
286
+ evidenceRoot: params.paths.migrationEvidenceRoot,
287
+ evidencePath,
288
+ expectedEvidenceSha256: evidenceSha256,
289
+ expectedKind: evidenceKind,
290
+ });
291
+ if (evidenceRead.evidence.acceptedPlanSha256 !== migration.acceptedPlanSha256 ||
292
+ evidenceRead.evidence.sourceMigrationStateFingerprintSha256 !== migration.sourceMigrationStateFingerprintSha256) {
293
+ throw new Error('community_native_database_restore_evidence_update_mismatch');
294
+ }
295
+ const recoveryOwner = evidenceRead.evidence.kind === 'managed-verified-backup' ? 'managed' : 'external';
296
+ let prepared = findPrepared(params.paths, params.updateId);
297
+ if (!prepared) {
298
+ const restoreId = (dependencies.createId ?? randomUUID)();
299
+ prepared = assertPrepared({
300
+ schemaVersion: 1,
301
+ status: 'community-native-database-restore-prepared',
302
+ restoreId,
303
+ updateId: params.updateId,
304
+ instanceName: params.state.instanceName,
305
+ installId: params.state.installId,
306
+ createdAt: now().toISOString(),
307
+ recoveryOwner,
308
+ evidenceKind: evidenceRead.evidence.kind,
309
+ evidencePath: evidenceRead.evidencePath,
310
+ evidenceSha256: evidenceRead.evidenceSha256,
311
+ sourceApplicationContentSha256: update.prepared.prior.content.applicationContentSha256,
312
+ targetApplicationContentSha256: update.prepared.target.content.applicationContentSha256,
313
+ sourceMigrationStateFingerprintSha256: migration.sourceMigrationStateFingerprintSha256,
314
+ });
315
+ writeExclusive(params.paths, `${prepared.restoreId}.prepared.json`, prepared);
316
+ }
317
+ if (prepared.updateId !== params.updateId || prepared.evidenceSha256 !== evidenceRead.evidenceSha256 ||
318
+ prepared.evidenceKind !== evidenceRead.evidence.kind || !samePath(prepared.evidencePath, evidenceRead.evidencePath) ||
319
+ prepared.recoveryOwner !== recoveryOwner) {
320
+ throw new Error('community_native_database_restore_prepared_conflict');
321
+ }
322
+ const completedRaw = readRecord(params.paths, `${prepared.restoreId}.completed.json`);
323
+ if (completedRaw !== null) {
324
+ const completed = assertCompleted(completedRaw);
325
+ if (completed.restoreId !== prepared.restoreId || completed.updateId !== prepared.updateId ||
326
+ completed.evidenceSha256 !== prepared.evidenceSha256) {
327
+ throw new Error('community_native_database_restore_completed_conflict');
328
+ }
329
+ if (completed.recoveryOwner === 'managed' && params.state.profile === 'native-container-postgres') {
330
+ await (dependencies.cleanupManagedRescue ?? cleanupCommunityNativeManagedRestoreRescueV1)({
331
+ restoreId: prepared.restoreId,
332
+ receiptRoot: params.paths.databaseRestoreRoot,
333
+ completedReceiptPath: path.join(params.paths.databaseRestoreRoot, `${prepared.restoreId}.completed.json`),
334
+ expectedCompletedReceiptSha256: sha256Json(completed),
335
+ evidenceRoot: params.paths.migrationEvidenceRoot,
336
+ evidencePath: prepared.evidencePath,
337
+ expectedEvidenceSha256: prepared.evidenceSha256,
338
+ sourceRoot: path.resolve(params.sourceRoot),
339
+ state: params.state.postgres,
340
+ instanceName: params.state.instanceName,
341
+ instanceRoot: params.paths.instanceRoot,
342
+ runtime: dependencies.postgresRuntime,
343
+ planStrictPgSchema: dependencies.planStrictPgSchema,
344
+ signal: params.signal,
345
+ });
346
+ }
347
+ return completedResult(prepared, completed);
348
+ }
349
+ const active = createCommunityNativeApplicationReferenceV1(params.state);
350
+ if (!sameCommunityNativeApplicationContent(active, update.prepared.target)) {
351
+ throw new Error('community_native_database_restore_update_target_not_active');
352
+ }
353
+ if (evidenceRead.evidence.kind === 'external-snapshot-attestation') {
354
+ if (params.state.profile !== 'native-external-postgres') {
355
+ throw new Error('community_native_database_restore_external_profile_mismatch');
356
+ }
357
+ if (params.confirmExternalRestoreComplete !== true) {
358
+ return {
359
+ prepared,
360
+ completed: null,
361
+ actionRequired: true,
362
+ dataRewound: false,
363
+ externalAction: {
364
+ provider: evidenceRead.evidence.provider,
365
+ snapshotRef: evidenceRead.evidence.snapshotRef,
366
+ snapshotDigest: evidenceRead.evidence.snapshotDigest,
367
+ restoreInstructionsRef: evidenceRead.evidence.restoreInstructionsRef,
368
+ },
369
+ };
370
+ }
371
+ const repoUrl = loadExternalPostgresUrl(params.state.postgres.configRef, params.state.postgres.tlsPolicy);
372
+ const planned = await (dependencies.planNativeMigration ?? planCommunityNativeMigration)({
373
+ sourceRoot: path.resolve(params.sourceRoot),
374
+ repoUrl,
375
+ signal: params.signal,
376
+ });
377
+ assertCommunityNativeRestoredSnapshotStateV1(evidenceRead.evidence, planned.planning);
378
+ const completed = assertCompleted({
379
+ schemaVersion: 1,
380
+ status: 'community-native-database-restored',
381
+ restoreId: prepared.restoreId,
382
+ updateId: prepared.updateId,
383
+ instanceName: prepared.instanceName,
384
+ completedAt: now().toISOString(),
385
+ recoveryOwner: 'external',
386
+ evidenceKind: evidenceRead.evidence.kind,
387
+ evidenceSha256: evidenceRead.evidenceSha256,
388
+ restoredLineageId: planned.planning.lineageId,
389
+ restoredMigrationStateFingerprintSha256: planned.planning.sourceFingerprintSha256,
390
+ restoredSchemaFingerprintSha256: planned.planning.schemaFingerprintSha256,
391
+ restoredReceiptFingerprintSha256: planned.planning.receiptFingerprintSha256,
392
+ restoredDataFingerprintSha256: evidenceRead.evidence.sourceDataFingerprintSha256,
393
+ restoredStateFingerprintSha256: planned.planning.stateFingerprintSha256,
394
+ rescueDatabase: null,
395
+ });
396
+ writeExclusive(params.paths, `${prepared.restoreId}.completed.json`, completed);
397
+ return completedResult(prepared, completed);
398
+ }
399
+ if (params.confirmExternalRestoreComplete === true) {
400
+ throw new Error('community_native_database_restore_external_confirmation_unexpected');
401
+ }
402
+ if (params.state.profile !== 'native-container-postgres') {
403
+ throw new Error('community_native_database_restore_managed_profile_mismatch');
404
+ }
405
+ const restored = await (dependencies.restoreManagedSnapshot ?? restoreCommunityNativeManagedSnapshotV1)({
406
+ restoreId: prepared.restoreId,
407
+ evidenceRoot: params.paths.migrationEvidenceRoot,
408
+ evidencePath: prepared.evidencePath,
409
+ expectedEvidenceSha256: prepared.evidenceSha256,
410
+ backupRoot: params.paths.backupRoot,
411
+ sourceRoot: path.resolve(params.sourceRoot),
412
+ state: params.state.postgres,
413
+ instanceName: params.state.instanceName,
414
+ instanceRoot: params.paths.instanceRoot,
415
+ runtime: dependencies.postgresRuntime,
416
+ planStrictPgSchema: dependencies.planStrictPgSchema,
417
+ onPhase: ({ phase }) => writePhase({ paths: params.paths, prepared: prepared, phase, now }),
418
+ signal: params.signal,
419
+ });
420
+ const completed = assertCompleted({
421
+ schemaVersion: 1,
422
+ status: 'community-native-database-restored',
423
+ restoreId: prepared.restoreId,
424
+ updateId: prepared.updateId,
425
+ instanceName: prepared.instanceName,
426
+ completedAt: now().toISOString(),
427
+ recoveryOwner: 'managed',
428
+ evidenceKind: evidenceRead.evidence.kind,
429
+ evidenceSha256: evidenceRead.evidenceSha256,
430
+ restoredLineageId: restored.restoredLineageId,
431
+ restoredMigrationStateFingerprintSha256: restored.restoredMigrationStateFingerprintSha256,
432
+ restoredSchemaFingerprintSha256: restored.restoredSchemaFingerprintSha256,
433
+ restoredReceiptFingerprintSha256: restored.restoredReceiptFingerprintSha256,
434
+ restoredDataFingerprintSha256: restored.restoredDataFingerprintSha256,
435
+ restoredStateFingerprintSha256: restored.restoredStateFingerprintSha256,
436
+ rescueDatabase: restored.rescueDatabase,
437
+ });
438
+ const completedReceiptPath = writeExclusive(params.paths, `${prepared.restoreId}.completed.json`, completed);
439
+ await (dependencies.cleanupManagedRescue ?? cleanupCommunityNativeManagedRestoreRescueV1)({
440
+ restoreId: prepared.restoreId,
441
+ receiptRoot: params.paths.databaseRestoreRoot,
442
+ completedReceiptPath,
443
+ expectedCompletedReceiptSha256: sha256Json(completed),
444
+ evidenceRoot: params.paths.migrationEvidenceRoot,
445
+ evidencePath: prepared.evidencePath,
446
+ expectedEvidenceSha256: prepared.evidenceSha256,
447
+ sourceRoot: path.resolve(params.sourceRoot),
448
+ state: params.state.postgres,
449
+ instanceName: params.state.instanceName,
450
+ instanceRoot: params.paths.instanceRoot,
451
+ runtime: dependencies.postgresRuntime,
452
+ planStrictPgSchema: dependencies.planStrictPgSchema,
453
+ signal: params.signal,
454
+ });
455
+ return completedResult(prepared, completed);
456
+ }