@aopslabs/aops 0.3.24 → 0.3.26

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 (64) hide show
  1. package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/SKILL.md +0 -1
  2. package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/references/agentspace/SKILL.md +0 -6
  3. package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/references/collaborative-work/SKILL.md +1 -2
  4. package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/references/working-disciplines/SKILL.md +2 -5
  5. package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/user-guides/agentspace.md +1 -16
  6. package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/user-guides/aops-cli.md +4 -33
  7. package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/user-guides/working-disciplines.md +32 -138
  8. package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/manifest.json +17 -22
  9. package/agent-assets-release/agent-assets/inventory.json +5 -5
  10. package/agent-assets-release/agent-assets/projection.json +1 -1
  11. package/agent-assets-release/agent-assets-release.json +48 -53
  12. package/agent-assets-release/agent-assets-release.sigstore.json +27 -28
  13. package/assets/agent-assets/core/SKILL.md +0 -1
  14. package/assets/agent-assets/core/references/agentspace/SKILL.md +0 -6
  15. package/assets/agent-assets/core/references/collaborative-work/SKILL.md +1 -2
  16. package/assets/agent-assets/core/references/working-disciplines/SKILL.md +2 -5
  17. package/assets/agent-assets/core/user-guides/agentspace.md +1 -16
  18. package/assets/agent-assets/core/user-guides/aops-cli.md +4 -33
  19. package/assets/agent-assets/core/user-guides/working-disciplines.md +32 -138
  20. package/dist/commands/checkpoint.js +4 -23
  21. package/dist/commands/community-server.js +212 -6
  22. package/dist/commands/db.js +145 -0
  23. package/dist/commands/experience.js +0 -2
  24. package/dist/commands/memory.js +8 -2
  25. package/dist/commands/official-catalog.js +1 -1
  26. package/dist/commands/plan.js +2 -2
  27. package/dist/commands/pm/index.js +39 -3
  28. package/dist/commands/pm/projectman.js +281 -8
  29. package/dist/commands/start-disciplines.js +8 -21
  30. package/dist/commands/start.js +20 -504
  31. package/dist/lib/agent-assets/native-fs.js +2 -2
  32. package/dist/lib/community-migration-snapshot.js +43 -2
  33. package/dist/lib/community-native-backup.js +386 -0
  34. package/dist/lib/community-native-lifecycle.js +93 -4
  35. package/dist/lib/community-native-migration.js +20 -0
  36. package/dist/main.js +2 -2
  37. package/dist/seeds/agents-md.js +1 -1
  38. package/dist/utils/playbook-workspace.js +5 -33
  39. package/dist/utils/session-guidance.js +0 -7
  40. package/native/bin/darwin-arm64/aops-agent-assets-fs +0 -0
  41. package/native/bin/darwin-x64/aops-agent-assets-fs +0 -0
  42. package/native/bin/win32-x64/aops-agent-assets-fs.exe +0 -0
  43. package/native/manifest.json +4 -4
  44. package/native/qualifications/win32-x64.json +2 -2
  45. package/native/tui/darwin-arm64/aops-tui +0 -0
  46. package/native/tui/linux-x64/aops-tui +0 -0
  47. package/native/tui/win32-x64/aops-tui.exe +0 -0
  48. package/package.json +18 -6
  49. package/agent-assets-release/agent-assets/core/647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09/files/references/mission/SKILL.md +0 -54
  50. package/assets/agent-assets/core/references/mission/SKILL.md +0 -54
  51. package/dist/commands/mission.js +0 -562
  52. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/references/aops-cli-core/SKILL.md +0 -0
  53. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/references/chatv3/SKILL.md +0 -0
  54. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/references/discuss/SKILL.md +0 -0
  55. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/references/docman/SKILL.md +0 -0
  56. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/references/projectman/SKILL.md +0 -0
  57. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/references/sys/SKILL.md +0 -0
  58. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/references/view/SKILL.md +0 -0
  59. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/user-guides/agent-assets.md +0 -0
  60. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/user-guides/aops-system.md +0 -0
  61. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/user-guides/chatv3.md +0 -0
  62. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/user-guides/docman.md +0 -0
  63. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/user-guides/projectman.md +0 -0
  64. /package/agent-assets-release/agent-assets/core/{647c6e84fbc48f9a36945d81660f4d2a5241f58a53a18054365bfc16868b0f09 → bc42dd7792dbe66040e99a7ef3d80b3bc788c72b369b5a065cf936aef29f53fb}/files/user-guides/sys.md +0 -0
@@ -543,9 +543,9 @@ function defaultNativeRoot() {
543
543
  return moduleDirectory.endsWith(`${path.sep}dist`) ? bundled : compiledOrSource;
544
544
  }
