moflo 4.10.24 → 4.10.25
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/.claude/guidance/shipped/moflo-yaml-reference.md +5 -5
- package/.claude/skills/meditate/SKILL.md +2 -2
- package/bin/cli-hooks/statusline.js +12 -8
- package/bin/cli.js +1 -1
- package/bin/hooks.mjs +1 -1
- package/bin/lib/meditate.mjs +1 -0
- package/bin/lib/pii-scrub.mjs +2 -5
- package/dist/src/cli/commands/daemon.js +4 -3
- package/dist/src/cli/commands/doctor-checks-deep.js +2 -2
- package/dist/src/cli/commands/doctor-checks-swarm.js +122 -13
- package/dist/src/cli/commands/hooks.js +7 -23
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/commands/mcp.js +2 -1
- package/dist/src/cli/commands/session.js +1 -1
- package/dist/src/cli/commands/start.js +1 -1
- package/dist/src/cli/commands/status.js +1 -1
- package/dist/src/cli/commands/task.js +1 -1
- package/dist/src/cli/commands/update.js +12 -12
- package/dist/src/cli/guidance/analyzer.js +3 -3
- package/dist/src/cli/guidance/gates.js +1 -1
- package/dist/src/cli/guidance/hooks.js +1 -1
- package/dist/src/cli/guidance/meta-governance.js +1 -1
- package/dist/src/cli/hooks/index.js +1 -1
- package/dist/src/cli/hooks/reasoningbank/guidance-provider.js +1 -1
- package/dist/src/cli/hooks/workers/index.js +1 -1
- package/dist/src/cli/hooks/workers/session-hook.js +0 -40
- package/dist/src/cli/index.js +2 -2
- package/dist/src/cli/init/executor.js +36 -20
- package/dist/src/cli/init/mcp-generator.js +10 -8
- package/dist/src/cli/init/settings-generator.js +10 -7
- package/dist/src/cli/init/types.js +2 -2
- package/dist/src/cli/mcp-server.js +2 -1
- package/dist/src/cli/memory/bridge-loader.js +42 -0
- package/dist/src/cli/memory/embedding-model.js +157 -0
- package/dist/src/cli/memory/entries-read.js +380 -0
- package/dist/src/cli/memory/entries-shared.js +73 -0
- package/dist/src/cli/memory/entries-write.js +384 -0
- package/dist/src/cli/memory/hnsw-singleton.js +242 -0
- package/dist/src/cli/memory/init.js +367 -0
- package/dist/src/cli/memory/learnings-overview.js +156 -0
- package/dist/src/cli/memory/memory-initializer.js +37 -2257
- package/dist/src/cli/memory/quantization.js +221 -0
- package/dist/src/cli/memory/schema.js +382 -0
- package/dist/src/cli/memory/verify.js +178 -0
- package/dist/src/cli/movector/index.js +1 -1
- package/dist/src/cli/plugins/store/discovery.js +9 -9
- package/dist/src/cli/{transfer/ipfs/client.js → plugins/store/ipfs-client.js} +4 -1
- package/dist/src/cli/plugins/tests/demo-plugin-store.js +1 -1
- package/dist/src/cli/plugins/tests/standalone-test.js +1 -1
- package/dist/src/cli/runtime/headless.js +5 -4
- package/dist/src/cli/scripts/publish-registry.js +6 -6
- package/dist/src/cli/services/daemon-dashboard.js +108 -7
- package/dist/src/cli/services/daemon-readiness.js +1 -1
- package/dist/src/cli/services/daemon-service.js +1 -1
- package/dist/src/cli/services/env-compat.js +29 -0
- package/dist/src/cli/services/hook-block-hash.js +5 -6
- package/dist/src/cli/services/registry-api.js +1 -1
- package/dist/src/cli/shared/core/config/loader.js +19 -11
- package/dist/src/cli/shared/events/example-usage.js +2 -2
- package/dist/src/cli/shared/events/index.js +1 -1
- package/dist/src/cli/shared/index.js +1 -1
- package/dist/src/cli/shared/mcp/index.js +1 -1
- package/dist/src/cli/shared/mcp/server.js +3 -3
- package/dist/src/cli/shared/plugin-interface.js +1 -1
- package/dist/src/cli/shared/plugins/index.js +1 -1
- package/dist/src/cli/shared/plugins/official/index.js +1 -1
- package/dist/src/cli/shared/security/index.js +1 -1
- package/dist/src/cli/shared/services/v3-progress.service.js +40 -29
- package/dist/src/cli/shared/types.js +1 -1
- package/dist/src/cli/swarm/coordination/swarm-hub.js +1 -1
- package/dist/src/cli/update/index.js +1 -1
- package/dist/src/cli/update/rate-limiter.js +3 -2
- package/dist/src/cli/version.js +1 -1
- package/package.json +2 -2
- package/dist/src/cli/commands/transfer-store.js +0 -428
- package/dist/src/cli/transfer/anonymization/index.js +0 -281
- package/dist/src/cli/transfer/deploy-seraphine.js +0 -205
- package/dist/src/cli/transfer/export.js +0 -113
- package/dist/src/cli/transfer/index.js +0 -31
- package/dist/src/cli/transfer/ipfs/upload.js +0 -411
- package/dist/src/cli/transfer/models/seraphine.js +0 -373
- package/dist/src/cli/transfer/serialization/cfp.js +0 -184
- package/dist/src/cli/transfer/storage/gcs.js +0 -242
- package/dist/src/cli/transfer/storage/index.js +0 -6
- package/dist/src/cli/transfer/store/discovery.js +0 -382
- package/dist/src/cli/transfer/store/download.js +0 -334
- package/dist/src/cli/transfer/store/index.js +0 -153
- package/dist/src/cli/transfer/store/publish.js +0 -294
- package/dist/src/cli/transfer/store/registry.js +0 -285
- package/dist/src/cli/transfer/store/search.js +0 -232
- package/dist/src/cli/transfer/store/tests/standalone-test.js +0 -190
- package/dist/src/cli/transfer/store/types.js +0 -6
- package/dist/src/cli/transfer/test-seraphine.js +0 -105
- package/dist/src/cli/transfer/tests/test-store.js +0 -214
- package/dist/src/cli/transfer/types.js +0 -6
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory initialization verification.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `memory-initializer.ts` (#1203 decomposition). Runs a
|
|
5
|
+
* self-test suite (schema, write, read, embedding, pattern, vector-index)
|
|
6
|
+
* against a database to confirm a healthy install — used by the healer /
|
|
7
|
+
* `flo memory verify` paths.
|
|
8
|
+
*
|
|
9
|
+
* @module memory/verify
|
|
10
|
+
*/
|
|
11
|
+
import { openDaemonDatabase } from './daemon-backend.js';
|
|
12
|
+
import { generateEmbedding } from './embedding-model.js';
|
|
13
|
+
/**
|
|
14
|
+
* Verify memory initialization works correctly
|
|
15
|
+
* Tests: write, read, search, patterns
|
|
16
|
+
*/
|
|
17
|
+
export async function verifyMemoryInit(dbPath, options) {
|
|
18
|
+
const { verbose = false } = options || {};
|
|
19
|
+
const tests = [];
|
|
20
|
+
try {
|
|
21
|
+
const db = openDaemonDatabase(dbPath);
|
|
22
|
+
// Test 1: Schema verification
|
|
23
|
+
const schemaStart = Date.now();
|
|
24
|
+
const tables = db.exec("SELECT name FROM sqlite_master WHERE type='table'");
|
|
25
|
+
const tableNames = tables[0]?.values?.map(v => v[0]) || [];
|
|
26
|
+
const expectedTables = ['memory_entries', 'patterns', 'metadata', 'vector_indexes'];
|
|
27
|
+
const missingTables = expectedTables.filter(t => !tableNames.includes(t));
|
|
28
|
+
tests.push({
|
|
29
|
+
name: 'Schema verification',
|
|
30
|
+
passed: missingTables.length === 0,
|
|
31
|
+
details: missingTables.length > 0 ? `Missing: ${missingTables.join(', ')}` : `${tableNames.length} tables found`,
|
|
32
|
+
duration: Date.now() - schemaStart
|
|
33
|
+
});
|
|
34
|
+
// Test 2: Write entry
|
|
35
|
+
const writeStart = Date.now();
|
|
36
|
+
const testId = `test_${Date.now()}`;
|
|
37
|
+
const testKey = 'verification_test';
|
|
38
|
+
const testValue = 'This is a verification test entry for memory initialization';
|
|
39
|
+
try {
|
|
40
|
+
db.run(`
|
|
41
|
+
INSERT INTO memory_entries (id, key, namespace, content, type, created_at, updated_at)
|
|
42
|
+
VALUES (?, ?, 'test', ?, 'semantic', ?, ?)
|
|
43
|
+
`, [testId, testKey, testValue, Date.now(), Date.now()]);
|
|
44
|
+
tests.push({
|
|
45
|
+
name: 'Write entry',
|
|
46
|
+
passed: true,
|
|
47
|
+
details: 'Entry written successfully',
|
|
48
|
+
duration: Date.now() - writeStart
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
tests.push({
|
|
53
|
+
name: 'Write entry',
|
|
54
|
+
passed: false,
|
|
55
|
+
details: e instanceof Error ? e.message : 'Write failed',
|
|
56
|
+
duration: Date.now() - writeStart
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// Test 3: Read entry
|
|
60
|
+
const readStart = Date.now();
|
|
61
|
+
try {
|
|
62
|
+
const result = db.exec(`SELECT content FROM memory_entries WHERE id = ?`, [testId]);
|
|
63
|
+
const content = result[0]?.values[0]?.[0];
|
|
64
|
+
tests.push({
|
|
65
|
+
name: 'Read entry',
|
|
66
|
+
passed: content === testValue,
|
|
67
|
+
details: content === testValue ? 'Content matches' : 'Content mismatch',
|
|
68
|
+
duration: Date.now() - readStart
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
tests.push({
|
|
73
|
+
name: 'Read entry',
|
|
74
|
+
passed: false,
|
|
75
|
+
details: e instanceof Error ? e.message : 'Read failed',
|
|
76
|
+
duration: Date.now() - readStart
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// Test 4: Write with embedding
|
|
80
|
+
const embeddingStart = Date.now();
|
|
81
|
+
try {
|
|
82
|
+
const { embedding, dimensions, model } = await generateEmbedding(testValue);
|
|
83
|
+
const embeddingJson = JSON.stringify(embedding);
|
|
84
|
+
db.run(`
|
|
85
|
+
UPDATE memory_entries
|
|
86
|
+
SET embedding = ?, embedding_dimensions = ?, embedding_model = ?
|
|
87
|
+
WHERE id = ?
|
|
88
|
+
`, [embeddingJson, dimensions, model, testId]);
|
|
89
|
+
tests.push({
|
|
90
|
+
name: 'Generate embedding',
|
|
91
|
+
passed: true,
|
|
92
|
+
details: `${dimensions}-dim vector (${model})`,
|
|
93
|
+
duration: Date.now() - embeddingStart
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
tests.push({
|
|
98
|
+
name: 'Generate embedding',
|
|
99
|
+
passed: false,
|
|
100
|
+
details: e instanceof Error ? e.message : 'Embedding failed',
|
|
101
|
+
duration: Date.now() - embeddingStart
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Test 5: Pattern storage
|
|
105
|
+
const patternStart = Date.now();
|
|
106
|
+
try {
|
|
107
|
+
const patternId = `pattern_${Date.now()}`;
|
|
108
|
+
db.run(`
|
|
109
|
+
INSERT INTO patterns (id, name, pattern_type, condition, action, confidence, created_at, updated_at)
|
|
110
|
+
VALUES (?, 'test-pattern', 'task-routing', 'test condition', 'test action', 0.5, ?, ?)
|
|
111
|
+
`, [patternId, Date.now(), Date.now()]);
|
|
112
|
+
tests.push({
|
|
113
|
+
name: 'Pattern storage',
|
|
114
|
+
passed: true,
|
|
115
|
+
details: 'Pattern stored with confidence scoring',
|
|
116
|
+
duration: Date.now() - patternStart
|
|
117
|
+
});
|
|
118
|
+
// Cleanup test pattern
|
|
119
|
+
db.run(`DELETE FROM patterns WHERE id = ?`, [patternId]);
|
|
120
|
+
}
|
|
121
|
+
catch (e) {
|
|
122
|
+
tests.push({
|
|
123
|
+
name: 'Pattern storage',
|
|
124
|
+
passed: false,
|
|
125
|
+
details: e instanceof Error ? e.message : 'Pattern storage failed',
|
|
126
|
+
duration: Date.now() - patternStart
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
// Test 6: Vector index configuration
|
|
130
|
+
const indexStart = Date.now();
|
|
131
|
+
try {
|
|
132
|
+
const indexResult = db.exec(`SELECT name, dimensions, hnsw_m, hnsw_ef_construction FROM vector_indexes`);
|
|
133
|
+
const indexes = indexResult[0]?.values || [];
|
|
134
|
+
tests.push({
|
|
135
|
+
name: 'Vector index config',
|
|
136
|
+
passed: indexes.length > 0,
|
|
137
|
+
details: `${indexes.length} indexes configured (HNSW M=16, ef=200)`,
|
|
138
|
+
duration: Date.now() - indexStart
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
tests.push({
|
|
143
|
+
name: 'Vector index config',
|
|
144
|
+
passed: false,
|
|
145
|
+
details: e instanceof Error ? e.message : 'Index check failed',
|
|
146
|
+
duration: Date.now() - indexStart
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
// Cleanup test entry — WAL persists this DELETE incrementally; no
|
|
150
|
+
// export+rewrite needed (the sql.js whole-file dump that lived here
|
|
151
|
+
// was the multi-writer clobber vector epic #1078 killed).
|
|
152
|
+
db.run(`DELETE FROM memory_entries WHERE id = ?`, [testId]);
|
|
153
|
+
db.close();
|
|
154
|
+
const passed = tests.filter(t => t.passed).length;
|
|
155
|
+
const failed = tests.filter(t => !t.passed).length;
|
|
156
|
+
return {
|
|
157
|
+
success: failed === 0,
|
|
158
|
+
tests,
|
|
159
|
+
summary: {
|
|
160
|
+
passed,
|
|
161
|
+
failed,
|
|
162
|
+
total: tests.length
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
return {
|
|
168
|
+
success: false,
|
|
169
|
+
tests: [{
|
|
170
|
+
name: 'Database access',
|
|
171
|
+
passed: false,
|
|
172
|
+
details: error instanceof Error ? error.message : 'Unknown error'
|
|
173
|
+
}],
|
|
174
|
+
summary: { passed: 0, failed: 1, total: 1 }
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Parallel implementation to pattern store for plugins
|
|
5
5
|
*/
|
|
6
6
|
import * as crypto from 'crypto';
|
|
7
|
-
import { resolveIPNS, fetchFromIPFS } from '
|
|
7
|
+
import { resolveIPNS, fetchFromIPFS } from './ipfs-client.js';
|
|
8
8
|
/**
|
|
9
9
|
* Fetch real npm download stats for a package
|
|
10
10
|
*/
|
|
@@ -50,8 +50,8 @@ export const MODEL_REGISTRY_CID = 'QmNr1yYMKi7YBaL8JSztQyuB5ZUaTdRMLxJC1pBpGbjsT
|
|
|
50
50
|
export const DEFAULT_PLUGIN_STORE_CONFIG = {
|
|
51
51
|
registries: [
|
|
52
52
|
{
|
|
53
|
-
name: '
|
|
54
|
-
description: 'Official
|
|
53
|
+
name: 'moflo-official',
|
|
54
|
+
description: 'Official MoFlo plugin registry',
|
|
55
55
|
// Use direct CID for reliable resolution (IPNS can be slow)
|
|
56
56
|
ipnsName: LIVE_REGISTRY_CID,
|
|
57
57
|
gateway: 'https://gateway.pinata.cloud',
|
|
@@ -69,7 +69,7 @@ export const DEFAULT_PLUGIN_STORE_CONFIG = {
|
|
|
69
69
|
official: false,
|
|
70
70
|
},
|
|
71
71
|
],
|
|
72
|
-
defaultRegistry: '
|
|
72
|
+
defaultRegistry: 'moflo-official',
|
|
73
73
|
gateway: 'https://gateway.pinata.cloud',
|
|
74
74
|
timeout: 30000,
|
|
75
75
|
cacheDir: '.moflo/plugins/cache',
|
|
@@ -186,8 +186,8 @@ export class PluginDiscoveryService {
|
|
|
186
186
|
],
|
|
187
187
|
authors: [
|
|
188
188
|
{
|
|
189
|
-
id: '
|
|
190
|
-
displayName: '
|
|
189
|
+
id: 'moflo-team',
|
|
190
|
+
displayName: 'MoFlo Team',
|
|
191
191
|
verified: true,
|
|
192
192
|
plugins: plugins.length,
|
|
193
193
|
totalDownloads: plugins.reduce((sum, p) => sum + p.downloads, 0),
|
|
@@ -224,8 +224,8 @@ export class PluginDiscoveryService {
|
|
|
224
224
|
getDemoPlugins() {
|
|
225
225
|
const baseTime = new Date().toISOString();
|
|
226
226
|
const officialAuthor = {
|
|
227
|
-
id: '
|
|
228
|
-
displayName: '
|
|
227
|
+
id: 'moflo-team',
|
|
228
|
+
displayName: 'MoFlo Team',
|
|
229
229
|
verified: true,
|
|
230
230
|
plugins: 5,
|
|
231
231
|
totalDownloads: 50000,
|
|
@@ -273,7 +273,7 @@ export class PluginDiscoveryService {
|
|
|
273
273
|
id: 'community-analytics',
|
|
274
274
|
name: 'community-analytics',
|
|
275
275
|
displayName: 'Analytics Dashboard',
|
|
276
|
-
description: 'Analytics and metrics visualization for
|
|
276
|
+
description: 'Analytics and metrics visualization for MoFlo operations',
|
|
277
277
|
version: '1.2.0',
|
|
278
278
|
cid: 'bafybeianalyticsplugin',
|
|
279
279
|
size: 210000,
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* - Cloudflare IPFS
|
|
8
8
|
* - Protocol Labs ipfs.io
|
|
9
9
|
* - dweb.link (LibP2P)
|
|
10
|
+
*
|
|
11
|
+
* Relocated from the (deleted) transfer/ pattern-marketplace tree in #1210; the
|
|
12
|
+
* plugin store is the sole consumer of these primitives.
|
|
10
13
|
*/
|
|
11
14
|
import * as crypto from 'crypto';
|
|
12
15
|
import { errorDetail } from '../../shared/utils/error-detail.js';
|
|
@@ -305,4 +308,4 @@ export function formatBytes(bytes) {
|
|
|
305
308
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
306
309
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
307
310
|
}
|
|
308
|
-
//# sourceMappingURL=client.js.map
|
|
311
|
+
//# sourceMappingURL=ipfs-client.js.map
|
|
@@ -7,7 +7,7 @@ import { createPluginDiscoveryService, searchPlugins, getOfficialPlugins, } from
|
|
|
7
7
|
async function demo() {
|
|
8
8
|
console.log('');
|
|
9
9
|
console.log('╔══════════════════════════════════════════════════════════════╗');
|
|
10
|
-
console.log('║
|
|
10
|
+
console.log('║ MoFlo V3 - IPFS PLUGIN STORE DEMO ║');
|
|
11
11
|
console.log('║ Decentralized Plugin Marketplace ║');
|
|
12
12
|
console.log('╚══════════════════════════════════════════════════════════════╝');
|
|
13
13
|
console.log('');
|
|
@@ -12,7 +12,7 @@ import { createPluginDiscoveryService, searchPlugins, getFeaturedPlugins, getOff
|
|
|
12
12
|
async function main() {
|
|
13
13
|
console.log('');
|
|
14
14
|
console.log('╔══════════════════════════════════════════════════════════════╗');
|
|
15
|
-
console.log('║
|
|
15
|
+
console.log('║ MoFlo V3 - STANDALONE PLUGIN STORE TEST ║');
|
|
16
16
|
console.log('║ IPFS-Based Decentralized Plugin Marketplace ║');
|
|
17
17
|
console.log('╚══════════════════════════════════════════════════════════════╝');
|
|
18
18
|
console.log('');
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* npx moflo headless --benchmark
|
|
10
10
|
*
|
|
11
11
|
* Environment:
|
|
12
|
-
*
|
|
12
|
+
* MOFLO_HEADLESS=true
|
|
13
13
|
* CLAUDE_CODE_HEADLESS=true
|
|
14
14
|
*
|
|
15
15
|
* @module v3/cli/runtime/headless
|
|
@@ -20,6 +20,7 @@ import { attachSignalHandlers } from '../shared/resilience/signal-handlers.js';
|
|
|
20
20
|
import { errorDetail } from '../shared/utils/error-detail.js';
|
|
21
21
|
import { initializeIntelligence, benchmarkAdaptation, getIntelligenceStats } from '../memory/intelligence.js';
|
|
22
22
|
import { getHNSWStatus, batchCosineSim, flashAttentionSearch } from '../memory/memory-initializer.js';
|
|
23
|
+
import { readMofloEnv } from '../services/env-compat.js';
|
|
23
24
|
// ============================================================================
|
|
24
25
|
// Main Runtime
|
|
25
26
|
// ============================================================================
|
|
@@ -85,7 +86,7 @@ Options:
|
|
|
85
86
|
-h, --help Show help
|
|
86
87
|
|
|
87
88
|
Environment:
|
|
88
|
-
|
|
89
|
+
MOFLO_HEADLESS=true Enable headless mode
|
|
89
90
|
CLAUDE_CODE_HEADLESS=true Enable Claude Code headless
|
|
90
91
|
|
|
91
92
|
Examples:
|
|
@@ -237,7 +238,7 @@ async function showStatus() {
|
|
|
237
238
|
console.log(` Initialized: ${hnsw.initialized}`);
|
|
238
239
|
console.log(` Entries: ${hnsw.entryCount}`);
|
|
239
240
|
console.log('\nEnvironment:');
|
|
240
|
-
console.log(`
|
|
241
|
+
console.log(` MOFLO_HEADLESS: ${readMofloEnv('HEADLESS') || 'not set'}`);
|
|
241
242
|
console.log(` CLAUDE_CODE_HEADLESS: ${process.env.CLAUDE_CODE_HEADLESS || 'not set'}`);
|
|
242
243
|
console.log(` NODE_ENV: ${process.env.NODE_ENV || 'development'}`);
|
|
243
244
|
}
|
|
@@ -246,7 +247,7 @@ async function showStatus() {
|
|
|
246
247
|
*/
|
|
247
248
|
async function main() {
|
|
248
249
|
// Set headless environment
|
|
249
|
-
process.env.
|
|
250
|
+
process.env.MOFLO_HEADLESS = 'true';
|
|
250
251
|
const config = parseArgs();
|
|
251
252
|
try {
|
|
252
253
|
switch (config.mode) {
|
|
@@ -123,13 +123,13 @@ async function generateRegistry() {
|
|
|
123
123
|
id: pkg,
|
|
124
124
|
name: pkg,
|
|
125
125
|
displayName: pkg.replace('@moflo/plugin-', '').replace('@moflo/', ''),
|
|
126
|
-
description: `Official
|
|
126
|
+
description: `Official MoFlo plugin: ${pkg}`,
|
|
127
127
|
version: stats?.version || '0.0.0',
|
|
128
128
|
size: 100000,
|
|
129
129
|
checksum: `sha256:${crypto.randomBytes(32).toString('hex')}`,
|
|
130
130
|
author: {
|
|
131
|
-
id: '
|
|
132
|
-
displayName: '
|
|
131
|
+
id: 'moflo-team',
|
|
132
|
+
displayName: 'MoFlo Team',
|
|
133
133
|
verified: true,
|
|
134
134
|
},
|
|
135
135
|
license: 'MIT',
|
|
@@ -156,7 +156,7 @@ async function generateRegistry() {
|
|
|
156
156
|
ipnsName: '', // Will be set after publishing
|
|
157
157
|
plugins,
|
|
158
158
|
categories: [
|
|
159
|
-
{ id: 'official', name: 'Official', description: 'Official
|
|
159
|
+
{ id: 'official', name: 'Official', description: 'Official MoFlo plugins', pluginCount: plugins.length },
|
|
160
160
|
],
|
|
161
161
|
totalPlugins: plugins.length,
|
|
162
162
|
totalDownloads,
|
|
@@ -215,7 +215,7 @@ async function main() {
|
|
|
215
215
|
// Pin to IPFS
|
|
216
216
|
console.log('\n📌 Pinning to IPFS via Pinata...');
|
|
217
217
|
try {
|
|
218
|
-
const result = await pinToIPFS(registry, '
|
|
218
|
+
const result = await pinToIPFS(registry, 'moflo-plugin-registry', jwt);
|
|
219
219
|
console.log('\n✅ Published successfully!');
|
|
220
220
|
console.log(` CID: ${result.IpfsHash}`);
|
|
221
221
|
console.log(` Size: ${(result.PinSize / 1024).toFixed(2)} KB`);
|
|
@@ -232,7 +232,7 @@ async function main() {
|
|
|
232
232
|
console.log('\n📝 Next steps:');
|
|
233
233
|
console.log(' 1. Update DEFAULT_PLUGIN_STORE_CONFIG in discovery.ts with the new CID');
|
|
234
234
|
console.log(' 2. If using IPNS, update the IPNS pointer via Pinata dashboard');
|
|
235
|
-
console.log(' 3. Test with: npx
|
|
235
|
+
console.log(' 3. Test with: npx moflo@latest plugins list');
|
|
236
236
|
}
|
|
237
237
|
catch (error) {
|
|
238
238
|
console.error('\n❌ Publish failed:', error);
|
|
@@ -289,6 +289,23 @@ async function handleMemoryStats() {
|
|
|
289
289
|
available: total > 0 || Object.keys(namespaces).length > 0,
|
|
290
290
|
};
|
|
291
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* Build the `/api/learnings` response (#1203).
|
|
294
|
+
*
|
|
295
|
+
* Surfaces the `learnings` namespace for the Luminarium "Learnings" panel:
|
|
296
|
+
* recent lessons (key + first line + capped body), a per-day growth series,
|
|
297
|
+
* and a provenance tally from the write-time `source:<origin>` tag. The
|
|
298
|
+
* `total` is an authoritative COUNT (not the capped recent-list length), so
|
|
299
|
+
* the panel never under-reports — the #1149 "memory_stats lies" guard.
|
|
300
|
+
*
|
|
301
|
+
* Errors propagate to the request handler's 500 path (matching
|
|
302
|
+
* `handleMemoryStats`) rather than degrading to a fake-empty panel.
|
|
303
|
+
*/
|
|
304
|
+
async function handleLearnings() {
|
|
305
|
+
const { getLearningsOverview } = await import('../memory/learnings-overview.js');
|
|
306
|
+
const overview = await getLearningsOverview();
|
|
307
|
+
return { ok: true, available: overview.total > 0, ...overview };
|
|
308
|
+
}
|
|
292
309
|
/**
|
|
293
310
|
* Build the `/api/claude-stats` response (#1044).
|
|
294
311
|
*
|
|
@@ -488,6 +505,9 @@ async function handleRequest(req, res, daemon, opts) {
|
|
|
488
505
|
else if (url === '/api/memory/stats') {
|
|
489
506
|
sendJson(res, 200, await handleMemoryStats());
|
|
490
507
|
}
|
|
508
|
+
else if (url === '/api/learnings') {
|
|
509
|
+
sendJson(res, 200, await handleLearnings());
|
|
510
|
+
}
|
|
491
511
|
else if (url === '/api/claude-stats') {
|
|
492
512
|
sendJson(res, 200, await handleClaudeStats());
|
|
493
513
|
}
|
|
@@ -784,6 +804,20 @@ const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
784
804
|
.stat-card { background: #161b22; border: 1px solid #30363d; border-radius: 6px; padding: 12px; }
|
|
785
805
|
.stat-card .label { color: #8b949e; font-size: 0.75rem; }
|
|
786
806
|
.stat-card .value { font-size: 1.25rem; font-weight: 700; color: #58a6ff; }
|
|
807
|
+
/* Learnings panel (#1203) */
|
|
808
|
+
.lrn-bars { display: flex; align-items: flex-end; gap: 3px; height: 70px; padding: 8px 0; margin-bottom: 16px; }
|
|
809
|
+
.lrn-bar { flex: 1; background: #58a6ff; border-radius: 2px 2px 0 0; min-height: 2px; opacity: 0.85; }
|
|
810
|
+
.lrn-bar:hover { opacity: 1; }
|
|
811
|
+
.lrn-prov { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
|
|
812
|
+
.lrn-item { background: #161b22; border: 1px solid #30363d; border-radius: 6px; margin-bottom: 8px; }
|
|
813
|
+
.lrn-head { display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: pointer; }
|
|
814
|
+
.lrn-head:hover { background: #1c2128; }
|
|
815
|
+
.lrn-key { color: #58a6ff; font-weight: 600; font-size: 0.85rem; }
|
|
816
|
+
.lrn-first { color: #c9d1d9; font-size: 0.82rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
|
|
817
|
+
.lrn-body { padding: 0 12px 12px; color: #c9d1d9; font-size: 0.83rem; white-space: pre-wrap; line-height: 1.55; border-top: 1px solid #21262d; margin-top: 2px; padding-top: 10px; }
|
|
818
|
+
.lrn-item.collapsed .lrn-body { display: none; }
|
|
819
|
+
.lrn-item.collapsed .lrn-chevron { transform: rotate(-90deg); }
|
|
820
|
+
.lrn-chevron { transition: transform 0.15s; display: inline-block; color: #484f58; font-size: 0.75rem; }
|
|
787
821
|
.poll-indicator { position: fixed; top: 8px; right: 12px; font-size: 0.7rem; color: #484f58; }
|
|
788
822
|
.api-links { margin-top: 12px; padding: 12px 16px; background: #161b22; border: 1px solid #30363d; border-radius: 6px; }
|
|
789
823
|
.api-links a { color: #58a6ff; text-decoration: none; font-size: 0.85rem; margin-right: 16px; }
|
|
@@ -819,12 +853,13 @@ const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
819
853
|
<div id="panel-schedules" class="panel" style="display:none"><div id="schedules-active"></div><div id="schedules-events"></div></div>
|
|
820
854
|
<div id="panel-executions" class="panel" style="display:none"></div>
|
|
821
855
|
<div id="panel-memory" class="panel" style="display:none"></div>
|
|
856
|
+
<div id="panel-learnings" class="panel" style="display:none"><div class="loading-block" role="status" aria-label="Loading learnings"><div class="spinner"></div><div class="msg">Reading learnings…</div></div></div>
|
|
822
857
|
<div id="panel-claude-stats" class="panel" style="display:none"><div class="loading-block" role="status" aria-label="Loading Claude Code transcripts"><div class="spinner"></div><div class="msg">Reading Claude Code transcripts…</div><div class="hint">First load can take 10–15 seconds — moflo walks every session file in this project's transcript directory. Subsequent loads in this tab are much faster.</div></div></div>
|
|
823
858
|
<div id="poll-indicator" class="poll-indicator"></div>
|
|
824
859
|
<script>
|
|
825
860
|
// Tab navigation — plain DOM, no framework
|
|
826
|
-
const tabIds = ['workers', 'schedules', 'executions', 'memory', 'claude-stats'];
|
|
827
|
-
const tabLabels = ['Workers', 'Schedules', 'Flo Runs', 'Memory', 'Claude Stats'];
|
|
861
|
+
const tabIds = ['workers', 'schedules', 'executions', 'memory', 'learnings', 'claude-stats'];
|
|
862
|
+
const tabLabels = ['Workers', 'Schedules', 'Flo Runs', 'Memory', 'Learnings', 'Claude Stats'];
|
|
828
863
|
let activeTab = 'workers';
|
|
829
864
|
|
|
830
865
|
function switchTab(id) {
|
|
@@ -836,10 +871,10 @@ const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
836
871
|
document.querySelectorAll('.nav-tab').forEach(el => {
|
|
837
872
|
el.classList.toggle('active', el.dataset.tab === id);
|
|
838
873
|
});
|
|
839
|
-
// Tabs whose data is fetched lazily (
|
|
840
|
-
//
|
|
841
|
-
//
|
|
842
|
-
if (id === 'claude-stats' && prev !== id && typeof poll === 'function') {
|
|
874
|
+
// Tabs whose data is fetched lazily (Claude Stats + Learnings) need an
|
|
875
|
+
// immediate poll on entry — otherwise the user waits up to the 5s
|
|
876
|
+
// polling interval for first paint.
|
|
877
|
+
if ((id === 'claude-stats' || id === 'learnings') && prev !== id && typeof poll === 'function') {
|
|
843
878
|
poll();
|
|
844
879
|
}
|
|
845
880
|
}
|
|
@@ -1137,6 +1172,67 @@ const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1137
1172
|
'<table><thead><tr><th>Namespace</th><th>Entries</th></tr></thead><tbody>' + rows + '</tbody></table>';
|
|
1138
1173
|
}
|
|
1139
1174
|
|
|
1175
|
+
// Friendly labels + badge colors for the write-time source:<origin> tag (#1203).
|
|
1176
|
+
const PROV_LABELS = { 'auto-meditate': 'Auto-meditate', 'meditate-manual': '/meditate', 'manual': 'Manual', 'user': 'User (locked)', 'unknown': 'Legacy / unknown' };
|
|
1177
|
+
const PROV_COLORS = { 'auto-meditate': 'green', 'meditate-manual': 'yellow', 'manual': 'gray', 'user': 'gray', 'unknown': 'gray' };
|
|
1178
|
+
const provLabel = (s) => PROV_LABELS[s] || s;
|
|
1179
|
+
const provColor = (s) => PROV_COLORS[s] || 'gray';
|
|
1180
|
+
|
|
1181
|
+
function renderLearnings(l) {
|
|
1182
|
+
const el = document.getElementById('panel-learnings');
|
|
1183
|
+
// Null on first paint AND on fetch error (.catch(() => null)) — show the
|
|
1184
|
+
// spinner block on both so the tab never looks frozen.
|
|
1185
|
+
if (!l) {
|
|
1186
|
+
el.innerHTML = '<div class="loading-block" role="status" aria-label="Loading learnings"><div class="spinner"></div><div class="msg">Reading learnings…</div></div>';
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
if (!l.available) {
|
|
1190
|
+
el.innerHTML = '<h2>Learnings</h2><div class="empty">No learnings captured yet — run <code>/meditate</code>, or let auto-meditate distill a session.</div>';
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
const cards = '<div class="grid">' +
|
|
1195
|
+
'<div class="stat-card"><div class="label">Total Learnings</div><div class="value">' + l.total + '</div></div>' +
|
|
1196
|
+
'<div class="stat-card"><div class="label">Added (7d)</div><div class="value">' + l.addedLast7d + '</div></div>' +
|
|
1197
|
+
'<div class="stat-card"><div class="label">Added (30d)</div><div class="value">' + l.addedLast30d + '</div></div>' +
|
|
1198
|
+
'</div>';
|
|
1199
|
+
|
|
1200
|
+
const provEntries = Object.entries(l.provenance || {}).sort((a, b) => b[1] - a[1]);
|
|
1201
|
+
// esc() the source label — for unknown sources provLabel returns the raw
|
|
1202
|
+
// DB tag value, so it must be escaped before going into badge() (which
|
|
1203
|
+
// does not escape its text). Guards stored-XSS from a crafted source tag.
|
|
1204
|
+
const provHtml = provEntries.length
|
|
1205
|
+
? '<div class="lrn-prov">' + provEntries.map(e => badge(esc(provLabel(e[0])) + ': ' + e[1], provColor(e[0]))).join('') + '</div>'
|
|
1206
|
+
: '<div class="empty">No provenance recorded</div>';
|
|
1207
|
+
|
|
1208
|
+
let growthHtml = '';
|
|
1209
|
+
if (l.growth && l.growth.length) {
|
|
1210
|
+
const maxG = Math.max(1, ...l.growth.map(g => g.count));
|
|
1211
|
+
const bars = l.growth.map(g =>
|
|
1212
|
+
'<div class="lrn-bar" style="height:' + Math.max(2, Math.round(g.count / maxG * 60)) + 'px" title="' + esc(g.date) + ': ' + g.count + ' new"></div>'
|
|
1213
|
+
).join('');
|
|
1214
|
+
growthHtml = '<h2>Growth (new learnings per day)</h2><div class="lrn-bars">' + bars + '</div>';
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
const recentHtml = (l.recent || []).map(r => {
|
|
1218
|
+
const srcBadge = r.source ? ' ' + badge(esc(provLabel(r.source)), provColor(r.source)) : '';
|
|
1219
|
+
const trunc = r.truncated ? '<span class="dim"> … (truncated)</span>' : '';
|
|
1220
|
+
return '<div class="lrn-item collapsed">' +
|
|
1221
|
+
'<div class="lrn-head" onclick="this.parentElement.classList.toggle(\\'collapsed\\')">' +
|
|
1222
|
+
'<span class="lrn-chevron">▼</span>' +
|
|
1223
|
+
'<span class="lrn-key">' + esc(r.key) + '</span>' + srcBadge +
|
|
1224
|
+
'<span class="lrn-first">' + esc(r.firstLine) + '</span>' +
|
|
1225
|
+
'<span class="dim" style="margin-left:auto">' + fmtTimeAgo(r.updatedAt) + '</span>' +
|
|
1226
|
+
'</div>' +
|
|
1227
|
+
'<div class="lrn-body">' + esc(r.body) + trunc + '</div>' +
|
|
1228
|
+
'</div>';
|
|
1229
|
+
}).join('');
|
|
1230
|
+
const recentSection = '<h2>Recent Learnings (' + (l.recent ? l.recent.length : 0) + ' of ' + l.total + ')</h2>' +
|
|
1231
|
+
(recentHtml || '<div class="empty">None</div>');
|
|
1232
|
+
|
|
1233
|
+
el.innerHTML = cards + '<h2>Provenance</h2>' + provHtml + growthHtml + recentSection;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1140
1236
|
// Format: 1234567 → "1.23M", 5432 → "5.43K"
|
|
1141
1237
|
const fmtCount = (n) => {
|
|
1142
1238
|
if (n == null) return '-';
|
|
@@ -1250,6 +1346,7 @@ const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1250
1346
|
// four lightweight endpoints. Switching to the tab triggers an
|
|
1251
1347
|
// immediate poll so the user doesn't wait up to 5s for first paint.
|
|
1252
1348
|
const wantClaudeStats = activeTab === 'claude-stats';
|
|
1349
|
+
const wantLearnings = activeTab === 'learnings';
|
|
1253
1350
|
const fetches = [
|
|
1254
1351
|
fetch('/api/status').then(r => r.json()),
|
|
1255
1352
|
fetch('/api/schedules').then(r => r.json()),
|
|
@@ -1258,14 +1355,18 @@ const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1258
1355
|
wantClaudeStats
|
|
1259
1356
|
? fetch('/api/claude-stats').then(r => r.json()).catch(() => null)
|
|
1260
1357
|
: Promise.resolve(null),
|
|
1358
|
+
wantLearnings
|
|
1359
|
+
? fetch('/api/learnings').then(r => r.json()).catch(() => null)
|
|
1360
|
+
: Promise.resolve(null),
|
|
1261
1361
|
];
|
|
1262
|
-
const [s, sc, w, m, cs] = await Promise.all(fetches);
|
|
1362
|
+
const [s, sc, w, m, cs, lr] = await Promise.all(fetches);
|
|
1263
1363
|
renderStatus(s);
|
|
1264
1364
|
renderWorkers(s);
|
|
1265
1365
|
renderSchedules(sc);
|
|
1266
1366
|
renderExecutions(w);
|
|
1267
1367
|
renderMemory(m);
|
|
1268
1368
|
if (wantClaudeStats) renderClaudeStats(cs);
|
|
1369
|
+
if (wantLearnings) renderLearnings(lr);
|
|
1269
1370
|
document.getElementById('poll-indicator').textContent = 'Last poll: ' + new Date().toLocaleTimeString();
|
|
1270
1371
|
} catch (e) {
|
|
1271
1372
|
console.error('Poll failed:', e);
|
|
@@ -85,7 +85,7 @@ async function defaultStartDaemon(projectRoot) {
|
|
|
85
85
|
const spawnArgs = [cliPath, 'daemon', 'start', '--foreground', '--quiet'];
|
|
86
86
|
const daemonEnv = {
|
|
87
87
|
...process.env,
|
|
88
|
-
|
|
88
|
+
MOFLO_DAEMON: '1',
|
|
89
89
|
...(process.platform === 'darwin' ? { NOHUP: '1' } : {}),
|
|
90
90
|
};
|
|
91
91
|
// On Windows, join command + args into a single shell string to avoid
|
|
@@ -176,7 +176,7 @@ function generateSystemdUnit(projectRoot, nodePath, cliPath) {
|
|
|
176
176
|
`WorkingDirectory=${projectRoot}`,
|
|
177
177
|
'Restart=on-failure',
|
|
178
178
|
'RestartSec=10',
|
|
179
|
-
`Environment=
|
|
179
|
+
`Environment=MOFLO_DAEMON=1`,
|
|
180
180
|
'',
|
|
181
181
|
'[Install]',
|
|
182
182
|
'WantedBy=default.target',
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backward-compatible environment-variable reader for the claude-flow → moflo
|
|
3
|
+
* rebrand (issue #1209).
|
|
4
|
+
*
|
|
5
|
+
* Reads the canonical `MOFLO_<suffix>` variable first, falling back to the
|
|
6
|
+
* pre-rebrand `CLAUDE_FLOW_<suffix>` name. Every writer across the codebase
|
|
7
|
+
* emits ONLY `MOFLO_*`; this fallback exists so consumers whose shell env or
|
|
8
|
+
* already-persisted files (e.g. an installed systemd unit written by an older
|
|
9
|
+
* moflo) still set the old names keep working without a manual migration.
|
|
10
|
+
*
|
|
11
|
+
* Deprecation window: keep the `CLAUDE_FLOW_*` fallback for at least one
|
|
12
|
+
* release cycle after the rebrand ships, then drop both this fallback and the
|
|
13
|
+
* writer-side exemption in `published-package-drift-guard.test.ts`.
|
|
14
|
+
*
|
|
15
|
+
* Zero imports on purpose — safe to import from any layer (including
|
|
16
|
+
* `shared/core/config`) without risking a cycle.
|
|
17
|
+
*/
|
|
18
|
+
const MOFLO_PREFIX = 'MOFLO_';
|
|
19
|
+
const LEGACY_PREFIX = 'CLAUDE_FLOW_';
|
|
20
|
+
/**
|
|
21
|
+
* Read an env var by its `MOFLO_<suffix>` name, falling back to the legacy
|
|
22
|
+
* `CLAUDE_FLOW_<suffix>` name. Returns `undefined` when neither is set.
|
|
23
|
+
*
|
|
24
|
+
* @param suffix the shared variable suffix, e.g. `'MAX_AGENTS'`, `'DAEMON'`.
|
|
25
|
+
*/
|
|
26
|
+
export function readMofloEnv(suffix) {
|
|
27
|
+
return process.env[MOFLO_PREFIX + suffix] ?? process.env[LEGACY_PREFIX + suffix];
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=env-compat.js.map
|
|
@@ -274,12 +274,11 @@ export function computeHookBlockDrift(consumerHooks, referenceHooks) {
|
|
|
274
274
|
* `claudeFlow.hooks.locked: true` (legacy alias) in their settings.json —
|
|
275
275
|
* a sentinel that suppresses drift surfacing entirely.
|
|
276
276
|
*
|
|
277
|
-
* The `claudeFlow.*` settings tree
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
* tell them to set it.
|
|
277
|
+
* The `claudeFlow.*` settings tree was migrated to `moflo.*` in #1209: every
|
|
278
|
+
* writer now emits `moflo.*` and the upgrade merge folds an existing
|
|
279
|
+
* `claudeFlow.*` tree into `moflo.*`. This reader keeps the `claudeFlow.hooks.locked`
|
|
280
|
+
* fallback for one deprecation cycle so a consumer who set the escape hatch but
|
|
281
|
+
* hasn't re-run init yet stays covered.
|
|
283
282
|
*/
|
|
284
283
|
export function isHookBlockLocked(settings) {
|
|
285
284
|
const root = settings;
|