@aopslabs/aops 0.3.6 → 0.3.8
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.
- package/LICENSE +54 -14
- package/NOTICE +3 -3
- package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/discuss/SKILL.md +4 -4
- package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/user-guides/aops-cli.md +1 -1
- package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/manifest.json +7 -7
- package/agent-assets-release/agent-assets/inventory.json +5 -5
- package/agent-assets-release/agent-assets/projection.json +1 -1
- package/agent-assets-release/agent-assets-release.json +39 -39
- package/agent-assets-release/agent-assets-release.sigstore.json +23 -25
- package/assets/agent-assets/core/references/discuss/SKILL.md +4 -4
- package/assets/agent-assets/core/user-guides/aops-cli.md +1 -1
- package/dist/commands/discuss.js +95 -41
- package/dist/commands/doc.js +223 -45
- package/dist/lib/tui-launcher.js +19 -2
- package/native/tui/darwin-arm64/aops-tui +0 -0
- package/native/tui/win32-x64/aops-tui.exe +0 -0
- package/package.json +11 -10
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/agentspace/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/aops-cli-core/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/chatv3/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/collaborative-work/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/docman/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/hosted-chat/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/mission/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/projectman/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/sys/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/view/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/references/working-disciplines/SKILL.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/user-guides/agent-assets.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/user-guides/agentspace.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/user-guides/aops-system.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/user-guides/chatv3.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/user-guides/docman.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/user-guides/projectman.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/user-guides/sys.md +0 -0
- /package/agent-assets-release/agent-assets/core/{8b06775d96e37f8aef535673b8b855c11f29042f3c70ec4f273b6df04dbe824a → 0ffc854a8e32500f3d82c10a1e1ee6376d57e616201a323638f0f9caf8fb2706}/files/user-guides/working-disciplines.md +0 -0
package/dist/commands/doc.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { mkdir, readFile, readdir, rename, stat, unlink, writeFile } from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { Command } from 'commander';
|
|
6
6
|
import { logError, logInfo, logSuccess } from '@aopslabs/xf-cli-ui';
|
|
@@ -887,14 +887,39 @@ function recordVersion(record) {
|
|
|
887
887
|
const parsed = Number(record.version);
|
|
888
888
|
return Number.isFinite(parsed) ? Math.trunc(parsed) : 0;
|
|
889
889
|
}
|
|
890
|
-
function
|
|
891
|
-
const
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
890
|
+
export function selectCurrentPublishedDocumentVersion(versions, documentId) {
|
|
891
|
+
const normalizedDocumentId = normalizeNonEmpty(documentId);
|
|
892
|
+
if (!normalizedDocumentId)
|
|
893
|
+
throw new Error('Mirror version selection requires a document id.');
|
|
894
|
+
if (versions.length === 0) {
|
|
895
|
+
throw new Error(`Docman document ${normalizedDocumentId} has no version rows; mirror pull stopped before writing files.`);
|
|
896
|
+
}
|
|
897
|
+
const seenVersionIds = new Set();
|
|
898
|
+
for (const version of versions) {
|
|
899
|
+
const rowDocumentId = normalizeNonEmpty(version.documentId);
|
|
900
|
+
if (rowDocumentId !== normalizedDocumentId) {
|
|
901
|
+
throw new Error(`Docman version list returned a foreign row for document ${normalizedDocumentId}: ` +
|
|
902
|
+
`row ${recordId(version, 'documentVersionId') ?? '<missing-id>'} belongs to ${rowDocumentId ?? '<missing-document-id>'}.`);
|
|
903
|
+
}
|
|
904
|
+
const versionId = recordId(version, 'documentVersionId');
|
|
905
|
+
if (!versionId) {
|
|
906
|
+
throw new Error(`Docman document ${normalizedDocumentId} returned a version row without an id.`);
|
|
907
|
+
}
|
|
908
|
+
if (seenVersionIds.has(versionId)) {
|
|
909
|
+
throw new Error(`Docman document ${normalizedDocumentId} returned duplicate version id ${versionId}.`);
|
|
910
|
+
}
|
|
911
|
+
seenVersionIds.add(versionId);
|
|
912
|
+
}
|
|
913
|
+
const current = versions.filter((version) => version.isCurrent === true);
|
|
914
|
+
if (current.length !== 1) {
|
|
915
|
+
throw new Error(`Docman document ${normalizedDocumentId} must have exactly one current version; received ${current.length}. ` +
|
|
916
|
+
'Mirror pull does not fall back to the highest version.');
|
|
917
|
+
}
|
|
918
|
+
const selected = current[0];
|
|
919
|
+
if (normalizeNonEmpty(selected.status)?.toLowerCase() !== 'published') {
|
|
920
|
+
throw new Error(`Docman document ${normalizedDocumentId} current version ${recordId(selected, 'documentVersionId') ?? '<missing-id>'} is not published.`);
|
|
921
|
+
}
|
|
922
|
+
return selected;
|
|
898
923
|
}
|
|
899
924
|
function findVersionBySourceHash(versions, sourceHash) {
|
|
900
925
|
const shortHash = sourceHash.slice(0, 12);
|
|
@@ -1521,7 +1546,7 @@ function buildGroupPath(document, groupsById, groupsByUid) {
|
|
|
1521
1546
|
}
|
|
1522
1547
|
return path.join(...(segments.length > 0 ? segments : ['ungrouped']));
|
|
1523
1548
|
}
|
|
1524
|
-
|
|
1549
|
+
function buildDocMirrorFile(params) {
|
|
1525
1550
|
const documentSlug = normalizeNonEmpty(params.document.slug) ??
|
|
1526
1551
|
pathSegmentSlug(normalizeNonEmpty(params.document.title), normalizeNonEmpty(params.document.id) ?? 'document');
|
|
1527
1552
|
const filePath = path.join(params.outputRoot, params.groupPath, `${pathSegmentSlug(documentSlug, 'document')}.md`);
|
|
@@ -1544,11 +1569,12 @@ async function writeDocMirrorFile(params) {
|
|
|
1544
1569
|
target: params.target,
|
|
1545
1570
|
pulledAt: new Date().toISOString(),
|
|
1546
1571
|
});
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1572
|
+
return {
|
|
1573
|
+
filePath,
|
|
1574
|
+
content: `${frontmatter}<!-- READ-ONLY MIRROR: update Docman/source docs, then run aops-cli doc mirror pull. -->\n\n${params.content}`,
|
|
1575
|
+
};
|
|
1550
1576
|
}
|
|
1551
|
-
|
|
1577
|
+
function buildDocMirrorIndex(params) {
|
|
1552
1578
|
const indexPath = path.join(params.outputRoot, 'index.md');
|
|
1553
1579
|
const frontmatter = renderFrontmatter({
|
|
1554
1580
|
schemaVersion: 1,
|
|
@@ -1575,9 +1601,79 @@ async function writeDocMirrorIndex(params) {
|
|
|
1575
1601
|
}),
|
|
1576
1602
|
'',
|
|
1577
1603
|
];
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1604
|
+
return { filePath: indexPath, content: lines.join('\n') };
|
|
1605
|
+
}
|
|
1606
|
+
export async function stageMirrorFile(targetPath, content, sequence) {
|
|
1607
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
1608
|
+
const temporaryPath = `${targetPath}.aops-stage-${process.pid}-${sequence}-${randomUUID()}`;
|
|
1609
|
+
await writeFile(temporaryPath, content, 'utf8');
|
|
1610
|
+
return { targetPath, temporaryPath };
|
|
1611
|
+
}
|
|
1612
|
+
function isFileSystemError(error, code) {
|
|
1613
|
+
return Boolean(error && typeof error === 'object' && 'code' in error && error.code === code);
|
|
1614
|
+
}
|
|
1615
|
+
async function replaceStagedMirrorFile(file) {
|
|
1616
|
+
const backupPath = `${file.temporaryPath}.previous`;
|
|
1617
|
+
let movedExistingTarget = false;
|
|
1618
|
+
try {
|
|
1619
|
+
try {
|
|
1620
|
+
await rename(file.targetPath, backupPath);
|
|
1621
|
+
movedExistingTarget = true;
|
|
1622
|
+
}
|
|
1623
|
+
catch (error) {
|
|
1624
|
+
if (!isFileSystemError(error, 'ENOENT'))
|
|
1625
|
+
throw error;
|
|
1626
|
+
}
|
|
1627
|
+
try {
|
|
1628
|
+
await rename(file.temporaryPath, file.targetPath);
|
|
1629
|
+
}
|
|
1630
|
+
catch (error) {
|
|
1631
|
+
if (movedExistingTarget) {
|
|
1632
|
+
try {
|
|
1633
|
+
await rename(backupPath, file.targetPath);
|
|
1634
|
+
}
|
|
1635
|
+
catch (restoreError) {
|
|
1636
|
+
throw new AggregateError([error, restoreError], `Docman mirror replacement failed and could not restore ${file.targetPath}.`);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
throw error;
|
|
1640
|
+
}
|
|
1641
|
+
return { ...file, backupPath, targetExisted: movedExistingTarget };
|
|
1642
|
+
}
|
|
1643
|
+
catch (error) {
|
|
1644
|
+
await unlink(file.temporaryPath).catch(() => undefined);
|
|
1645
|
+
throw error;
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
export async function commitStagedMirrorFiles(files) {
|
|
1649
|
+
const committed = [];
|
|
1650
|
+
try {
|
|
1651
|
+
for (const file of files) {
|
|
1652
|
+
committed.push(await replaceStagedMirrorFile(file));
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
catch (error) {
|
|
1656
|
+
const rollbackErrors = [];
|
|
1657
|
+
for (const file of [...committed].reverse()) {
|
|
1658
|
+
try {
|
|
1659
|
+
await unlink(file.targetPath).catch((unlinkError) => {
|
|
1660
|
+
if (!isFileSystemError(unlinkError, 'ENOENT'))
|
|
1661
|
+
throw unlinkError;
|
|
1662
|
+
});
|
|
1663
|
+
if (file.targetExisted)
|
|
1664
|
+
await rename(file.backupPath, file.targetPath);
|
|
1665
|
+
}
|
|
1666
|
+
catch (rollbackError) {
|
|
1667
|
+
rollbackErrors.push(rollbackError);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
await Promise.all(files.map((file) => unlink(file.temporaryPath).catch(() => undefined)));
|
|
1671
|
+
if (rollbackErrors.length > 0) {
|
|
1672
|
+
throw new AggregateError([error, ...rollbackErrors], 'Docman mirror commit failed and rollback was incomplete.');
|
|
1673
|
+
}
|
|
1674
|
+
throw error;
|
|
1675
|
+
}
|
|
1676
|
+
await Promise.all(committed.filter((file) => file.targetExisted).map((file) => unlink(file.backupPath).catch(() => undefined)));
|
|
1581
1677
|
}
|
|
1582
1678
|
async function listDocMirrorIndexEntries(outputRoot) {
|
|
1583
1679
|
const files = await listLocalMirrorMarkdownFiles(outputRoot);
|
|
@@ -2120,6 +2216,11 @@ function normalizeDocImportSlugStrategy(options) {
|
|
|
2120
2216
|
return explicit;
|
|
2121
2217
|
throw new Error('Invalid --slug-strategy. Expected one of: hash-suffix-on-collision, kebab-from-title.');
|
|
2122
2218
|
}
|
|
2219
|
+
export function resolveDocImportToolId(dryRun) {
|
|
2220
|
+
return dryRun
|
|
2221
|
+
? 'docman.document-version.import-headings.plan'
|
|
2222
|
+
: 'docman.document-version.import-headings';
|
|
2223
|
+
}
|
|
2123
2224
|
export async function runDocImportMarkdown(options = {}) {
|
|
2124
2225
|
try {
|
|
2125
2226
|
if (options.fromMarkdown !== true) {
|
|
@@ -2143,6 +2244,10 @@ export async function runDocImportMarkdown(options = {}) {
|
|
|
2143
2244
|
})
|
|
2144
2245
|
: undefined;
|
|
2145
2246
|
const dryRun = options.dryRun === true || options.preview === true || options.apply !== true;
|
|
2247
|
+
const planToken = normalizeNonEmpty(options.planToken);
|
|
2248
|
+
if (dryRun && planToken) {
|
|
2249
|
+
throw new Error('--plan-token is only valid with --apply.');
|
|
2250
|
+
}
|
|
2146
2251
|
const auditFields = await resolveDocAuditFields(apiState, options, {}, 'create');
|
|
2147
2252
|
const importOptions = compactPayload({
|
|
2148
2253
|
dryRun,
|
|
@@ -2167,13 +2272,14 @@ export async function runDocImportMarkdown(options = {}) {
|
|
|
2167
2272
|
nodes,
|
|
2168
2273
|
},
|
|
2169
2274
|
options: importOptions,
|
|
2275
|
+
...(!dryRun && planToken ? { planToken } : {}),
|
|
2170
2276
|
...auditFields,
|
|
2171
2277
|
});
|
|
2172
2278
|
if (baselineGuard?.status === 'blocked' && !dryRun && options.confirm !== true) {
|
|
2173
2279
|
throw new Error(`Doc import baseline guard blocked ${baselineGuard.blockingDeltaCount} unrelated markdown deltas. ` +
|
|
2174
2280
|
'Run the command with --dry-run --json to inspect result.baselineGuard, add --guard-target for the intended heading, or use --confirm only after reviewing the full-import delta.');
|
|
2175
2281
|
}
|
|
2176
|
-
const toolId =
|
|
2282
|
+
const toolId = resolveDocImportToolId(dryRun);
|
|
2177
2283
|
const invokeInput = compactPayload({
|
|
2178
2284
|
pathParams: { id: documentVersionId },
|
|
2179
2285
|
body: input,
|
|
@@ -2407,6 +2513,36 @@ export async function runDocMirrorPull(options = {}) {
|
|
|
2407
2513
|
}), compactPayload({
|
|
2408
2514
|
limit: toInteger(options.limit, '--limit'),
|
|
2409
2515
|
}));
|
|
2516
|
+
const expectedGroup = groupUid ? groupsByUid.get(groupUid) : undefined;
|
|
2517
|
+
const expectedGroupId = normalizeNonEmpty(expectedGroup?.id);
|
|
2518
|
+
const expectedStatus = normalizeNonEmpty(options.status)?.toLowerCase();
|
|
2519
|
+
const expectedScopeId = normalizeNonEmpty(resolvedContext.scopeId);
|
|
2520
|
+
const seenDocumentIds = new Set();
|
|
2521
|
+
for (const document of allDocuments) {
|
|
2522
|
+
const documentId = recordId(document, 'documentId');
|
|
2523
|
+
if (!documentId) {
|
|
2524
|
+
throw new Error('Docman document list returned a row without a document id; mirror pull stopped before writing files.');
|
|
2525
|
+
}
|
|
2526
|
+
if (seenDocumentIds.has(documentId)) {
|
|
2527
|
+
throw new Error(`Docman document list returned duplicate document id ${documentId}.`);
|
|
2528
|
+
}
|
|
2529
|
+
seenDocumentIds.add(documentId);
|
|
2530
|
+
const rowScopeId = normalizeNonEmpty(document.scopeId);
|
|
2531
|
+
if (expectedScopeId && rowScopeId && rowScopeId !== expectedScopeId) {
|
|
2532
|
+
throw new Error(`Docman document list returned foreign-scope row ${documentId} (${rowScopeId}, expected ${expectedScopeId}).`);
|
|
2533
|
+
}
|
|
2534
|
+
if (groupUid) {
|
|
2535
|
+
const rowGroupUid = normalizeNonEmpty(document.groupUid);
|
|
2536
|
+
const rowGroupId = normalizeNonEmpty(document.groupId);
|
|
2537
|
+
if (rowGroupUid !== groupUid && (!expectedGroupId || rowGroupId !== expectedGroupId)) {
|
|
2538
|
+
throw new Error(`Docman document list returned foreign-group row ${documentId}; expected group ${groupUid}.`);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
const rowStatus = normalizeNonEmpty(document.status)?.toLowerCase();
|
|
2542
|
+
if (expectedStatus && rowStatus !== expectedStatus) {
|
|
2543
|
+
throw new Error(`Docman document list returned foreign-status row ${documentId}; expected ${expectedStatus}, received ${rowStatus ?? '<missing>'}.`);
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2410
2546
|
const documents = documentSlugFilters.size > 0
|
|
2411
2547
|
? allDocuments.filter((document) => {
|
|
2412
2548
|
const documentSlug = normalizeSlugFilter(document.slug);
|
|
@@ -2419,22 +2555,30 @@ export async function runDocMirrorPull(options = {}) {
|
|
|
2419
2555
|
throw new Error(`No Docman documents matched --document-slug ${[...documentSlugFilters].join(', ')}.`);
|
|
2420
2556
|
}
|
|
2421
2557
|
const plans = [];
|
|
2558
|
+
const seenVersionIds = new Set();
|
|
2559
|
+
const seenMirrorPaths = new Set();
|
|
2422
2560
|
for (const document of documents) {
|
|
2423
2561
|
const documentId = recordId(document, 'documentId');
|
|
2424
|
-
if (!documentId)
|
|
2425
|
-
|
|
2426
|
-
continue;
|
|
2427
|
-
}
|
|
2562
|
+
if (!documentId)
|
|
2563
|
+
throw new Error('Docman document row lost its validated id unexpectedly.');
|
|
2428
2564
|
const versions = await listDocumentVersions(apiState, options, resolvedContext, documentId);
|
|
2429
|
-
const latest =
|
|
2565
|
+
const latest = selectCurrentPublishedDocumentVersion(versions, documentId);
|
|
2430
2566
|
const documentVersionId = recordId(latest, 'documentVersionId');
|
|
2431
|
-
if (!
|
|
2432
|
-
|
|
2433
|
-
|
|
2567
|
+
if (!documentVersionId)
|
|
2568
|
+
throw new Error(`Docman document ${documentId} current published version is missing its id.`);
|
|
2569
|
+
if (seenVersionIds.has(documentVersionId)) {
|
|
2570
|
+
throw new Error(`Docman mirror plan contains duplicate version id ${documentVersionId}.`);
|
|
2434
2571
|
}
|
|
2572
|
+
seenVersionIds.add(documentVersionId);
|
|
2435
2573
|
const groupPath = buildGroupPath(document, groupsById, groupsByUid);
|
|
2436
2574
|
const documentSlug = normalizeNonEmpty(document.slug) ??
|
|
2437
2575
|
pathSegmentSlug(normalizeNonEmpty(document.title), normalizeNonEmpty(documentId) ?? 'document');
|
|
2576
|
+
const mirrorPath = path.join(groupPath, `${pathSegmentSlug(documentSlug, 'document')}.md`);
|
|
2577
|
+
const canonicalMirrorPath = path.resolve(outputRoot, mirrorPath).toLowerCase();
|
|
2578
|
+
if (seenMirrorPaths.has(canonicalMirrorPath)) {
|
|
2579
|
+
throw new Error(`Docman mirror plan contains duplicate output path ${mirrorPath}.`);
|
|
2580
|
+
}
|
|
2581
|
+
seenMirrorPaths.add(canonicalMirrorPath);
|
|
2438
2582
|
plans.push({
|
|
2439
2583
|
documentId,
|
|
2440
2584
|
documentVersionId,
|
|
@@ -2442,7 +2586,7 @@ export async function runDocMirrorPull(options = {}) {
|
|
|
2442
2586
|
title: document.title,
|
|
2443
2587
|
slug: documentSlug,
|
|
2444
2588
|
groupPath,
|
|
2445
|
-
mirrorPath
|
|
2589
|
+
mirrorPath,
|
|
2446
2590
|
});
|
|
2447
2591
|
}
|
|
2448
2592
|
const input = compactPayload({
|
|
@@ -2462,16 +2606,16 @@ export async function runDocMirrorPull(options = {}) {
|
|
|
2462
2606
|
input,
|
|
2463
2607
|
}))
|
|
2464
2608
|
return;
|
|
2465
|
-
const
|
|
2609
|
+
const materializedPlans = [];
|
|
2466
2610
|
for (const plan of plans) {
|
|
2467
2611
|
const documentId = normalizeNonEmpty(plan.documentId);
|
|
2468
2612
|
const documentVersionId = normalizeNonEmpty(plan.documentVersionId);
|
|
2469
|
-
if (!documentId || !documentVersionId)
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
const
|
|
2613
|
+
if (!documentId || !documentVersionId)
|
|
2614
|
+
throw new Error('Validated mirror plan lost a document or version id.');
|
|
2615
|
+
const document = documents.find((entry) => recordId(entry, 'documentId') === documentId);
|
|
2616
|
+
if (!document)
|
|
2617
|
+
throw new Error(`Validated mirror plan references unknown document ${documentId}.`);
|
|
2618
|
+
const documentVersion = { id: documentVersionId, documentId, version: plan.documentVersion, status: 'published', isCurrent: true };
|
|
2475
2619
|
const materialized = await callDocmanVersionRoute(apiState, options, resolvedContext, {
|
|
2476
2620
|
documentVersionId,
|
|
2477
2621
|
suffix: 'materialize',
|
|
@@ -2483,30 +2627,62 @@ export async function runDocMirrorPull(options = {}) {
|
|
|
2483
2627
|
fallbackLocale: normalizeNonEmpty(options.fallbackLocale),
|
|
2484
2628
|
}),
|
|
2485
2629
|
});
|
|
2630
|
+
const materializedPayload = unwrapResultData(materialized);
|
|
2631
|
+
const returnedVersionId = normalizeNonEmpty(materializedPayload?.documentVersionId);
|
|
2632
|
+
if (returnedVersionId !== documentVersionId) {
|
|
2633
|
+
throw new Error(`Docman materialize returned version ${returnedVersionId ?? '<missing>'}; expected ${documentVersionId}. ` +
|
|
2634
|
+
'Mirror pull stopped before replacing any destination file.');
|
|
2635
|
+
}
|
|
2486
2636
|
const content = materializedContent(materialized);
|
|
2487
|
-
|
|
2637
|
+
materializedPlans.push({ plan, document, documentVersion, content });
|
|
2638
|
+
}
|
|
2639
|
+
const existingIndexEntries = await listDocMirrorIndexEntries(outputRoot);
|
|
2640
|
+
const written = [];
|
|
2641
|
+
const artifacts = materializedPlans.map(({ plan, document, documentVersion, content }) => {
|
|
2642
|
+
const artifact = buildDocMirrorFile({
|
|
2488
2643
|
outputRoot,
|
|
2489
2644
|
groupPath: normalizeNonEmpty(plan.groupPath) ?? 'ungrouped',
|
|
2490
2645
|
document,
|
|
2491
|
-
documentVersion
|
|
2646
|
+
documentVersion,
|
|
2492
2647
|
resolvedContext,
|
|
2493
2648
|
target,
|
|
2494
2649
|
content,
|
|
2495
2650
|
});
|
|
2496
2651
|
written.push({
|
|
2497
2652
|
...plan,
|
|
2498
|
-
outputPath,
|
|
2653
|
+
outputPath: artifact.filePath,
|
|
2499
2654
|
});
|
|
2655
|
+
return artifact;
|
|
2656
|
+
});
|
|
2657
|
+
const indexEntriesByPath = new Map();
|
|
2658
|
+
for (const entry of existingIndexEntries) {
|
|
2659
|
+
const mirrorPath = normalizeNonEmpty(entry.mirrorPath);
|
|
2660
|
+
if (mirrorPath)
|
|
2661
|
+
indexEntriesByPath.set(mirrorPath.toLowerCase(), entry);
|
|
2500
2662
|
}
|
|
2501
|
-
const
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2663
|
+
for (const entry of written) {
|
|
2664
|
+
const mirrorPath = normalizeNonEmpty(entry.mirrorPath);
|
|
2665
|
+
if (mirrorPath)
|
|
2666
|
+
indexEntriesByPath.set(mirrorPath.toLowerCase(), entry);
|
|
2667
|
+
}
|
|
2668
|
+
const indexArtifact = buildDocMirrorIndex({
|
|
2506
2669
|
outputRoot,
|
|
2507
2670
|
resolvedContext,
|
|
2508
|
-
entries:
|
|
2671
|
+
entries: [...indexEntriesByPath.values()],
|
|
2509
2672
|
});
|
|
2673
|
+
const staged = [];
|
|
2674
|
+
try {
|
|
2675
|
+
for (const [index, artifact] of artifacts.entries()) {
|
|
2676
|
+
staged.push(await stageMirrorFile(artifact.filePath, artifact.content, index));
|
|
2677
|
+
}
|
|
2678
|
+
staged.push(await stageMirrorFile(indexArtifact.filePath, indexArtifact.content, artifacts.length));
|
|
2679
|
+
}
|
|
2680
|
+
catch (error) {
|
|
2681
|
+
await Promise.all(staged.map((file) => unlink(file.temporaryPath).catch(() => undefined)));
|
|
2682
|
+
throw error;
|
|
2683
|
+
}
|
|
2684
|
+
await commitStagedMirrorFiles(staged);
|
|
2685
|
+
const indexPath = indexArtifact.filePath;
|
|
2510
2686
|
await emitDocResult({
|
|
2511
2687
|
options,
|
|
2512
2688
|
command: 'doc.mirror.pull',
|
|
@@ -2517,7 +2693,7 @@ export async function runDocMirrorPull(options = {}) {
|
|
|
2517
2693
|
outputRoot,
|
|
2518
2694
|
indexPath,
|
|
2519
2695
|
writtenCount: written.filter((entry) => normalizeNonEmpty(entry.outputPath)).length,
|
|
2520
|
-
skippedCount:
|
|
2696
|
+
skippedCount: 0,
|
|
2521
2697
|
documents: written,
|
|
2522
2698
|
},
|
|
2523
2699
|
artifacts: {
|
|
@@ -5097,6 +5273,7 @@ export function makeDocCommand() {
|
|
|
5097
5273
|
.option('--baseline <path>', 'Prior materialized markdown path used to guard against unrelated body loss')
|
|
5098
5274
|
.option('--guard-target <heading>', 'Heading path/title allowed to change when --baseline is provided (repeatable)', collectRepeatedOption, [])
|
|
5099
5275
|
.option('--dry-run', 'Execute service-side import planning without mutating rows')
|
|
5276
|
+
.option('--plan-token <token>', 'Apply only if this server-issued import plan is still current')
|
|
5100
5277
|
.option('--existing-graph-policy <policy>', 'Existing graph policy: error, append, replace', 'error')
|
|
5101
5278
|
.option('--append-existing-graph', 'Append into an existing graph explicitly')
|
|
5102
5279
|
.option('--replace-existing-graph', 'Request explicit replace semantics')
|
|
@@ -5117,6 +5294,7 @@ export function makeDocCommand() {
|
|
|
5117
5294
|
'In JSON, warning codes are under result.summary.warnings[].code, or result.import.summary.warnings[].code when baselineGuard wraps the import result.',
|
|
5118
5295
|
'Use --synthesize-overview-pages when direct H2/H3 body text must become child Overview pages, then verify the mirror after publish/pull.',
|
|
5119
5296
|
'Use --baseline and --guard-target for full-document imports; apply is blocked on unrelated deltas unless --confirm is explicit.',
|
|
5297
|
+
'A dry-run returns a short-lived planToken. Pass it to --apply --plan-token <token> to reject source or graph drift; apply without a token remains supported.',
|
|
5120
5298
|
],
|
|
5121
5299
|
}));
|
|
5122
5300
|
const mirror = cmd.command('mirror').description('Docman mirror pull and markdown import helpers');
|
package/dist/lib/tui-launcher.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
2
|
+
import { accessSync, constants, existsSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
const SUPPORTED_TUI_TARGETS = new Set([
|
|
6
6
|
'darwin-arm64',
|
|
7
7
|
'win32-x64',
|
|
8
8
|
]);
|
|
9
|
+
const TUI_REINSTALL_MESSAGE = 'The bundled AOPS TUI is not executable. Reinstall @aopslabs/aops or use aops --help.';
|
|
10
|
+
function isExecutableFile(filePath) {
|
|
11
|
+
try {
|
|
12
|
+
accessSync(filePath, constants.X_OK);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
9
19
|
function packageRootFromModule() {
|
|
10
20
|
return fileURLToPath(new URL('../../', import.meta.url));
|
|
11
21
|
}
|
|
@@ -41,10 +51,17 @@ export async function launchBundledTui(options = {}) {
|
|
|
41
51
|
if (!fileExists(invocation.command)) {
|
|
42
52
|
throw new Error('The bundled AOPS TUI is missing. Reinstall @aopslabs/aops or use aops --help.');
|
|
43
53
|
}
|
|
54
|
+
const platform = options.platform ?? process.platform;
|
|
55
|
+
const fileExecutable = options.fileExecutable ?? isExecutableFile;
|
|
56
|
+
if (platform !== 'win32' && !fileExecutable(invocation.command)) {
|
|
57
|
+
throw new Error(TUI_REINSTALL_MESSAGE);
|
|
58
|
+
}
|
|
44
59
|
const spawnProcess = options.spawnProcess ?? spawn;
|
|
45
60
|
const child = spawnProcess(invocation.command, [...invocation.args], invocation.options);
|
|
46
61
|
return await new Promise((resolve, reject) => {
|
|
47
|
-
child.once('error',
|
|
62
|
+
child.once('error', (error) => {
|
|
63
|
+
reject(error.code === 'EACCES' ? new Error(TUI_REINSTALL_MESSAGE) : error);
|
|
64
|
+
});
|
|
48
65
|
child.once('exit', (code, signal) => {
|
|
49
66
|
if (signal) {
|
|
50
67
|
reject(new Error(`The AOPS TUI stopped after receiving ${signal}.`));
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aopslabs/aops",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AOPS CLI and terminal setup application.",
|
|
6
|
-
"aopsDockerServerVersion": "0.2.
|
|
6
|
+
"aopsDockerServerVersion": "0.2.9",
|
|
7
7
|
"license": "SEE LICENSE IN LICENSE",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"test:commercial-license": "node --test test/commercial-license.test.mjs",
|
|
40
40
|
"test:commercial-runbook": "node --test test/commercial-runbook.test.mjs",
|
|
41
41
|
"test:commercial-performance": "node --test test/commercial-performance.test.mjs",
|
|
42
|
-
"test:package-identity": "node --test test/package-identity.test.mjs",
|
|
42
|
+
"test:package-identity": "node --test test/package-identity.test.mjs test/npm-tar-entry-mode.test.mjs",
|
|
43
43
|
"test:tui-launcher": "node --test test/tui-launcher.test.mjs",
|
|
44
44
|
"test:skill-discovery": "node --test test/skill-discovery.test.mjs",
|
|
45
45
|
"test:target-auth": "node --test test/target-auth.test.mjs test/device-login.test.mjs",
|
|
@@ -48,24 +48,25 @@
|
|
|
48
48
|
"test:r6-auth-cutover": "node --test test/r6-auth-cutover.test.mjs",
|
|
49
49
|
"test:container-runtime": "node --test test/container-runtime.test.mjs test/community-native-postgres.test.mjs",
|
|
50
50
|
"test:kernel-alignment": "node --test test/kernel-alignment.test.mjs",
|
|
51
|
+
"test:hosted-contract-hardening": "node --test test/hosted-contract-hardening.test.mjs",
|
|
51
52
|
"test:home": "node --test test/home-menu.test.mjs",
|
|
52
53
|
"test:launchers": "node --test test/terminal-launchers.test.mjs",
|
|
53
|
-
"test:shortcuts": "pnpm run test:home && pnpm run test:cockpit && pnpm run test:launchers && pnpm run test:assets-menu && pnpm run test:agent-assets-content && pnpm run test:setup-install && pnpm run test:container-runtime && pnpm run test:native-lifecycle && pnpm run test:operation-journal && pnpm run test:commercial-canonical-order && pnpm run test:commercial-runtime-closure && pnpm run test:commercial-artifact && pnpm run test:commercial-final-readiness && pnpm run test:commercial-admission && pnpm run test:commercial-license && pnpm run test:commercial-runbook && pnpm run test:package-identity && pnpm run test:skill-discovery && pnpm run test:target-transport && pnpm run test:target-auth && pnpm run test:r6-auth-cutover && pnpm run test:r6-cutover-baseline",
|
|
54
|
+
"test:shortcuts": "pnpm run test:home && pnpm run test:cockpit && pnpm run test:launchers && pnpm run test:assets-menu && pnpm run test:agent-assets-content && pnpm run test:setup-install && pnpm run test:container-runtime && pnpm run test:native-lifecycle && pnpm run test:operation-journal && pnpm run test:commercial-canonical-order && pnpm run test:commercial-runtime-closure && pnpm run test:commercial-artifact && pnpm run test:commercial-final-readiness && pnpm run test:commercial-admission && pnpm run test:commercial-license && pnpm run test:commercial-runbook && pnpm run test:package-identity && pnpm run test:skill-discovery && pnpm run test:target-transport && pnpm run test:target-auth && pnpm run test:r6-auth-cutover && pnpm run test:r6-cutover-baseline && pnpm run test:hosted-contract-hardening",
|
|
54
55
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
55
56
|
"start": "node dist/main.js"
|
|
56
57
|
},
|
|
57
58
|
"dependencies": {
|
|
58
|
-
"@aopslabs/aops-server": "0.2.
|
|
59
|
-
"@aopslabs/artifact-trust-contracts": "0.3.
|
|
59
|
+
"@aopslabs/aops-server": "0.2.9",
|
|
60
|
+
"@aopslabs/artifact-trust-contracts": "0.3.1",
|
|
60
61
|
"@aopslabs/cli-kit": "0.1.0",
|
|
61
|
-
"@aopslabs/aops-host-registration": "0.2.
|
|
62
|
-
"@aopslabs/aops-pg-bootstrap": "0.2.
|
|
63
|
-
"@aopslabs/aops-runtime-config": "0.2.
|
|
62
|
+
"@aopslabs/aops-host-registration": "0.2.1",
|
|
63
|
+
"@aopslabs/aops-pg-bootstrap": "0.2.2",
|
|
64
|
+
"@aopslabs/aops-runtime-config": "0.2.1",
|
|
64
65
|
"@sigstore/bundle": "4.0.0",
|
|
65
66
|
"@sigstore/protobuf-specs": "0.5.1",
|
|
66
67
|
"@sigstore/tuf": "4.0.2",
|
|
67
68
|
"@sigstore/verify": "3.1.1",
|
|
68
|
-
"@aopslabs/api-client": "0.2.
|
|
69
|
+
"@aopslabs/api-client": "0.2.1",
|
|
69
70
|
"@aopslabs/domain-product-client-chatv3": "0.2.2",
|
|
70
71
|
"@aopslabs/xf-cli-ui": "0.2.0",
|
|
71
72
|
"commander": "14.0.3",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|