545
545
  function injectedNativeManifestSha256() {
546
- return typeof "99724da24c7a8af713cd05f83646af0ddbf95c89fc892ce31f0edc7d21874483" === 'undefined'
546
+ return typeof "d079a8a4ff6a05567912c963ac82f52259e980fafc22a041e4934932cdf7c2dd" === 'undefined'
547
547
  ? undefined
548
- : "99724da24c7a8af713cd05f83646af0ddbf95c89fc892ce31f0edc7d21874483";
548
+ : "d079a8a4ff6a05567912c963ac82f52259e980fafc22a041e4934932cdf7c2dd";
549
549
  }
550
550
  function sha256(bytes) {
551
551
  return createHash('sha256').update(bytes).digest('hex');
@@ -265,6 +265,33 @@ export function readCommunityNativeSnapshotEvidenceV1(params) {
265
265
  }
266
266
  return { evidencePath, evidenceSha256, evidence };
267
267
  }
268
+ export function assertBackupUnchanged(params) {
269
+ const reread = hashHeldFile(params.heldFd, false);
270
+ if (reread.sha256 !== params.expectedSha256 || reread.byteLength !== params.expectedByteLength) {
271
+ throw new Error('community_native_snapshot_backup_identity_changed');
272
+ }
273
+ }
274
+ export function openBackupForVerify(params) {
275
+ const noFollow = typeof constants.O_NOFOLLOW === 'number' ? constants.O_NOFOLLOW : 0;
276
+ const verifyFd = openSync(params.backupPath, constants.O_RDONLY | noFollow);
277
+ try {
278
+ const written = fstatSync(params.writtenFd, { bigint: true });
279
+ const opened = fstatSync(verifyFd, { bigint: true });
280
+ if (!opened.isFile() || opened.nlink !== 1n ||
281
+ opened.dev !== written.dev || opened.ino !== written.ino ||
282
+ opened.size !== BigInt(params.expectedByteLength)) {
283
+ throw new Error('community_native_snapshot_backup_identity_changed');
284
+ }
285
+ }
286
+ catch (error) {
287
+ try {
288
+ closeSync(verifyFd);
289
+ }
290
+ catch { }
291
+ throw error;
292
+ }
293
+ return verifyFd;
294
+ }
268
295
  async function runDocker(params) {
269
296
  const result = await params.runtime.run({
270
297
  command: 'docker',
@@ -298,7 +325,7 @@ async function runDockerCapture(params) {
298
325
  }
299
326
  return result.stdout;
300
327
  }
301
- function hashHeldFile(fd, syncBeforeRead = true) {
328
+ export function hashHeldFile(fd, syncBeforeRead = true) {
302
329
  if (syncBeforeRead)
303
330
  fsyncSync(fd);
304
331
  const before = fstatSync(fd, { bigint: true });
@@ -473,13 +500,18 @@ export async function createCommunityNativeManagedSnapshotV1(params) {
473
500
  operation: 'verify_create',
474
501
  signal: params.signal,
475
502
  });
503
+ const verifyFd = openBackupForVerify({
504
+ backupPath,
505
+ writtenFd: backupFd,
506
+ expectedByteLength: backup.byteLength,
507
+ });
476
508
  try {
477
509
  await runDocker({
478
510
  runtime,
479
511
  args: ['container', 'exec', '-i', state.containerName, 'pg_restore', '-U', 'aops',
480
512
  '-d', verifyDatabase, '--no-owner', '--no-acl', '--exit-on-error'],
481
513
  operation: 'verify_restore',
482
- inputFd: backupFd,
514
+ inputFd: verifyFd,
483
515
  signal: params.signal,
484
516
  });
485
517
  const restored = await (params.planStrictPgSchema ?? planCommunityStrictPgSchema)({
@@ -490,6 +522,10 @@ export async function createCommunityNativeManagedSnapshotV1(params) {
490
522
  assertRestoredState(params.planning, restored);
491
523
  }
492
524
  finally {
525
+ try {
526
+ closeSync(verifyFd);
527
+ }
528
+ catch { }
493
529
  await runDocker({
494
530
  runtime,
495
531
  args: ['container', 'exec', state.containerName, 'dropdb', '-U', 'aops', '--if-exists', '--force', verifyDatabase],
@@ -497,6 +533,11 @@ export async function createCommunityNativeManagedSnapshotV1(params) {
497
533
  signal: params.signal,
498
534
  });
499
535
  }
536
+ assertBackupUnchanged({
537
+ heldFd: backupFd,
538
+ expectedSha256: backup.sha256,
539
+ expectedByteLength: backup.byteLength,
540
+ });
500
541
  const dataFingerprint = sourceDataFingerprint(params.planning);
501
542
  const evidence = {
502
543
  schemaVersion: 1,
@@ -0,0 +1,386 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { closeSync, constants, existsSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, statSync, writeFileSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { assertBackupUnchanged, hashHeldFile, openBackupForVerify } from './community-migration-snapshot.js';
6
+ export async function runNativePgProcess(params) {
7
+ return await new Promise((resolve, reject) => {
8
+ const child = spawn(params.tool, params.args, {
9
+ env: params.env ? { ...process.env, ...params.env } : process.env,
10
+ stdio: [
11
+ params.inputFd === undefined ? 'ignore' : params.inputFd,
12
+ params.outputFd === undefined ? 'ignore' : params.outputFd,
13
+ 'inherit',
14
+ ],
15
+ shell: false,
16
+ });
17
+ child.on('error', reject);
18
+ child.on('close', (exitCode, signal) => resolve({ exitCode, signal }));
19
+ });
20
+ }
21
+ export function pgEnvironmentFromUrl(repoUrl) {
22
+ let parsed;
23
+ try {
24
+ parsed = new URL(repoUrl);
25
+ }
26
+ catch {
27
+ throw new Error('community_native_backup_repo_url_invalid');
28
+ }
29
+ if (parsed.protocol !== 'postgres:' && parsed.protocol !== 'postgresql:') {
30
+ throw new Error('community_native_backup_repo_url_invalid');
31
+ }
32
+ const database = decodeURIComponent(parsed.pathname.replace(/^\//, ''));
33
+ if (!database)
34
+ throw new Error('community_native_backup_repo_url_invalid');
35
+ const env = {
36
+ PGHOST: parsed.hostname,
37
+ PGDATABASE: database,
38
+ };
39
+ if (parsed.port)
40
+ env.PGPORT = parsed.port;
41
+ if (parsed.username)
42
+ env.PGUSER = decodeURIComponent(parsed.username);
43
+ if (parsed.password)
44
+ env.PGPASSWORD = decodeURIComponent(parsed.password);
45
+ const sslmode = parsed.searchParams.get('sslmode');
46
+ if (sslmode)
47
+ env.PGSSLMODE = sslmode;
48
+ return env;
49
+ }
50
+ export function resolvePgTool(tool, options = {}) {
51
+ const exists = options.exists ?? existsSync;
52
+ const executable = process.platform === 'win32' ? `${tool}.exe` : tool;
53
+ if (options.explicit)
54
+ return options.explicit;
55
+ if (options.binDir)
56
+ return path.join(options.binDir, executable);
57
+ const candidates = process.platform === 'darwin'
58
+ ? ['/Applications/pgAdmin 4.app/Contents/SharedSupport', '/opt/homebrew/bin', '/usr/local/bin']
59
+ : process.platform === 'win32'
60
+ ? ['C:\\Program Files\\PostgreSQL\\17\\bin', 'C:\\Program Files\\PostgreSQL\\16\\bin']
61
+ : ['/usr/bin', '/usr/local/bin'];
62
+ for (const dir of candidates) {
63
+ const candidate = path.join(dir, executable);
64
+ if (exists(candidate))
65
+ return candidate;
66
+ }
67
+ return executable;
68
+ }
69
+ function assertSafeBackupRoot(backupRoot) {
70
+ mkdirSync(backupRoot, { recursive: true, mode: 0o700 });
71
+ const stats = lstatSync(backupRoot, { bigint: true });
72
+ if (stats.isSymbolicLink() || !stats.isDirectory()) {
73
+ throw new Error('community_backup_root_unsafe');
74
+ }
75
+ return realpathSync.native(backupRoot);
76
+ }
77
+ export function verifyHeldBackupAgainstReceipt(params) {
78
+ const hashed = hashHeldFile(params.heldFd, false);
79
+ if (hashed.byteLength !== params.receipt.byteLength) {
80
+ throw new Error('community_native_backup_size_mismatch');
81
+ }
82
+ if (hashed.sha256 !== params.receipt.sha256) {
83
+ throw new Error('community_native_backup_digest_mismatch');
84
+ }
85
+ }
86
+ export function planSchemaRewind(params) {
87
+ const inArchive = new Set([...params.archiveSchemas, 'public']);
88
+ const orphaned = params.databaseSchemas.filter((name) => !inArchive.has(name));
89
+ if (orphaned.length > 0) {
90
+ throw new Error(`community_native_restore_schema_not_in_backup:${orphaned.join(',')}:` +
91
+ 'dropping_would_lose_them_keeping_would_leave_a_partial_rewind');
92
+ }
93
+ const drop = new Set(params.databaseSchemas);
94
+ drop.add('public');
95
+ return { drop: [...drop].sort() };
96
+ }
97
+ export function quoteSchemaIdentifier(name) {
98
+ if (name === '' || name.includes('\u0000')) {
99
+ throw new Error('community_native_restore_schema_name_unrepresentable');
100
+ }
101
+ return `"${name.replace(/"/g, '""')}"`;
102
+ }
103
+ export function parseArchiveSchemas(pgRestoreListOutput) {
104
+ const names = new Set();
105
+ for (const line of pgRestoreListOutput.split('\n')) {
106
+ const match = /\bSCHEMA\s+-\s+(\S+)/.exec(line);
107
+ if (match?.[1])
108
+ names.add(match[1]);
109
+ }
110
+ return [...names].sort();
111
+ }
112
+ export async function resolveSchemaRewindPlan(params) {
113
+ const pgEnv = pgEnvironmentFromUrl(params.repoUrl);
114
+ const capture = params.captureOutput ?? defaultCaptureOutput;
115
+ const query = "select nspname from pg_namespace where nspname not in ('information_schema','pg_catalog','pg_toast') and nspname not like 'pg_temp%' and nspname not like 'pg_toast_temp%'";
116
+ const databaseSchemas = (await capture({
117
+ tool: resolvePgTool('psql', { explicit: params.psql, binDir: params.pgBinDir }),
118
+ args: ['--dbname', String(pgEnv.PGDATABASE), '--tuples-only', '--no-align', '--quiet', '--command', query],
119
+ env: pgEnv,
120
+ }))
121
+ .split('\n')
122
+ .map((line) => line.trim())
123
+ .filter(Boolean);
124
+ const archiveSchemas = parseArchiveSchemas(await capture(params.archiveFd === undefined
125
+ ? {
126
+ tool: resolvePgTool('pg_restore', { explicit: params.pgRestore, binDir: params.pgBinDir }),
127
+ args: ['--list', params.archivePath],
128
+ }
129
+ : {
130
+ tool: resolvePgTool('pg_restore', { explicit: params.pgRestore, binDir: params.pgBinDir }),
131
+ args: ['--list'],
132
+ inputFd: params.archiveFd,
133
+ }));
134
+ return planSchemaRewind({ databaseSchemas, archiveSchemas });
135
+ }
136
+ async function defaultCaptureOutput(params) {
137
+ return await new Promise((resolve, reject) => {
138
+ const child = spawn(params.tool, params.args, {
139
+ stdio: [params.inputFd ?? 'ignore', 'pipe', 'inherit'],
140
+ shell: false,
141
+ env: params.env ? { ...process.env, ...params.env } : process.env,
142
+ });
143
+ let out = '';
144
+ const stdout = child.stdout;
145
+ if (!stdout) {
146
+ reject(new Error('community_native_restore_schema_probe_no_output'));
147
+ return;
148
+ }
149
+ stdout.setEncoding('utf8');
150
+ stdout.on('data', (chunk) => { out += chunk; });
151
+ child.on('error', reject);
152
+ child.on('close', (code) => {
153
+ if (code !== 0)
154
+ reject(new Error(`community_native_restore_schema_probe_failed:exit_${String(code)}`));
155
+ else
156
+ resolve(out);
157
+ });
158
+ });
159
+ }
160
+ function hashBackupAtPath(backupPath) {
161
+ const fd = openSync(backupPath, constants.O_RDONLY | constants.O_NOFOLLOW);
162
+ try {
163
+ return hashHeldFile(fd, false).sha256;
164
+ }
165
+ finally {
166
+ try {
167
+ closeSync(fd);
168
+ }
169
+ catch { }
170
+ }
171
+ }
172
+ export async function verifyCommunityNativeBackupV1(params) {
173
+ const readText = params.readFileText ?? ((candidate) => readFileSync(candidate, 'utf8'));
174
+ const receiptPath = `${params.backupPath}.json`;
175
+ if (!existsSync(receiptPath))
176
+ throw new Error('community_native_backup_receipt_missing');
177
+ let record;
178
+ try {
179
+ record = JSON.parse(readText(receiptPath));
180
+ }
181
+ catch {
182
+ throw new Error('community_native_backup_receipt_unreadable');
183
+ }
184
+ if (record?.kind !== 'native-pg-custom' || record.schemaVersion !== 1 || record.verified !== true) {
185
+ throw new Error('community_native_backup_receipt_invalid');
186
+ }
187
+ if (record.verifiedBy !== undefined && record.verifiedBy !== 'pg-restore-list') {
188
+ throw new Error('community_native_backup_receipt_unknown_verification_depth');
189
+ }
190
+ if (path.resolve(String(record.path)) !== path.resolve(params.backupPath)) {
191
+ throw new Error('community_native_backup_receipt_path_mismatch');
192
+ }
193
+ const stats = params.statFile ? params.statFile(params.backupPath) : statSync(params.backupPath);
194
+ if (stats.size !== record.byteLength)
195
+ throw new Error('community_native_backup_size_mismatch');
196
+ const digest = params.hashFile ? params.hashFile(params.backupPath) : hashBackupAtPath(params.backupPath);
197
+ if (digest !== record.sha256)
198
+ throw new Error('community_native_backup_digest_mismatch');
199
+ return record;
200
+ }
201
+ export async function createCommunityNativeBackupV1(params) {
202
+ const run = params.runProcess ?? runNativePgProcess;
203
+ const now = params.now ?? (() => new Date());
204
+ const backupRoot = assertSafeBackupRoot(params.backupRoot);
205
+ const label = params.label ? `-${params.label.replace(/[^A-Za-z0-9._-]/g, '_').slice(0, 64)}` : '';
206
+ const backupPath = path.join(backupRoot, `${now().toISOString().replace(/[:.]/g, '-')}${label}-${randomUUID()}.dump`);
207
+ const noFollow = typeof constants.O_NOFOLLOW === 'number' ? constants.O_NOFOLLOW : 0;
208
+ const backupFd = openSync(backupPath, constants.O_CREAT | constants.O_EXCL | constants.O_RDWR | noFollow, 0o600);
209
+ let keep = false;
210
+ try {
211
+ const pgEnv = pgEnvironmentFromUrl(params.repoUrl);
212
+ const dump = await run({
213
+ tool: resolvePgTool('pg_dump', { explicit: params.pgDump, binDir: params.pgBinDir }),
214
+ args: ['--format=custom', '--no-owner', '--no-acl'],
215
+ outputFd: backupFd,
216
+ env: pgEnv,
217
+ });
218
+ if (dump.exitCode !== 0) {
219
+ throw new Error(`community_native_backup_dump_failed:exit_${String(dump.exitCode ?? 'null')}`);
220
+ }
221
+ const hashed = hashHeldFile(backupFd);
222
+ const verifyFd = openBackupForVerify({
223
+ backupPath,
224
+ writtenFd: backupFd,
225
+ expectedByteLength: hashed.byteLength,
226
+ });
227
+ try {
228
+ const verify = await run({
229
+ tool: resolvePgTool('pg_restore', { explicit: params.pgRestore, binDir: params.pgBinDir }),
230
+ args: ['--list'],
231
+ inputFd: verifyFd,
232
+ });
233
+ if (verify.exitCode !== 0) {
234
+ throw new Error(`community_native_backup_verify_failed:exit_${String(verify.exitCode ?? 'null')}`);
235
+ }
236
+ }
237
+ finally {
238
+ try {
239
+ closeSync(verifyFd);
240
+ }
241
+ catch { }
242
+ }
243
+ assertBackupUnchanged({
244
+ heldFd: backupFd,
245
+ expectedSha256: hashed.sha256,
246
+ expectedByteLength: hashed.byteLength,
247
+ });
248
+ const record = {
249
+ schemaVersion: 1,
250
+ kind: 'native-pg-custom',
251
+ path: backupPath,
252
+ sha256: hashed.sha256,
253
+ byteLength: hashed.byteLength,
254
+ verified: true,
255
+ verifiedBy: 'pg-restore-list',
256
+ createdAt: now().toISOString(),
257
+ instanceName: params.instanceName,
258
+ releaseVersion: params.releaseVersion ?? null,
259
+ };
260
+ const receiptPath = `${backupPath}.json`;
261
+ if (existsSync(receiptPath))
262
+ throw new Error('community_native_backup_receipt_already_exists');
263
+ writeFileSync(receiptPath, `${JSON.stringify(record, null, 2)}\n`, { mode: 0o600, flag: 'wx' });
264
+ keep = true;
265
+ return { record, receiptPath };
266
+ }
267
+ finally {
268
+ try {
269
+ closeSync(backupFd);
270
+ }
271
+ catch { }
272
+ if (!keep) {
273
+ try {
274
+ const { unlinkSync } = await import('node:fs');
275
+ unlinkSync(backupPath);
276
+ }
277
+ catch { }
278
+ }
279
+ }
280
+ }
281
+ export async function restoreCommunityNativeBackupV1(params) {
282
+ if (params.confirmDataRewind !== true) {
283
+ throw new Error('community_native_restore_confirmation_required:pass_--confirm-data-rewind');
284
+ }
285
+ if (params.skipPreRestoreBackup === true && params.confirmNoUndo !== true) {
286
+ throw new Error('community_native_restore_no_undo_confirmation_required:pass_--confirm-no-undo');
287
+ }
288
+ const run = params.runProcess ?? runNativePgProcess;
289
+ const receiptPath = `${params.backupPath}.json`;
290
+ if (!existsSync(receiptPath))
291
+ throw new Error('community_native_backup_receipt_missing');
292
+ let record;
293
+ try {
294
+ record = JSON.parse(readFileSync(receiptPath, 'utf8'));
295
+ }
296
+ catch {
297
+ throw new Error('community_native_backup_receipt_unreadable');
298
+ }
299
+ if (record?.kind !== 'native-pg-custom' || record.schemaVersion !== 1 || record.verified !== true) {
300
+ throw new Error('community_native_backup_receipt_invalid');
301
+ }
302
+ if (path.resolve(String(record.path)) !== path.resolve(params.backupPath)) {
303
+ throw new Error('community_native_backup_receipt_path_mismatch');
304
+ }
305
+ const noFollow = typeof constants.O_NOFOLLOW === 'number' ? constants.O_NOFOLLOW : 0;
306
+ const archiveFd = openSync(params.backupPath, constants.O_RDONLY | noFollow);
307
+ let preRestoreBackup = null;
308
+ try {
309
+ const pgEnv = pgEnvironmentFromUrl(params.repoUrl);
310
+ const probeFd = openBackupForVerify({
311
+ backupPath: params.backupPath,
312
+ writtenFd: archiveFd,
313
+ expectedByteLength: record.byteLength,
314
+ });
315
+ let rewindPlan;
316
+ try {
317
+ rewindPlan = await resolveSchemaRewindPlan({
318
+ repoUrl: params.repoUrl,
319
+ archivePath: params.backupPath,
320
+ archiveFd: probeFd,
321
+ pgBinDir: params.pgBinDir,
322
+ psql: params.psql,
323
+ pgRestore: params.pgRestore,
324
+ captureOutput: params.captureSchemaProbe,
325
+ });
326
+ }
327
+ finally {
328
+ try {
329
+ closeSync(probeFd);
330
+ }
331
+ catch { }
332
+ }
333
+ if (params.skipPreRestoreBackup !== true) {
334
+ const taken = await createCommunityNativeBackupV1({
335
+ backupRoot: params.backupRoot,
336
+ repoUrl: params.repoUrl,
337
+ instanceName: params.instanceName,
338
+ releaseVersion: params.releaseVersion ?? null,
339
+ pgBinDir: params.pgBinDir,
340
+ pgDump: params.pgDump,
341
+ pgRestore: params.pgRestore,
342
+ now: params.now,
343
+ runProcess: params.runProcess,
344
+ });
345
+ preRestoreBackup = {
346
+ path: taken.record.path,
347
+ sha256: taken.record.sha256,
348
+ byteLength: taken.record.byteLength,
349
+ };
350
+ }
351
+ verifyHeldBackupAgainstReceipt({ heldFd: archiveFd, receipt: record });
352
+ const rewind = await run({
353
+ tool: resolvePgTool('psql', { explicit: params.psql, binDir: params.pgBinDir }),
354
+ args: [
355
+ '--dbname', String(pgEnv.PGDATABASE),
356
+ '--set', 'ON_ERROR_STOP=1',
357
+ '--quiet',
358
+ '--command',
359
+ `${rewindPlan.drop.map((name) => `DROP SCHEMA IF EXISTS ${quoteSchemaIdentifier(name)} CASCADE;`).join(' ')} CREATE SCHEMA public;`,
360
+ ],
361
+ env: pgEnv,
362
+ });
363
+ if (rewind.exitCode !== 0) {
364
+ throw new Error(`community_native_restore_rewind_failed:exit_${String(rewind.exitCode ?? 'null')}`);
365
+ }
366
+ const restore = await run({
367
+ tool: resolvePgTool('pg_restore', { explicit: params.pgRestore, binDir: params.pgBinDir }),
368
+ args: [
369
+ '--dbname', String(pgEnv.PGDATABASE),
370
+ '--no-owner', '--no-acl', '--exit-on-error',
371
+ ],
372
+ inputFd: archiveFd,
373
+ env: pgEnv,
374
+ });
375
+ if (restore.exitCode !== 0) {
376
+ throw new Error(`community_native_restore_failed:exit_${String(restore.exitCode ?? 'null')}`);
377
+ }
378
+ }
379
+ finally {
380
+ try {
381
+ closeSync(archiveFd);
382
+ }
383
+ catch { }
384
+ }
385
+ return { restored: record, preRestoreBackup };
386
+ }
@@ -10,7 +10,8 @@ import { resolveCommunityInstallPaths } from './community-lifecycle.js';
10
10
  import { buildCommunityInstanceContract, } from './community-instance-contract.js';
11
11
  import { COMMUNITY_NATIVE_POSTGRES_CONTRACT_PATH, assertCommunityNativePostgresInstanceState, assertCommunityNativePostgresState, buildCommunityNativePostgresUrl, setupCommunityNativePostgres, startCommunityNativePostgres, } from './community-native-postgres.js';
12
12
  import { COMMUNITY_NATIVE_MIGRATION_POLICY_PATH, assertCommunityNativeMigrationReceiptV1, planCommunityNativeMigration, runCommunityNativeMigration, } from './community-native-migration.js';
13
- import { createCommunityExternalSnapshotAttestationV1, writeCommunityExternalSnapshotAttestationV1, } from './community-migration-snapshot.js';
13
+ import { createCommunityExternalSnapshotAttestationV1, createCommunityNativeManagedSnapshotV1, writeCommunityExternalSnapshotAttestationV1, } from './community-migration-snapshot.js';
14
+ import { createCommunityNativeBackupV1 } from './community-native-backup.js';
14
15
  import { createCommunityNativeApplicationReferenceV1, readCommunityNativeApplicationUpdate, sameCommunityNativeApplicationContent, writeCommunityNativeApplicationRollbackOutcome, writeCommunityNativeApplicationRollbackPrepared, writeCommunityNativeApplicationUpdateOutcome, writeCommunityNativeApplicationUpdatePrepared, } from './community-native-application-recovery.js';
15
16
  const PUBLIC_ROOT_NAME = 'aops-community-distribution';
16
17
  const PUBLIC_SERVER_PACKAGE_NAME = '@aopslabs/aops-server';
@@ -1195,10 +1196,17 @@ export async function attestCommunityNativeExternalSnapshot(params, dependencies
1195
1196
  evidence,
1196
1197
  };
1197
1198
  }
1198
- function childEnvironment(params) {
1199
+ const FORWARDED_TUNING_ENV_KEYS = new Set([
1200
+ 'CHATV3_RATE_OPEN_PER_MIN',
1201
+ 'CHATV3_RATE_MEMBER_PER_MIN',
1202
+ 'CHATV3_RATE_LOCAL_PER_MIN',
1203
+ ]);
1204
+ export function childEnvironment(params) {
1199
1205
  const env = { ...(params.env ?? process.env) };
1200
1206
  for (const key of Object.keys(env)) {
1201
1207
  const upper = key.toUpperCase();
1208
+ if (FORWARDED_TUNING_ENV_KEYS.has(upper))
1209
+ continue;
1202
1210
  if (/^PG[A-Z0-9_]+$/i.test(key) ||
1203
1211
  /(?:^|_)(?:AUTH|COOKIE|DATABASE|ENCRYPT|JWT|PASSWORD|PG|POSTGRES|REPO|SECRET|SESSION|SQLITE|TOKEN)(?:_|$)/i.test(key) ||
1204
1212
  /^(?:AOPS|PROJECTMAN|DOCMAN|AGENTSPACE|CHATV3|AUTHV2|KANBAN|SPRINT|TASKER|FILEMAN|SYS)_/i.test(key) ||
@@ -1529,6 +1537,16 @@ function removeControlIfExact(paths, launchId) {
1529
1537
  throw new Error('community_native_control_launch_mismatch');
1530
1538
  rmSync(paths.controlPath, { force: true });
1531
1539
  }
1540
+ function reconcileDisappearedRecord(record, reason, now) {
1541
+ if (record.status !== 'starting' && record.status !== 'running')
1542
+ return record;
1543
+ return {
1544
+ ...record,
1545
+ status: 'failed',
1546
+ updatedAt: now().toISOString(),
1547
+ failure: record.failure ?? reason,
1548
+ };
1549
+ }
1532
1550
  async function observeInstalledNative(params) {
1533
1551
  const record = params.processRecord;
1534
1552
  const identityPresent = existsSync(params.paths.identityPath);
@@ -1607,7 +1625,8 @@ async function observeInstalledNative(params) {
1607
1625
  if (!identity) {
1608
1626
  if (!supervisorAlive && !hostAlive) {
1609
1627
  return {
1610
- runtimeState: 'crashed', recoverable: true, process: record, identity: null,
1628
+ runtimeState: 'crashed', recoverable: true, identity: null,
1629
+ process: reconcileDisappearedRecord(record, 'community_native_process_disappeared_without_identity', params.now),
1611
1630
  supervisorAlive: false, hostAlive: false, health: 'not-checked',
1612
1631
  reason: 'community_native_process_disappeared_without_identity',
1613
1632
  };
@@ -1620,7 +1639,8 @@ async function observeInstalledNative(params) {
1620
1639
  }
1621
1640
  if (!supervisorAlive && !hostAlive) {
1622
1641
  return {
1623
- runtimeState: 'crashed', recoverable: true, process: record, identity,
1642
+ runtimeState: 'crashed', recoverable: true, identity,
1643
+ process: reconcileDisappearedRecord(record, 'community_native_process_disappeared', params.now),
1624
1644
  supervisorAlive: false, hostAlive: false, health: 'not-checked',
1625
1645
  reason: 'community_native_process_disappeared',
1626
1646
  };
@@ -1689,6 +1709,10 @@ export async function stopCommunityNativeInstall(params = {}) {
1689
1709
  now,
1690
1710
  });
1691
1711
  if (observed.runtimeState === 'stopped' || observed.runtimeState === 'crashed') {
1712
+ if (observed.runtimeState === 'crashed' && observed.process &&
1713
+ inspection.process && inspection.process.status !== observed.process.status) {
1714
+ writeProcess(inspection.paths, observed.process);
1715
+ }
1692
1716
  return { status: 'already-stopped', state: inspection.state, process: observed.process };
1693
1717
  }
1694
1718
  if (!observed.process || !observed.identity || !observed.supervisorAlive ||
@@ -1842,6 +1866,60 @@ export function resolveCommunityNativeLaunchMode(input) {
1842
1866
  throw new Error('community_native_launch_mode_conflict');
1843
1867
  return input.foreground === true ? 'foreground' : 'detached';
1844
1868
  }
1869
+ export async function resolveCommunityNativeSnapshotEvidence(params) {
1870
+ if (params.context.planning.migrationPlan.action !== 'migrate')
1871
+ return null;
1872
+ const exists = params.fileExists ?? existsSync;
1873
+ if (params.postgres.mode === 'container') {
1874
+ const create = params.createManagedSnapshot ?? createCommunityNativeManagedSnapshotV1;
1875
+ const snapshot = await create({
1876
+ planning: params.context.planning,
1877
+ policy: params.context.policy,
1878
+ sourceRoot: params.context.sourceRoot,
1879
+ state: params.postgres,
1880
+ instanceName: params.instanceName,
1881
+ instanceRoot: params.instanceRoot,
1882
+ backupRoot: params.backupRoot,
1883
+ evidenceRoot: params.evidenceRoot,
1884
+ runtime: params.postgresRuntime,
1885
+ signal: params.signal,
1886
+ });
1887
+ return { path: snapshot.evidencePath, policy: 'managed-verified-only-v1' };
1888
+ }
1889
+ const attestationPath = path.join(params.evidenceRoot, `external-${params.context.planning.acceptedPlanSha256}.json`);
1890
+ if (exists(attestationPath)) {
1891
+ return { path: attestationPath, policy: 'managed-or-external-attested-v1' };
1892
+ }
1893
+ const postgres = params.postgres;
1894
+ const loadUrl = params.loadRepoUrl
1895
+ ?? ((state) => {
1896
+ const configRef = state.configRef;
1897
+ if (typeof configRef !== 'string' || configRef.trim() === '') {
1898
+ throw new Error('community_native_snapshot_external_config_ref_missing');
1899
+ }
1900
+ return loadExternalPostgresUrl(configRef, state.tlsPolicy);
1901
+ });
1902
+ const createBackup = params.createExternalBackup ?? createCommunityNativeBackupV1;
1903
+ const writeAttestation = params.writeExternalAttestation ?? writeCommunityExternalSnapshotAttestationV1;
1904
+ const firstPending = params.context.planning.migrationPlan.pendingMigrations?.[0]?.tag;
1905
+ const { record } = await createBackup({
1906
+ backupRoot: params.backupRoot,
1907
+ repoUrl: loadUrl(postgres),
1908
+ instanceName: params.instanceName,
1909
+ label: firstPending ? `pre-${firstPending}` : 'pre-migration',
1910
+ });
1911
+ const written = writeAttestation({
1912
+ planning: params.context.planning,
1913
+ policy: params.context.policy,
1914
+ evidenceRoot: params.evidenceRoot,
1915
+ provider: 'aops-cli-native-pg-dump',
1916
+ snapshotRef: record.path,
1917
+ snapshotDigest: record.sha256,
1918
+ attestedBy: 'aops-cli',
1919
+ restoreInstructionsRef: `aops db restore --backup ${record.path} --confirm-data-rewind`,
1920
+ });
1921
+ return { path: written.evidencePath, policy: 'managed-or-external-attested-v1' };
1922
+ }
1845
1923
  async function launchInstalledNative(params) {
1846
1924
  throwIfNativeAborted(params.signal);
1847
1925
  assertCommunityNativePathLayout(params.paths, { requireInstanceRoot: true });
@@ -1911,6 +1989,17 @@ async function launchInstalledNative(params) {
1911
1989
  sourceFingerprint: params.state.source.sourceFingerprint,
1912
1990
  requiredPlanSha256: params.requiredMigrationPlanSha256,
1913
1991
  requiredAction: params.requiredMigrationAction,
1992
+ snapshotEvidenceProvider: (context) => resolveCommunityNativeSnapshotEvidence({
1993
+ context,
1994
+ postgres,
1995
+ instanceName: params.state.instanceName,
1996
+ instanceRoot: params.paths.instanceRoot,
1997
+ backupRoot: params.paths.backupRoot,
1998
+ evidenceRoot: params.paths.migrationEvidenceRoot,
1999
+ postgresRuntime: params.postgresRuntime,
2000
+ signal: params.signal,
2001
+ createManagedSnapshot: params.createManagedSnapshot,
2002
+ }),
1914
2003
  onPlanAccepted: (context) => {
1915
2004
  const pendingMigrationCount = context.migrationPlan.pendingMigrations.length;
1916
2005
  params.progress?.({
@@ -212,6 +212,26 @@ function receiptFromResult(invocation, result, completedAt, migrationLogCount) {
212
212
  migrationLogCount,
213
213
  });
214
214
  }
215
+ export async function reportCommunityNativePendingMigrations(params, dependencies = {}) {
216
+ try {
217
+ const policy = readCommunityNativeMigrationPolicy(params.sourceRoot);
218
+ const planning = await (dependencies.planStrictPgSchema ?? planCommunityStrictPgSchema)({
219
+ repoUrl: params.repoUrl,
220
+ workspaceRoot: params.sourceRoot,
221
+ policy,
222
+ });
223
+ const pending = planning.migrationPlan.pendingMigrations ?? [];
224
+ return {
225
+ status: 'planned',
226
+ action: planning.migrationPlan.action,
227
+ pendingMigrationCount: pending.length,
228
+ pendingMigrationTags: pending.map((entry) => String(entry.tag ?? '')).filter(Boolean),
229
+ };
230
+ }
231
+ catch (error) {
232
+ return { status: 'unavailable', reason: error instanceof Error ? error.message : String(error) };
233
+ }
234
+ }
215
235
  export async function runCommunityNativeMigration(invocation, dependencies = {}) {
216
236
  const prior = invocation.priorIntentExpectation;
217
237
  if (!path.isAbsolute(invocation.sourceRoot) || !invocation.repoUrl ||