@claude-flow/cli 3.57.1 → 3.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/helpers/.helpers-version +1 -1
- package/.claude/helpers/helpers.manifest.json +3 -3
- package/.claude/helpers/intelligence.cjs +3 -5
- package/README.md +1 -0
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/agent.js +4 -1
- package/dist/src/commands/hooks.js +20 -1
- package/dist/src/commands/mcp.js +66 -6
- package/dist/src/commands/memory.js +78 -3
- package/dist/src/commands/workflow.js +55 -15
- package/dist/src/init/executor.js +9 -5
- package/dist/src/init/helper-companions.d.ts +1 -1
- package/dist/src/init/helper-companions.js +4 -3
- package/dist/src/mcp-server.d.ts +5 -0
- package/dist/src/mcp-server.js +39 -0
- package/dist/src/mcp-tools/agent-tools.js +28 -0
- package/dist/src/mcp-tools/claims-tools.js +25 -15
- package/dist/src/mcp-tools/hooks-tools.js +9 -3
- package/dist/src/mcp-tools/memory-tools.js +58 -9
- package/dist/src/mcp-tools/workflow-tools.js +2 -1
- package/dist/src/memory/graph-edge-writer.d.ts +16 -4
- package/dist/src/memory/graph-edge-writer.js +19 -7
- package/dist/src/memory/intelligence.js +4 -0
- package/dist/src/memory/memory-initializer.js +55 -9
- package/dist/src/memory/memory-peek.d.ts +28 -0
- package/dist/src/memory/memory-peek.js +222 -0
- package/dist/src/memory/model-cache-dir.d.ts +11 -0
- package/dist/src/memory/model-cache-dir.js +17 -0
- package/dist/src/ruvector/task-embedder.js +38 -4
- package/dist/src/services/memory-backup.d.ts +6 -0
- package/dist/src/services/memory-backup.js +109 -81
- package/dist/src/services/policy-ledger-anchor.d.ts +2 -0
- package/dist/src/services/policy-ledger-anchor.js +4 -4
- package/dist/src/services/policy-runtime.js +54 -3
- package/dist/src/services/worker-daemon.js +4 -0
- package/node_modules/@claude-flow/codex/README.md +87 -9
- package/node_modules/@claude-flow/codex/dist/activity/cli.d.ts +3 -0
- package/node_modules/@claude-flow/codex/dist/activity/cli.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/cli.js +115 -0
- package/node_modules/@claude-flow/codex/dist/activity/cli.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.d.ts +14 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.js +142 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.d.ts +21 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.js +179 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.d.ts +20 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.js +106 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.d.ts +4 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.js +27 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.d.ts +31 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.js +5 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.d.ts +21 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.js +168 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/cli.js +3 -0
- package/node_modules/@claude-flow/codex/dist/cli.js.map +1 -1
- package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js +3 -3
- package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/server.d.ts.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/server.js +5 -3
- package/node_modules/@claude-flow/mcp/dist/server.js.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/types.d.ts +11 -0
- package/node_modules/@claude-flow/mcp/dist/types.d.ts.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/types.js.map +1 -1
- package/package.json +5 -4
- package/plugins/ruflo-console/dist/tui.mjs +159 -159
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import * as fs from 'fs';
|
|
16
16
|
import * as path from 'path';
|
|
17
|
-
import { readFileMaybeEncrypted } from '../fs-secure.js';
|
|
17
|
+
import { existingFileIsEncrypted, readFileMaybeEncrypted } from '../fs-secure.js';
|
|
18
18
|
import { loadBetterSqlite3 } from '../memory/shared-sqlite.js';
|
|
19
19
|
import { resolveMemoryRoot } from '../memory/memory-root.js';
|
|
20
20
|
export function defaultMemoryDbPath(cwd = process.cwd()) {
|
|
@@ -37,12 +37,18 @@ export async function backupProjectMemoryDbs(cwd, opts = {}) {
|
|
|
37
37
|
}
|
|
38
38
|
const successful = backups.filter(result => result.backedUp);
|
|
39
39
|
const failed = backups.find(result => !result.backedUp && result.skipped !== 'no-db');
|
|
40
|
+
// Only report an aggregate `method` when every successful store agrees — a
|
|
41
|
+
// mixed native/raw-copy result must not collapse to whichever store
|
|
42
|
+
// happened to be first, the exact false-confidence #4287 is about.
|
|
43
|
+
const methods = new Set(successful.map(result => result.method));
|
|
44
|
+
const aggregateMethod = methods.size === 1 ? successful[0]?.method : undefined;
|
|
40
45
|
return {
|
|
41
46
|
...successful[0],
|
|
42
47
|
backedUp: successful.length > 0 && !failed,
|
|
43
48
|
sizeBytes: successful.reduce((sum, result) => sum + (result.sizeBytes ?? 0), 0),
|
|
44
49
|
rotatedAway: successful.flatMap(result => result.rotatedAway ?? []),
|
|
45
50
|
skipped: failed?.skipped ?? (successful.length === 0 ? 'no-db' : undefined),
|
|
51
|
+
method: aggregateMethod,
|
|
46
52
|
backups,
|
|
47
53
|
};
|
|
48
54
|
}
|
|
@@ -63,88 +69,25 @@ function isSnapshotFor(file, dbPath) {
|
|
|
63
69
|
return file.startsWith(prefix)
|
|
64
70
|
&& /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z\.db$/.test(file.slice(prefix.length));
|
|
65
71
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
/**
|
|
73
|
+
* A plain file copy of a live WAL-mode SQLite database is not a consistent
|
|
74
|
+
* snapshot — committed data can live only in the `-wal` sidecar until it's
|
|
75
|
+
* checkpointed back into the main file. We can't open an RFE1-encrypted
|
|
76
|
+
* source natively to run a real `wal_checkpoint` probe (it isn't a SQLite
|
|
77
|
+
* image), so for the raw-copy-fallback path the only signal available is
|
|
78
|
+
* sidecar presence: a non-empty `-wal` file means there are frames that a
|
|
79
|
+
* copy of the main file alone would silently drop. Fail closed on that.
|
|
80
|
+
*/
|
|
81
|
+
function hasActiveWal(dbPath) {
|
|
71
82
|
try {
|
|
72
|
-
|
|
73
|
-
Database = (await import(mod)).default;
|
|
83
|
+
return fs.statSync(`${dbPath}-wal`).size > 0;
|
|
74
84
|
}
|
|
75
85
|
catch {
|
|
76
|
-
return
|
|
86
|
+
return false;
|
|
77
87
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
catch { /* */ }
|
|
83
|
-
const destPath = path.join(destDir, `${snapshotPrefix(dbPath)}${fileStamp(opts.timestamp ?? Date.now())}.db`);
|
|
84
|
-
// WAL-safe online backup: read-only source, consistent snapshot to destPath.
|
|
85
|
-
let db;
|
|
86
|
-
try {
|
|
87
|
-
db = new Database(dbPath, { readonly: true });
|
|
88
|
-
await db.backup(destPath);
|
|
89
|
-
db.close();
|
|
90
|
-
}
|
|
91
|
-
catch (e) {
|
|
92
|
-
try {
|
|
93
|
-
db?.close();
|
|
94
|
-
}
|
|
95
|
-
catch { /* */ }
|
|
96
|
-
// #2798 — under CLAUDE_FLOW_ENCRYPT_AT_REST the source is an RFE1
|
|
97
|
-
// blob, not native SQLite, so better-sqlite3's online-backup API
|
|
98
|
-
// throws "file is not a database" and the run was silently recorded
|
|
99
|
-
// as skipped (users with encryption had ZERO backups behind a green
|
|
100
|
-
// status). The online-backup API only exists to avoid tearing a live
|
|
101
|
-
// WAL-mode DB; that concern doesn't apply to the encrypted file (sql.js
|
|
102
|
-
// rewrites it wholesale per flush), so a plain byte copy is a valid
|
|
103
|
-
// snapshot. Fall back to it before giving up.
|
|
104
|
-
try {
|
|
105
|
-
fs.copyFileSync(dbPath, destPath);
|
|
106
|
-
const copiedBytes = fs.statSync(destPath).size;
|
|
107
|
-
if (copiedBytes > 0) {
|
|
108
|
-
// Fall through to rotation/offsite/return below via a flag path.
|
|
109
|
-
// (Mirror the success path's tail by re-using the same code.)
|
|
110
|
-
const keep = typeof opts.keep === 'number' && opts.keep > 0 ? opts.keep : 7;
|
|
111
|
-
const rotatedAway = [];
|
|
112
|
-
try {
|
|
113
|
-
const snaps = fs.readdirSync(destDir).filter(f => isSnapshotFor(f, dbPath)).sort();
|
|
114
|
-
while (snaps.length > keep) {
|
|
115
|
-
const old = snaps.shift();
|
|
116
|
-
try {
|
|
117
|
-
fs.rmSync(path.join(destDir, old), { force: true });
|
|
118
|
-
rotatedAway.push(old);
|
|
119
|
-
}
|
|
120
|
-
catch { /* */ }
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
catch { /* */ }
|
|
124
|
-
let gcsUri;
|
|
125
|
-
if (opts.gcs) {
|
|
126
|
-
try {
|
|
127
|
-
const { execFileSync } = await import('child_process');
|
|
128
|
-
const dest = opts.gcs.replace(/\/+$/, '') + '/' + path.basename(destPath);
|
|
129
|
-
execFileSync('gcloud', ['storage', 'cp', destPath, dest], { stdio: ['ignore', 'ignore', 'inherit'] });
|
|
130
|
-
gcsUri = dest;
|
|
131
|
-
}
|
|
132
|
-
catch { /* offsite failed — local snapshot stands */ }
|
|
133
|
-
}
|
|
134
|
-
if (opts.verbose) {
|
|
135
|
-
console.log(`memory DB backed up (byte-copy, encrypted-at-rest) → ${destPath} (${Math.round(copiedBytes / 1024)} KB)`);
|
|
136
|
-
}
|
|
137
|
-
return { backedUp: true, path: destPath, sizeBytes: copiedBytes, rotatedAway, gcsUri };
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
catch { /* byte-copy also failed — report the original error */ }
|
|
141
|
-
return { backedUp: false, skipped: `backup failed: ${e?.message ?? e}` };
|
|
142
|
-
}
|
|
143
|
-
let sizeBytes = 0;
|
|
144
|
-
try {
|
|
145
|
-
sizeBytes = fs.statSync(destPath).size;
|
|
146
|
-
}
|
|
147
|
-
catch { /* */ }
|
|
88
|
+
}
|
|
89
|
+
/** Shared tail: rotation + optional GCS offsite + verbose logging + the success receipt. */
|
|
90
|
+
async function finishBackup(destPath, dbPath, destDir, opts, method, sizeBytes) {
|
|
148
91
|
// Rotation — ISO-stamped names sort chronologically, so keep the tail.
|
|
149
92
|
const keep = typeof opts.keep === 'number' && opts.keep > 0 ? opts.keep : 7;
|
|
150
93
|
const rotatedAway = [];
|
|
@@ -172,11 +115,96 @@ export async function backupMemoryDb(opts = {}) {
|
|
|
172
115
|
catch { /* offsite failed — local snapshot stands */ }
|
|
173
116
|
}
|
|
174
117
|
if (opts.verbose) {
|
|
175
|
-
|
|
118
|
+
const label = method === 'raw-copy-fallback' ? ' (byte-copy, encrypted-at-rest)' : '';
|
|
119
|
+
console.log(`memory DB backed up${label} → ${destPath} (${Math.round(sizeBytes / 1024)} KB)` +
|
|
176
120
|
(rotatedAway.length ? `, rotated ${rotatedAway.length} old` : '') +
|
|
177
121
|
(gcsUri ? `, offsite ${gcsUri}` : ''));
|
|
178
122
|
}
|
|
179
|
-
return { backedUp: true, path: destPath, sizeBytes, rotatedAway, gcsUri };
|
|
123
|
+
return { backedUp: true, path: destPath, sizeBytes, rotatedAway, gcsUri, method };
|
|
124
|
+
}
|
|
125
|
+
export async function backupMemoryDb(opts = {}) {
|
|
126
|
+
const dbPath = opts.dbPath ?? defaultMemoryDbPath();
|
|
127
|
+
if (!dbPath || !fs.existsSync(dbPath))
|
|
128
|
+
return { backedUp: false, skipped: 'no-db' };
|
|
129
|
+
const destDir = opts.destDir ?? path.join(path.dirname(dbPath), 'backups');
|
|
130
|
+
try {
|
|
131
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
catch { /* */ }
|
|
134
|
+
const destPath = path.join(destDir, `${snapshotPrefix(dbPath)}${fileStamp(opts.timestamp ?? Date.now())}.db`);
|
|
135
|
+
let Database = opts.Database;
|
|
136
|
+
let nativeError = null;
|
|
137
|
+
if (!Database) {
|
|
138
|
+
try {
|
|
139
|
+
const mod = 'better-sqlite3';
|
|
140
|
+
Database = (await import(mod)).default;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
nativeError = 'better-sqlite3 unavailable';
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (!nativeError) {
|
|
147
|
+
// WAL-safe online backup: read-only source, consistent snapshot to destPath.
|
|
148
|
+
let db;
|
|
149
|
+
try {
|
|
150
|
+
db = new Database(dbPath, { readonly: true });
|
|
151
|
+
await db.backup(destPath);
|
|
152
|
+
db.close();
|
|
153
|
+
let sizeBytes = 0;
|
|
154
|
+
try {
|
|
155
|
+
sizeBytes = fs.statSync(destPath).size;
|
|
156
|
+
}
|
|
157
|
+
catch { /* */ }
|
|
158
|
+
return await finishBackup(destPath, dbPath, destDir, opts, 'native-online-backup', sizeBytes);
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
try {
|
|
162
|
+
db?.close();
|
|
163
|
+
}
|
|
164
|
+
catch { /* */ }
|
|
165
|
+
try {
|
|
166
|
+
fs.rmSync(destPath, { force: true });
|
|
167
|
+
}
|
|
168
|
+
catch { /* a native backup() that throws mid-write can leave a torn destPath */ }
|
|
169
|
+
nativeError = e?.message ?? String(e);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// #2798 — under CLAUDE_FLOW_ENCRYPT_AT_REST the source is an RFE1 blob, not
|
|
173
|
+
// native SQLite, so better-sqlite3's online-backup API throws "file is not
|
|
174
|
+
// a database" (or isn't even importable on a WASM-only host). The
|
|
175
|
+
// online-backup API exists to avoid tearing a live WAL-mode DB; that
|
|
176
|
+
// concern doesn't apply to the encrypted file (sql.js rewrites it wholesale
|
|
177
|
+
// per flush), so a plain byte copy IS a valid snapshot there.
|
|
178
|
+
//
|
|
179
|
+
// But this fallback must never fire for an ORDINARY (non-encrypted) source
|
|
180
|
+
// just because the native backup threw — SQLITE_BUSY, a disk error, or any
|
|
181
|
+
// other native-backup failure on a perfectly normal database must fail
|
|
182
|
+
// closed, not silently report `backedUp: true` off a raw copy. Positively
|
|
183
|
+
// verify encryption (4-byte RFE1 magic sniff) before ever taking this path;
|
|
184
|
+
// "the native backup threw" is never by itself evidence the source is
|
|
185
|
+
// encrypted.
|
|
186
|
+
if (existingFileIsEncrypted(dbPath) !== true) {
|
|
187
|
+
return { backedUp: false, skipped: `native backup failed: ${nativeError}` };
|
|
188
|
+
}
|
|
189
|
+
// Even for a confirmed-encrypted source, refuse the raw-copy fallback if
|
|
190
|
+
// there's an active WAL — a plain copy of the main file alone would miss
|
|
191
|
+
// uncommitted WAL frames and silently produce an inconsistent snapshot.
|
|
192
|
+
if (hasActiveWal(dbPath)) {
|
|
193
|
+
return { backedUp: false, skipped: 'active WAL present — refusing raw-copy fallback (would be inconsistent)' };
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
fs.copyFileSync(dbPath, destPath);
|
|
197
|
+
const copiedBytes = fs.statSync(destPath).size;
|
|
198
|
+
if (copiedBytes > 0) {
|
|
199
|
+
return await finishBackup(destPath, dbPath, destDir, opts, 'raw-copy-fallback', copiedBytes);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch { /* byte-copy also failed — report the original native error below */ }
|
|
203
|
+
try {
|
|
204
|
+
fs.rmSync(destPath, { force: true });
|
|
205
|
+
}
|
|
206
|
+
catch { /* */ }
|
|
207
|
+
return { backedUp: false, skipped: `native backup failed: ${nativeError}` };
|
|
180
208
|
}
|
|
181
209
|
/** Verify a snapshot without modifying it, including RFE1 images and WASM-only hosts. */
|
|
182
210
|
async function verifiedBackupRows(file, Database) {
|
|
@@ -32,6 +32,8 @@ export type AnchorAssessment = {
|
|
|
32
32
|
};
|
|
33
33
|
export type AnchorWriter = (file: string, value: unknown) => Promise<void>;
|
|
34
34
|
export type StreamingAnchorWriter = (file: string, value: AtomicJsonSource) => Promise<void>;
|
|
35
|
+
export declare const ANCHOR_LOG_FILE = "ledger-anchors.json";
|
|
36
|
+
export declare const ANCHOR_MIRROR_FILE = "ledger-anchor-head.json";
|
|
35
37
|
export declare const anchorLogPath: (projectRoot: string) => string;
|
|
36
38
|
/**
|
|
37
39
|
* Directory holding per-project trust material (anchor mirror, HMAC key).
|
|
@@ -33,9 +33,9 @@ import { closeSync, constants as fsConstants, fstatSync, existsSync, mkdtempSync
|
|
|
33
33
|
import { isAbsolute, join, resolve } from 'node:path';
|
|
34
34
|
import { homedir, tmpdir, userInfo } from 'node:os';
|
|
35
35
|
import { AgenticPolicyEngine, canonicalizePolicy } from '@claude-flow/security/policy/index';
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
export const anchorLogPath = (projectRoot) => join(resolve(projectRoot), '.claude-flow', 'policy',
|
|
36
|
+
export const ANCHOR_LOG_FILE = 'ledger-anchors.json';
|
|
37
|
+
export const ANCHOR_MIRROR_FILE = 'ledger-anchor-head.json';
|
|
38
|
+
export const anchorLogPath = (projectRoot) => join(resolve(projectRoot), '.claude-flow', 'policy', ANCHOR_LOG_FILE);
|
|
39
39
|
/**
|
|
40
40
|
* The home directory trust material lives under: $HOME when it is an absolute path, else the account's home. An empty or relative
|
|
41
41
|
* $HOME (an unset variable in a wrapper script, `HOME=. cmd`) would make join() yield a path relative to the working directory and
|
|
@@ -71,7 +71,7 @@ export function policyTrustRoot() {
|
|
|
71
71
|
}
|
|
72
72
|
function mirrorPath(projectRoot) {
|
|
73
73
|
const id = createHash('sha256').update(realpathSync(projectRoot)).digest('hex');
|
|
74
|
-
return join(policyTrustRoot(), id,
|
|
74
|
+
return join(policyTrustRoot(), id, ANCHOR_MIRROR_FILE);
|
|
75
75
|
}
|
|
76
76
|
// Duplicated from policy-runtime.ts's RECEIPT_ARCHIVE_FILE rather than
|
|
77
77
|
// imported, to avoid a circular dependency (policy-runtime.ts imports this
|
|
@@ -7,17 +7,18 @@ import { AgenticPolicyEngine, POLICY_STATE_VERSION, createLegacyCompatibleState,
|
|
|
7
7
|
import { isMcpCallerAuthEnabled, decodeTokenEnvelope, publicKeyFromHex, verifyInvocationToken, } from '@claude-flow/security/mcp-caller-identity';
|
|
8
8
|
import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
9
9
|
import { execFileSync } from 'node:child_process';
|
|
10
|
-
import { closeSync, constants as fsConstants, existsSync, fstatSync, fsyncSync, ftruncateSync, writeSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readlinkSync, realpathSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
10
|
+
import { closeSync, constants as fsConstants, existsSync, fstatSync, fsyncSync, ftruncateSync, writeSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, readlinkSync, realpathSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
11
11
|
import { dirname, join, resolve } from 'node:path';
|
|
12
12
|
import { hostname, userInfo } from 'node:os';
|
|
13
13
|
import { syncPolicyProjection } from '../mods/policy-projection.js';
|
|
14
14
|
import { ATOMIC_JSON_WRITER } from './policy-anchor-log.js';
|
|
15
15
|
import { captureArchiveSnapshot, verifyFullPolicyLedger } from './policy-ledger-audit.js';
|
|
16
|
-
import { assessAnchors, policyTrustRoot, recordAnchor } from './policy-ledger-anchor.js';
|
|
16
|
+
import { ANCHOR_LOG_FILE, ANCHOR_MIRROR_FILE, assessAnchors, policyTrustRoot, recordAnchor } from './policy-ledger-anchor.js';
|
|
17
17
|
const POLICY_DIR = join('.claude-flow', 'policy');
|
|
18
18
|
const POLICY_FILE = 'state.json';
|
|
19
19
|
const LOCK_FILE = 'state.lock';
|
|
20
20
|
const RECEIPT_ARCHIVE_FILE = 'receipts.ledger.jsonl';
|
|
21
|
+
const STATE_ANCHOR_FILE = 'state.anchor.json';
|
|
21
22
|
const LOCK_WAIT_MS = 5_000;
|
|
22
23
|
// #3164: without a bound, `state.receipts` grows forever and every
|
|
23
24
|
// transaction's verify/clone/serialize cost is O(all receipts ever issued) —
|
|
@@ -362,11 +363,59 @@ async function writeJsonAtomic(file, value) {
|
|
|
362
363
|
}
|
|
363
364
|
}
|
|
364
365
|
}
|
|
366
|
+
// #3164: a writer killed between creating its temp file and renaming it
|
|
367
|
+
// (SIGKILL from a caller's timeout, OOM) never reaches writeJsonAtomic's
|
|
368
|
+
// cleanup, so a full-size copy of the file stays behind forever (~250 MB each
|
|
369
|
+
// on a mature ledger). Every writer of these files holds the project's
|
|
370
|
+
// state.lock, so while the lock is held no temp file for them can belong to a
|
|
371
|
+
// write in progress. That invariant has known gaps: released versions before
|
|
372
|
+
// the dead-owner fix take over any lock older than 30 s, even a live owner's
|
|
373
|
+
// (a writer suspended mid-write, e.g. a sleeping laptop). So a temp is also
|
|
374
|
+
// kept while its writer pid is alive on this host, and until it is
|
|
375
|
+
// ORPHAN_TEMP_MIN_AGE_MS old; a skipped orphan is removed by a later call.
|
|
376
|
+
const ORPHAN_TEMP_MIN_AGE_MS = 10 * 60_000;
|
|
377
|
+
const ATOMIC_TEMP = /^(.+)\.(\d+)\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.tmp$/;
|
|
378
|
+
const PROJECT_ATOMIC_FILES = new Set([POLICY_FILE, ANCHOR_LOG_FILE]);
|
|
379
|
+
const TRUST_ATOMIC_FILES = new Set([STATE_ANCHOR_FILE, ANCHOR_MIRROR_FILE]);
|
|
380
|
+
function reapOrphanedTemps(dir, files) {
|
|
381
|
+
let names;
|
|
382
|
+
try {
|
|
383
|
+
names = readdirSync(dir);
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const now = Date.now();
|
|
389
|
+
for (const name of names) {
|
|
390
|
+
const match = ATOMIC_TEMP.exec(name);
|
|
391
|
+
if (!match || !files.has(match[1]))
|
|
392
|
+
continue;
|
|
393
|
+
const pid = Number(match[2]);
|
|
394
|
+
if (Number.isSafeInteger(pid) && pid > 0 && isPidAlive(pid))
|
|
395
|
+
continue;
|
|
396
|
+
const file = join(dir, name);
|
|
397
|
+
try {
|
|
398
|
+
const stat = lstatSync(file);
|
|
399
|
+
if (!stat.isFile() || now - stat.mtimeMs < ORPHAN_TEMP_MIN_AGE_MS)
|
|
400
|
+
continue;
|
|
401
|
+
unlinkSync(file);
|
|
402
|
+
}
|
|
403
|
+
catch { /* already gone, or not removable: never fail the caller */ }
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/** Call only while holding the project's state.lock. */
|
|
407
|
+
function reapOrphanedPolicyTemps(projectRoot) {
|
|
408
|
+
reapOrphanedTemps(paths(projectRoot).dir, PROJECT_ATOMIC_FILES);
|
|
409
|
+
try {
|
|
410
|
+
reapOrphanedTemps(dirname(trustPaths(projectRoot).anchor), TRUST_ATOMIC_FILES);
|
|
411
|
+
}
|
|
412
|
+
catch { /* no trust dir yet */ }
|
|
413
|
+
}
|
|
365
414
|
function trustPaths(projectRoot) {
|
|
366
415
|
const trustRoot = policyTrustRoot();
|
|
367
416
|
const projectId = createHash('sha256').update(realpathSync(projectRoot)).digest('hex');
|
|
368
417
|
const dir = join(trustRoot, projectId);
|
|
369
|
-
return { key: join(dir, 'anchor.key'), anchor: join(dir,
|
|
418
|
+
return { key: join(dir, 'anchor.key'), anchor: join(dir, STATE_ANCHOR_FILE) };
|
|
370
419
|
}
|
|
371
420
|
function trustKey(projectRoot, create) {
|
|
372
421
|
const { key } = trustPaths(projectRoot);
|
|
@@ -531,6 +580,7 @@ export async function withPolicyTransaction(projectRoot, operation, options = {}
|
|
|
531
580
|
mkdirDurably(target.dir);
|
|
532
581
|
const release = await acquireLock(target.lock);
|
|
533
582
|
try {
|
|
583
|
+
reapOrphanedPolicyTemps(projectRoot);
|
|
534
584
|
const loaded = loadPolicyState(projectRoot);
|
|
535
585
|
const engine = AgenticPolicyEngine.fromState(loaded, {
|
|
536
586
|
signingKey: process.env.CLAUDE_FLOW_POLICY_SIGNING_KEY,
|
|
@@ -691,6 +741,7 @@ export async function verifyPolicyLedger(projectRoot = process.cwd(), options =
|
|
|
691
741
|
// strings, receipt arrays or maps. The descriptor pins path replacements.
|
|
692
742
|
const release = await acquireLock(target.lock);
|
|
693
743
|
try {
|
|
744
|
+
reapOrphanedPolicyTemps(projectRoot);
|
|
694
745
|
const engine = AgenticPolicyEngine.fromState(loadPolicyState(projectRoot), engineOptions);
|
|
695
746
|
const outcome = reconcileLedger(projectRoot, engine, options);
|
|
696
747
|
if (outcome.persist) {
|
|
@@ -1699,6 +1699,10 @@ export class WorkerDaemon extends EventEmitter {
|
|
|
1699
1699
|
rotatedAway: r.rotatedAway?.length ?? 0,
|
|
1700
1700
|
gcsUri: r.gcsUri,
|
|
1701
1701
|
skipped: r.skipped,
|
|
1702
|
+
// Which method actually completed (per-store detail is in `backups`);
|
|
1703
|
+
// migration tooling should require 'native-online-backup', not a
|
|
1704
|
+
// blanket `backedUp: true` — see #4287.
|
|
1705
|
+
method: r.method,
|
|
1702
1706
|
};
|
|
1703
1707
|
}
|
|
1704
1708
|
catch (error) {
|
|
@@ -639,19 +639,19 @@ The `@claude-flow/codex` package now includes built-in dual-mode orchestration:
|
|
|
639
639
|
|
|
640
640
|
```bash
|
|
641
641
|
# List available collaboration templates
|
|
642
|
-
npx claude-flow
|
|
642
|
+
npx @claude-flow/codex dual templates
|
|
643
643
|
|
|
644
644
|
# Run a feature development swarm
|
|
645
|
-
npx claude-flow
|
|
645
|
+
npx @claude-flow/codex dual run --template feature --task "Add user authentication"
|
|
646
646
|
|
|
647
647
|
# Run a security audit swarm
|
|
648
|
-
npx claude-flow
|
|
648
|
+
npx @claude-flow/codex dual run --template security --task "src/auth/"
|
|
649
649
|
|
|
650
650
|
# Run a refactoring swarm
|
|
651
|
-
npx claude-flow
|
|
651
|
+
npx @claude-flow/codex dual run --template refactor --task "src/legacy/"
|
|
652
652
|
|
|
653
653
|
# Check collaboration status
|
|
654
|
-
npx claude-flow
|
|
654
|
+
npx @claude-flow/codex dual status
|
|
655
655
|
```
|
|
656
656
|
|
|
657
657
|
### Codex Loop Runner
|
|
@@ -660,15 +660,15 @@ Codex does not expose Claude Code's `ScheduleWakeup`, so `@claude-flow/codex` pr
|
|
|
660
660
|
|
|
661
661
|
```bash
|
|
662
662
|
# Run Codex repeatedly until it creates .codex/loop/default.complete or reaches 10 iterations
|
|
663
|
-
npx claude-flow
|
|
663
|
+
npx @claude-flow/codex loop run "Fix failing tests and create the completion marker when done"
|
|
664
664
|
|
|
665
665
|
# Use command mode for recurring Ruflo workers or custom scripts
|
|
666
|
-
npx claude-flow
|
|
666
|
+
npx @claude-flow/codex loop run --name testgaps --interval 270 --max-iterations 0 \
|
|
667
667
|
--command "npx claude-flow hooks worker dispatch --trigger testgaps"
|
|
668
668
|
|
|
669
669
|
# Inspect or stop a loop from another terminal
|
|
670
|
-
npx claude-flow
|
|
671
|
-
npx claude-flow
|
|
670
|
+
npx @claude-flow/codex loop status --name testgaps
|
|
671
|
+
npx @claude-flow/codex loop stop --name testgaps
|
|
672
672
|
```
|
|
673
673
|
|
|
674
674
|
Loop state is stored in `.codex/loop/<name>.json`; `loop stop` writes `.codex/loop/<name>.stop`, which the runner observes between iterations.
|
|
@@ -1042,6 +1042,84 @@ npx claude-flow memory optimize --rebuild-index
|
|
|
1042
1042
|
|
|
1043
1043
|
---
|
|
1044
1044
|
|
|
1045
|
+
## Inspect an existing Codex session
|
|
1046
|
+
|
|
1047
|
+
The `activity` command lets you pick a helper and read its assignment, recorded
|
|
1048
|
+
state, public progress messages, and tool calls/results. It reads local rollout
|
|
1049
|
+
files without starting model sessions or sending commands to agents.
|
|
1050
|
+
|
|
1051
|
+
From a checkout containing this change:
|
|
1052
|
+
|
|
1053
|
+
```bash
|
|
1054
|
+
npm install --prefix v3/@claude-flow/codex --workspaces=false --ignore-scripts --legacy-peer-deps --package-lock=false
|
|
1055
|
+
npm run build --prefix v3/@claude-flow/codex
|
|
1056
|
+
node v3/@claude-flow/codex/dist/cli.js activity \
|
|
1057
|
+
--session-file /path/to/sessions/parent-rollout.jsonl \
|
|
1058
|
+
--sessions-dir /path/to/sessions
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
Choose the parent rollout explicitly. Only helpers whose recorded parent chain
|
|
1062
|
+
leads back to that session appear. The selected directory must contain both the
|
|
1063
|
+
parent and helper files; use the smallest directory that contains them all.
|
|
1064
|
+
|
|
1065
|
+
Use **j/k** or the arrow keys to select a helper, then **Enter** to open it.
|
|
1066
|
+
In the detail view, **j/k** scroll, **Space** moves down a page, **g** goes to the
|
|
1067
|
+
top, **G** follows the latest activity, and **b** or **Escape** returns to the list.
|
|
1068
|
+
Press **q** to quit; the Codex session keeps running.
|
|
1069
|
+
|
|
1070
|
+
Use `--agent <thread-id>` to open a specific helper, `--once` to print a snapshot
|
|
1071
|
+
without an interactive terminal, or `--interval <seconds>` to change the refresh
|
|
1072
|
+
interval (default: 2 seconds; range: 0.2–60).
|
|
1073
|
+
|
|
1074
|
+
These screenshots use made-up session records:
|
|
1075
|
+
|
|
1076
|
+

|
|
1077
|
+
|
|
1078
|
+

|
|
1079
|
+
|
|
1080
|
+
### What the viewer can read
|
|
1081
|
+
|
|
1082
|
+
The parser supports CLI rollout files with a `session_meta` header, helper ancestry
|
|
1083
|
+
in `source.subagent.thread_spawn.parent_thread_id`, and an own-thread marker
|
|
1084
|
+
(`payload.thread_id`) before helper activity. Public message, function/custom tool
|
|
1085
|
+
call and result records, and supported task/item events are shown. Formats without
|
|
1086
|
+
these attribution fields may produce an empty list or unavailable activity.
|
|
1087
|
+
|
|
1088
|
+
Copied parent history, reasoning, and system/developer messages are excluded.
|
|
1089
|
+
Ambiguous identities and paths through symlinks are rejected. The viewer strips
|
|
1090
|
+
terminal controls and masks common credential patterns before shortening previews.
|
|
1091
|
+
Masking cannot catch every secret in arbitrary command output: check the visible
|
|
1092
|
+
content before sharing a screenshot.
|
|
1093
|
+
|
|
1094
|
+
State is the last **recorded** state, not a heartbeat. Discovery stops after 4,000
|
|
1095
|
+
entries or eight directory levels; ancestry is limited to 64 links. Each detail
|
|
1096
|
+
refresh reads at most 1 MiB, lines are limited to 64 KiB, previews to 2,400 characters,
|
|
1097
|
+
and history to 80 events. The metadata header must include its terminating newline
|
|
1098
|
+
within the first 64 KiB; an oversized or incomplete header excludes that file.
|
|
1099
|
+
The screen labels incomplete scans, omitted records and logs that are still being
|
|
1100
|
+
read. Missing data stays unavailable.
|
|
1101
|
+
|
|
1102
|
+
### Check the viewer locally
|
|
1103
|
+
|
|
1104
|
+
```bash
|
|
1105
|
+
cd v3/@claude-flow/codex
|
|
1106
|
+
npm test -- --run tests/activity-files.test.ts tests/activity-parser.test.ts tests/activity-viewer.test.ts
|
|
1107
|
+
cd ../../..
|
|
1108
|
+
node scripts/smoke-codex-activity.mjs
|
|
1109
|
+
```
|
|
1110
|
+
|
|
1111
|
+
The Node smoke check runs the built CLI against synthetic files and checks the
|
|
1112
|
+
picker, detail output, history isolation, invalid input and read-only access.
|
|
1113
|
+
The screenshots above were captured from the interactive viewer using synthetic
|
|
1114
|
+
records; screenshot generation is not part of CI.
|
|
1115
|
+
|
|
1116
|
+
### Release integration
|
|
1117
|
+
|
|
1118
|
+
Ship the viewer in `@claude-flow/codex` with the next coordinated Ruflo release.
|
|
1119
|
+
At release, bump the package version and exported `VERSION` together, update the
|
|
1120
|
+
bundled dependency pins and lockfile, and include the built package in the release
|
|
1121
|
+
train.
|
|
1122
|
+
|
|
1045
1123
|
## Related Packages
|
|
1046
1124
|
|
|
1047
1125
|
| Package | Description |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/activity/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAO1D,wBAAgB,qBAAqB,IAAI,OAAO,CA6B/C"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Command, InvalidArgumentError } from 'commander';
|
|
2
|
+
import { realpath } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { emitKeypressEvents } from 'node:readline';
|
|
5
|
+
import { ActivityViewer } from './viewer.js';
|
|
6
|
+
import { sanitize } from './sanitize.js';
|
|
7
|
+
export function createActivityCommand() {
|
|
8
|
+
return new Command('activity')
|
|
9
|
+
.description('Inspect helpers from an existing Codex session (read-only)')
|
|
10
|
+
.requiredOption('--session-file <file>', 'Parent CLI rollout JSONL file')
|
|
11
|
+
.requiredOption('--sessions-dir <directory>', 'Directory containing parent and helper rollouts')
|
|
12
|
+
.option('--agent <id>', 'Open a particular helper thread ID')
|
|
13
|
+
.option('--once', 'Print one bounded snapshot and exit (no interactive terminal needed)')
|
|
14
|
+
.option('--interval <seconds>', 'Refresh interval, 0.2 to 60 seconds', interval, 2)
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
try {
|
|
17
|
+
const root = await realpath(path.resolve(options.sessionsDir));
|
|
18
|
+
// Canonicalize the directory only. A selected file through a symlink is rejected.
|
|
19
|
+
const file = path.resolve(root, path.relative(path.resolve(options.sessionsDir), path.resolve(options.sessionFile)));
|
|
20
|
+
const viewer = new ActivityViewer(root, file);
|
|
21
|
+
await viewer.refresh();
|
|
22
|
+
if (viewer.list.error)
|
|
23
|
+
throw new Error(viewer.list.error);
|
|
24
|
+
if (options.agent) {
|
|
25
|
+
if (!viewer.list.helpers.some(item => item.id === options.agent))
|
|
26
|
+
throw new Error('Agent is not an attributable helper of this session.');
|
|
27
|
+
viewer.selected = options.agent;
|
|
28
|
+
viewer.detail = true;
|
|
29
|
+
await viewer.refresh();
|
|
30
|
+
}
|
|
31
|
+
if (options.once)
|
|
32
|
+
console.log(viewer.render(100, 200));
|
|
33
|
+
else
|
|
34
|
+
await runTerminal(viewer, options.interval * 1000);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error(`Activity viewer: ${sanitize(error instanceof Error ? error.message : 'Unable to read session.', 300)}`);
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function interval(value) {
|
|
43
|
+
const parsed = Number(value);
|
|
44
|
+
if (!Number.isFinite(parsed) || parsed < 0.2 || parsed > 60)
|
|
45
|
+
throw new InvalidArgumentError('Choose 0.2 to 60 seconds.');
|
|
46
|
+
return parsed;
|
|
47
|
+
}
|
|
48
|
+
async function runTerminal(viewer, intervalMs) {
|
|
49
|
+
const input = process.stdin;
|
|
50
|
+
const output = process.stdout;
|
|
51
|
+
if (!input.isTTY || !output.isTTY)
|
|
52
|
+
throw new Error('Use --once when input or output is not a terminal.');
|
|
53
|
+
const wasRaw = input.isRaw;
|
|
54
|
+
let stopped = false;
|
|
55
|
+
let busy = false;
|
|
56
|
+
let finish = () => { };
|
|
57
|
+
const done = new Promise(resolve => { finish = resolve; });
|
|
58
|
+
const draw = () => {
|
|
59
|
+
if (!stopped)
|
|
60
|
+
output.write(`\x1b[H\x1b[2J${viewer.render(output.columns || 80, output.rows || 24)}`);
|
|
61
|
+
};
|
|
62
|
+
const close = () => { stopped = true; finish(); };
|
|
63
|
+
const update = async () => {
|
|
64
|
+
if (busy || stopped)
|
|
65
|
+
return;
|
|
66
|
+
busy = true;
|
|
67
|
+
try {
|
|
68
|
+
await viewer.refresh();
|
|
69
|
+
draw();
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
close();
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
busy = false;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const keypress = (text, key) => {
|
|
80
|
+
if (key?.name === 'q' || key?.ctrl && key.name === 'c') {
|
|
81
|
+
close();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
viewer.key(text === 'G' ? 'G' : key?.name ?? text);
|
|
85
|
+
draw();
|
|
86
|
+
if (key?.name === 'return')
|
|
87
|
+
void update();
|
|
88
|
+
};
|
|
89
|
+
emitKeypressEvents(input);
|
|
90
|
+
const timer = setInterval(() => { void update(); }, intervalMs);
|
|
91
|
+
try {
|
|
92
|
+
input.setRawMode(true);
|
|
93
|
+
input.resume();
|
|
94
|
+
input.on('keypress', keypress);
|
|
95
|
+
output.on('resize', draw);
|
|
96
|
+
process.on('SIGINT', close);
|
|
97
|
+
process.on('SIGTERM', close);
|
|
98
|
+
output.write('\x1b[?1049h\x1b[?25l');
|
|
99
|
+
draw();
|
|
100
|
+
await done;
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
stopped = true;
|
|
104
|
+
clearInterval(timer);
|
|
105
|
+
input.off('keypress', keypress);
|
|
106
|
+
output.off('resize', draw);
|
|
107
|
+
process.off('SIGINT', close);
|
|
108
|
+
process.off('SIGTERM', close);
|
|
109
|
+
input.setRawMode(wasRaw);
|
|
110
|
+
// This command owns stdin. A resumed stream would keep Node alive after q.
|
|
111
|
+
input.pause();
|
|
112
|
+
output.write('\x1b[?25h\x1b[?1049l');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/activity/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FileHandle } from 'node:fs/promises';
|
|
2
|
+
import { type SessionIdentity } from './types.js';
|
|
3
|
+
export declare const MAX_LINE: number;
|
|
4
|
+
/** Only regular files whose resolved path stays inside the selected directory. */
|
|
5
|
+
export declare function openSession(root: string, file: string): Promise<FileHandle>;
|
|
6
|
+
export declare function readIdentity(root: string, file: string): Promise<SessionIdentity | null>;
|
|
7
|
+
export interface SessionList {
|
|
8
|
+
root: SessionIdentity | null;
|
|
9
|
+
helpers: SessionIdentity[];
|
|
10
|
+
partial: boolean;
|
|
11
|
+
error: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function listSessions(root: string, rootFile: string, expectedId?: string): Promise<SessionList>;
|
|
14
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/activity/files.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAU,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAG1D,eAAO,MAAM,QAAQ,QAAY,CAAC;AAGlC,kFAAkF;AAClF,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAmBjF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAyB9F;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAsD5G"}
|