total-recall-brain 3.41.2 → 3.41.4
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/bin/total-recall.mjs +19 -4
- package/frontend/dist/assets/{index-BG9NkVUy.css → index-BIQmqDKP.css} +1 -1
- package/frontend/dist/assets/{index-BtuiZqwP.js → index-CRzjQLu7.js} +7 -7
- package/frontend/dist/index.html +2 -2
- package/frontend/dist/sw.js +8 -5
- package/frontend/src/api/auth.ts +0 -3
- package/frontend/src/components/plugins/PluginPanel.tsx +7 -3
- package/frontend/src/components/plugins/card/CardEngine.tsx +1 -1
- package/frontend/src/pages/LoginPage.tsx +1 -41
- package/frontend/src/pages/PluginsPage.tsx +14 -7
- package/package.json +7 -5
- package/scaffold/.agent/skills/plugin/scripts/record.mjs +6 -3
- package/scaffold/.agent/skills/total-recall/plugins/auth/cli.mjs +197 -31
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +6 -6
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +8 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +48 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/login-projections.mjs +42 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/mesh-secrets.mjs +18 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/routes/secrets.mjs +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-carriers.mjs +76 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-prune.mjs +37 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-sync.mjs +53 -17
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/DESIGN.md +56 -8
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +1068 -204
- package/scaffold/.agent/skills/total-recall/plugins/auth/ui/panel.js +4 -2
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/DESIGN.md +32 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +436 -274
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +12 -12
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +16 -2
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +461 -242
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +12 -12
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +1 -7
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/DESIGN.md +17 -2
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +426 -300
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +23 -8
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +6 -2
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/DESIGN.md +32 -0
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +350 -283
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +23 -8
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +5 -1
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +31 -19
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +23 -10
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +5 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/grants.mjs +7 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +61 -38
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/leader-election.mjs +57 -2
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +220 -36
- package/scaffold/.agent/skills/total-recall/plugins/mesh/src/server/routes/mesh.mjs +7 -2
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +65 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/BRICK.md +59 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +534 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +139 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +2849 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +26 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +376 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +37 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adapters/dsh.mjs +446 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +23 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +398 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +152 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +82 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +90 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +415 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +36 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/setup.mjs +35 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/tracker.mjs +133 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +233 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/DESIGN.md +32 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +845 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/capability/BRICK.md +64 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/capability/index.mjs +250 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +344 -14
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +2917 -140
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +9 -3
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +223 -5
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/config.mjs +129 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +355 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/upstream-watcher.mjs +375 -0
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +37 -10
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +986 -280
- package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +7 -1
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +25 -10
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +6 -2
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/security/src/scanner.mjs +17 -3
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +38 -15
- package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +60 -34
- package/src/cli/command.mjs +23 -18
- package/src/cli/config.mjs +3 -0
- package/src/cli/connect.mjs +12 -116
- package/src/cli/daemon.mjs +23 -1
- package/src/cli/harness.mjs +0 -1
- package/src/cli/plugin/copies.mjs +8 -5
- package/src/cli/plugin/create.mjs +56 -3
- package/src/cli/plugin/index.mjs +40 -5
- package/src/cli/plugin/monetization.mjs +232 -12
- package/src/cli/preflight.mjs +161 -0
- package/src/cli/rebuild.mjs +22 -0
- package/src/cli/reset-password.mjs +3 -2
- package/src/cli/skill.mjs +8 -2
- package/src/cli/status.mjs +25 -7
- package/src/core/automatic-brain-updates.mjs +9 -3
- package/src/core/brain-updates.mjs +63 -5
- package/src/core/cli-commands.mjs +2 -0
- package/src/core/context-compiler.mjs +8 -1
- package/src/core/default-plugins.mjs +1 -1
- package/src/core/global-brain-updates.mjs +21 -1
- package/src/core/mesh-brain-updates.mjs +5 -2
- package/src/core/meta-harness.mjs +32 -79
- package/src/core/plugin-dependencies.mjs +6 -1
- package/src/core/plugin-runner.mjs +6 -1
- package/src/core/plugin-store.mjs +67 -1
- package/src/core/plugin-updates.mjs +1 -1
- package/src/core/portable-skills-maintenance.mjs +19 -0
- package/src/core/preflight-gate.mjs +77 -0
- package/src/core/preflight-receipt.mjs +123 -0
- package/src/core/required-plugins.mjs +7 -0
- package/src/core/runtime.mjs +0 -3
- package/src/core/skills-registry.mjs +33 -3
- package/src/core/surface.mjs +38 -1
- package/src/server/auth.mjs +41 -1
- package/src/server/memory-app.mjs +2 -1
- package/src/server/routes/auth.mjs +25 -1
- package/src/server/routes/plugins.mjs +17 -4
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# OpenWiki Capability Brick
|
|
2
|
+
|
|
3
|
+
Portable documentation discovery, markdown knowledge hub parsing, upstream version comparison, and directory scaffolding without host dependencies.
|
|
4
|
+
|
|
5
|
+
## Installation & Vendoring
|
|
6
|
+
|
|
7
|
+
Vendor this brick into any standalone project using Total Recall:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
total-recall plugin vendor openwiki
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## API Usage
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
import {
|
|
17
|
+
discoverWikiHubs,
|
|
18
|
+
parseWikiHub,
|
|
19
|
+
inspectWikiStaleness,
|
|
20
|
+
bootstrapWikiPages,
|
|
21
|
+
compareSemanticVersions,
|
|
22
|
+
createPropagationPlan,
|
|
23
|
+
formatPropagationReceipt
|
|
24
|
+
} from './capability/index.mjs';
|
|
25
|
+
|
|
26
|
+
// 1. Discover all documentation hubs in a project
|
|
27
|
+
const hubs = discoverWikiHubs({ root: process.cwd() });
|
|
28
|
+
console.log(`Discovered ${hubs.length} hubs`);
|
|
29
|
+
|
|
30
|
+
// 2. Parse a hub document
|
|
31
|
+
const hubData = parseWikiHub('# Architecture\n\nSystem overview [Details](details.md)');
|
|
32
|
+
console.log(hubData.title); // "Architecture"
|
|
33
|
+
|
|
34
|
+
// 3. Inspect directory staleness
|
|
35
|
+
const staleness = inspectWikiStaleness({ root: process.cwd() });
|
|
36
|
+
console.log(staleness.isStale);
|
|
37
|
+
|
|
38
|
+
// 4. Compare version strings
|
|
39
|
+
const comparison = compareSemanticVersions('0.7.3', '0.7.2');
|
|
40
|
+
console.log(comparison > 0); // true
|
|
41
|
+
|
|
42
|
+
// 5. Build propagation plan across registered repositories
|
|
43
|
+
const plan = createPropagationPlan({
|
|
44
|
+
projects: [{ name: 'api', root: '/repos/api' }],
|
|
45
|
+
latestVersion: '0.7.3',
|
|
46
|
+
currentVersion: '0.7.2'
|
|
47
|
+
});
|
|
48
|
+
console.log(plan.updateRequired); // true
|
|
49
|
+
|
|
50
|
+
// 6. Bootstrap wiki skeleton
|
|
51
|
+
const result = bootstrapWikiPages({
|
|
52
|
+
targetDirectory: './openwiki',
|
|
53
|
+
templates: {
|
|
54
|
+
'README.md': '# Project Knowledge Base\n'
|
|
55
|
+
},
|
|
56
|
+
dryRun: false
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Invariants
|
|
61
|
+
|
|
62
|
+
- Zero Total Recall runtime dependencies
|
|
63
|
+
- Zero network requests
|
|
64
|
+
- Pure filesystem and text operations
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenWiki Core Capability Brick
|
|
3
|
+
*
|
|
4
|
+
* Standalone knowledge graph hub discovery, markdown wiki parsing,
|
|
5
|
+
* staleness auditing, and structural bootstrap operations.
|
|
6
|
+
* Operates with zero host coupling and zero runtime dependencies.
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Discovers markdown wiki hubs within a specified directory.
|
|
13
|
+
* @param {object} options
|
|
14
|
+
* @param {string} options.root Base directory to inspect
|
|
15
|
+
* @param {string} [options.subfolder='openwiki'] Documentation folder name
|
|
16
|
+
* @returns {Array<{ name: string, title: string, path: string, lines: number, size_bytes: number }>}
|
|
17
|
+
*/
|
|
18
|
+
export function discoverWikiHubs({ root = '.', subfolder = 'openwiki' } = {}) {
|
|
19
|
+
const targetDir = path.resolve(root, subfolder);
|
|
20
|
+
if (!fs.existsSync(targetDir)) return [];
|
|
21
|
+
|
|
22
|
+
const stat = fs.statSync(targetDir, { throwIfNoEntry: false });
|
|
23
|
+
if (!stat || !stat.isDirectory()) return [];
|
|
24
|
+
|
|
25
|
+
const hubs = [];
|
|
26
|
+
const entries = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
27
|
+
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
|
|
30
|
+
const fullPath = path.join(targetDir, entry.name);
|
|
31
|
+
try {
|
|
32
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
33
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
34
|
+
const title = titleMatch ? titleMatch[1].trim() : entry.name.replace(/\.md$/, '');
|
|
35
|
+
const lines = content.split('\n').length;
|
|
36
|
+
hubs.push({
|
|
37
|
+
name: entry.name,
|
|
38
|
+
title,
|
|
39
|
+
path: path.relative(root, fullPath),
|
|
40
|
+
lines,
|
|
41
|
+
size_bytes: Buffer.byteLength(content, 'utf8')
|
|
42
|
+
});
|
|
43
|
+
} catch {
|
|
44
|
+
// Unreadable file skipped cleanly
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return hubs.sort((a, b) => a.name.localeCompare(b.name));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Parses markdown hub content into structured headings, internal links, and statistics.
|
|
53
|
+
* @param {string} content Markdown document content
|
|
54
|
+
* @returns {{ title: string, headings: string[], links: string[], lineCount: number, wordCount: number }}
|
|
55
|
+
*/
|
|
56
|
+
export function parseWikiHub(content = '') {
|
|
57
|
+
if (typeof content !== 'string') {
|
|
58
|
+
return { title: 'Untitled', headings: [], links: [], lineCount: 0, wordCount: 0 };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const lines = content.split('\n');
|
|
62
|
+
const lineCount = lines.length;
|
|
63
|
+
const wordCount = content.trim().length ? content.trim().split(/\s+/).length : 0;
|
|
64
|
+
|
|
65
|
+
const headings = [];
|
|
66
|
+
let title = 'Untitled';
|
|
67
|
+
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
const match = line.match(/^(#{1,6})\s+(.+)$/);
|
|
70
|
+
if (match) {
|
|
71
|
+
const text = match[2].trim();
|
|
72
|
+
headings.push(text);
|
|
73
|
+
if (match[1].length === 1 && title === 'Untitled') {
|
|
74
|
+
title = text;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const links = [];
|
|
80
|
+
const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
81
|
+
let linkMatch;
|
|
82
|
+
while ((linkMatch = linkRegex.exec(content)) !== null) {
|
|
83
|
+
links.push({ text: linkMatch[1], href: linkMatch[2] });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
title,
|
|
88
|
+
headings,
|
|
89
|
+
links,
|
|
90
|
+
lineCount,
|
|
91
|
+
wordCount
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Inspects documentation directory staleness by checking generation marker state.
|
|
97
|
+
* @param {object} options
|
|
98
|
+
* @param {string} options.root Project root containing an openwiki directory
|
|
99
|
+
* @returns {{ hasWiki: boolean, isStale: boolean, generatedAt: string | null, status: string | null, fileCount: number }}
|
|
100
|
+
*/
|
|
101
|
+
export function inspectWikiStaleness({ root = '.' } = {}) {
|
|
102
|
+
const wikiDir = path.resolve(root, 'openwiki');
|
|
103
|
+
if (!fs.existsSync(wikiDir)) {
|
|
104
|
+
return { hasWiki: false, isStale: true, generatedAt: null, status: null, fileCount: 0 };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let fileCount = 0;
|
|
108
|
+
try {
|
|
109
|
+
const entries = fs.readdirSync(wikiDir, { withFileTypes: true });
|
|
110
|
+
fileCount = entries.filter(e => e.isFile() && e.name.endsWith('.md')).length;
|
|
111
|
+
} catch {
|
|
112
|
+
fileCount = 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const markerPath = path.join(wikiDir, '.last-update.json');
|
|
116
|
+
let generatedAt = null;
|
|
117
|
+
let status = null;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const raw = fs.readFileSync(markerPath, 'utf8');
|
|
121
|
+
const parsed = JSON.parse(raw);
|
|
122
|
+
generatedAt = typeof parsed.updatedAt === 'string' ? parsed.updatedAt : null;
|
|
123
|
+
status = typeof parsed.status === 'string' ? parsed.status : null;
|
|
124
|
+
} catch {
|
|
125
|
+
// Marker missing or unreadable
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const isStale = generatedAt === null || fileCount === 0;
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
hasWiki: true,
|
|
132
|
+
isStale,
|
|
133
|
+
generatedAt,
|
|
134
|
+
status,
|
|
135
|
+
fileCount
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Ensures standard wiki files exist without overwriting authored work.
|
|
141
|
+
* @param {object} options
|
|
142
|
+
* @param {string} options.targetDirectory Destination directory path
|
|
143
|
+
* @param {Record<string, string>} [options.templates={}] Filename to markdown content map
|
|
144
|
+
* @param {boolean} [options.dryRun=false] When true, computes actions without filesystem modification
|
|
145
|
+
* @returns {{ created: string[], preserved: string[], directory: string }}
|
|
146
|
+
*/
|
|
147
|
+
export function bootstrapWikiPages({ targetDirectory, templates = {}, dryRun = false } = {}) {
|
|
148
|
+
if (!targetDirectory || typeof targetDirectory !== 'string') {
|
|
149
|
+
throw new Error('Target directory must be a valid path string');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const destination = path.resolve(targetDirectory);
|
|
153
|
+
const created = [];
|
|
154
|
+
const preserved = [];
|
|
155
|
+
|
|
156
|
+
if (!dryRun && !fs.existsSync(destination)) {
|
|
157
|
+
fs.mkdirSync(destination, { recursive: true });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const [filename, content] of Object.entries(templates)) {
|
|
161
|
+
const filePath = path.join(destination, filename);
|
|
162
|
+
if (fs.existsSync(filePath)) {
|
|
163
|
+
preserved.push(filename);
|
|
164
|
+
} else {
|
|
165
|
+
created.push(filename);
|
|
166
|
+
if (!dryRun) {
|
|
167
|
+
fs.writeFileSync(filePath, String(content), 'utf8');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
directory: destination,
|
|
174
|
+
created,
|
|
175
|
+
preserved
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Compares two semantic version strings.
|
|
181
|
+
* Returns 1 if v1 > v2, -1 if v1 < v2, and 0 when versions match.
|
|
182
|
+
* @param {string} v1 First version string
|
|
183
|
+
* @param {string} v2 Second version string
|
|
184
|
+
* @returns {number}
|
|
185
|
+
*/
|
|
186
|
+
export function compareSemanticVersions(v1 = '', v2 = '') {
|
|
187
|
+
const parse = (v) => {
|
|
188
|
+
const clean = String(v || '').replace(/^[^\d]*/, '').split('-')[0];
|
|
189
|
+
const parts = clean.split('.').map(n => Number.parseInt(n, 10) || 0);
|
|
190
|
+
while (parts.length < 3) parts.push(0);
|
|
191
|
+
return parts;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const [maj1, min1, pat1] = parse(v1);
|
|
195
|
+
const [maj2, min2, pat2] = parse(v2);
|
|
196
|
+
|
|
197
|
+
if (maj1 !== maj2) return maj1 > maj2 ? 1 : -1;
|
|
198
|
+
if (min1 !== min2) return min1 > min2 ? 1 : -1;
|
|
199
|
+
if (pat1 !== pat2) return pat1 > pat2 ? 1 : -1;
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Creates an execution plan for propagating an updated OpenWiki generator across projects.
|
|
205
|
+
* @param {object} options
|
|
206
|
+
* @param {Array<{ name: string, root: string }>} options.projects Registered projects
|
|
207
|
+
* @param {string} options.latestVersion Upstream generator version
|
|
208
|
+
* @param {string} [options.currentVersion] Currently recorded generator version
|
|
209
|
+
* @returns {{ updateRequired: boolean, eligibleCount: number, plannedActions: Array<{ name: string, root: string, action: string, generatorVersion: string }> }}
|
|
210
|
+
*/
|
|
211
|
+
export function createPropagationPlan({ projects = [], latestVersion = '', currentVersion = '' } = {}) {
|
|
212
|
+
const updateRequired = compareSemanticVersions(latestVersion, currentVersion) > 0;
|
|
213
|
+
const plannedActions = [];
|
|
214
|
+
|
|
215
|
+
for (const project of projects) {
|
|
216
|
+
if (!project || typeof project.root !== 'string') continue;
|
|
217
|
+
const staleness = inspectWikiStaleness({ root: project.root });
|
|
218
|
+
if (!staleness.hasWiki) continue;
|
|
219
|
+
|
|
220
|
+
plannedActions.push({
|
|
221
|
+
name: project.name || path.basename(project.root),
|
|
222
|
+
root: project.root,
|
|
223
|
+
action: updateRequired ? 'regenerate' : 'verify',
|
|
224
|
+
generatorVersion: latestVersion || currentVersion || 'latest'
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
updateRequired,
|
|
230
|
+
eligibleCount: plannedActions.length,
|
|
231
|
+
plannedActions
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Standardizes a propagation execution receipt for persistent audit logs.
|
|
237
|
+
* @param {object} receipt
|
|
238
|
+
* @returns {{ timestamp: string, previousVersion: string, newVersion: string, totalProjects: number, updatedProjects: number, failedProjects: number, ok: boolean }}
|
|
239
|
+
*/
|
|
240
|
+
export function formatPropagationReceipt(receipt = {}) {
|
|
241
|
+
return {
|
|
242
|
+
timestamp: receipt.timestamp || new Date().toISOString(),
|
|
243
|
+
previousVersion: String(receipt.previousVersion || 'unknown'),
|
|
244
|
+
newVersion: String(receipt.newVersion || 'unknown'),
|
|
245
|
+
totalProjects: Number.isInteger(receipt.totalProjects) ? receipt.totalProjects : 0,
|
|
246
|
+
updatedProjects: Number.isInteger(receipt.updatedProjects) ? receipt.updatedProjects : 0,
|
|
247
|
+
failedProjects: Number.isInteger(receipt.failedProjects) ? receipt.failedProjects : 0,
|
|
248
|
+
ok: Boolean(receipt.ok ?? (receipt.failedProjects === 0))
|
|
249
|
+
};
|
|
250
|
+
}
|