@aiwg/cli 2026.8.26 → 2026.8.28
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/aiwg.mjs +6 -0
- package/dist/src/a2a/client.js +4 -1
- package/dist/src/a2a/codecs.js +5 -2
- package/dist/src/a2a/protocol.js +12 -1
- package/dist/src/activity-log/cli.js +4 -1
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/backends/graphology-backend.js +11 -2
- package/dist/src/artifacts/backends/json-backend.js +12 -2
- package/dist/src/artifacts/backends/sqlite-backend.js +20 -7
- package/dist/src/artifacts/fortemi-core-sync.js +37 -0
- package/dist/src/artifacts/graph-backend.js +16 -0
- package/dist/src/audit/operator-decision.js +9 -25
- package/dist/src/features/catalog.js +3 -2
- package/dist/src/governance/boundary.js +354 -0
- package/dist/src/governance/classification.js +191 -0
- package/dist/src/governance/index.js +5 -0
- package/dist/src/governance/redaction.js +324 -0
- package/dist/src/governance/retention.js +274 -0
- package/dist/src/jobs/executor.js +2 -3
- package/dist/src/ops/cli.js +95 -0
- package/dist/src/serve/dispatch-router.js +1 -1
- package/dist/src/sessions/repository.js +8 -6
- package/dist/src/storage/backends/postgres.js +6 -1
- package/dist/src/storage/index.js +1 -1
- package/dist/src/storage/migration-protocol.js +228 -50
- package/dist/src/storage/qualification.js +39 -5
- package/package.json +1 -1
package/bin/aiwg.mjs
CHANGED
|
@@ -407,6 +407,12 @@ async function main() {
|
|
|
407
407
|
// commands remain reachable so an operator can explicitly adopt or switch.
|
|
408
408
|
if (args[0] !== 'installation') {
|
|
409
409
|
const identityPath = path.join(activePackageRoot, 'dist', 'src', 'installation', 'manager.mjs');
|
|
410
|
+
if (activePackageRoot !== packageRoot && !existsSync(identityPath)) {
|
|
411
|
+
console.error(`Dev mode: compiled installation manager not found at ${identityPath}`);
|
|
412
|
+
console.error(` Run: (cd ${activePackageRoot} && npm run build:cli)`);
|
|
413
|
+
console.error(` Or switch back: aiwg --use-stable`);
|
|
414
|
+
process.exit(1);
|
|
415
|
+
}
|
|
410
416
|
const { assertCanonicalInstallation } = await import(pathToFileURL(identityPath).href);
|
|
411
417
|
assertCanonicalInstallation({ actualRoot: activePackageRoot });
|
|
412
418
|
}
|
package/dist/src/a2a/client.js
CHANGED
|
@@ -102,7 +102,10 @@ export class A2AClient {
|
|
|
102
102
|
}
|
|
103
103
|
operationPath(v1Path, legacyPath) {
|
|
104
104
|
if (this.selectedInterface) {
|
|
105
|
-
|
|
105
|
+
// An advertised interface URL is already the operation base. Appending
|
|
106
|
+
// the legacy route prefix again turns cards ending in `/v1` into
|
|
107
|
+
// `/v1/v1/...` and breaks otherwise valid negotiated 0.3 calls.
|
|
108
|
+
return this.protocolVersion === '1.0' ? `/${v1Path}` : `/${legacyPath}`;
|
|
106
109
|
}
|
|
107
110
|
return `${this.agentPath()}/${legacyPath}`;
|
|
108
111
|
}
|
package/dist/src/a2a/codecs.js
CHANGED
|
@@ -24,8 +24,11 @@ export function decodePushNotificationConfig(version, input, path = '$') {
|
|
|
24
24
|
const result = {
|
|
25
25
|
url: stringAt(version, `${path}.url`, obj.url),
|
|
26
26
|
};
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
// The deployed 0.3 sandbox predates the stable field rename and returns
|
|
28
|
+
// `id`; accept that compatibility spelling while continuing to encode the
|
|
29
|
+
// documented 0.3 `configId` shape.
|
|
30
|
+
const id = version === '1.0' ? obj.id : (obj.configId ?? obj.id);
|
|
31
|
+
assignOptionalString(version, result, 'configId', id, `${path}.${version === '1.0' || obj.configId === undefined ? 'id' : 'configId'}`);
|
|
29
32
|
assignOptionalString(version, result, 'token', obj.token, `${path}.token`);
|
|
30
33
|
if (version === '0.3') {
|
|
31
34
|
assignOptionalString(version, result, 'secret', obj.secret, `${path}.secret`);
|
package/dist/src/a2a/protocol.js
CHANGED
|
@@ -38,7 +38,7 @@ export function normalizeAgentCard(card) {
|
|
|
38
38
|
if (!entry || typeof entry !== 'object' || typeof entry.url !== 'string') {
|
|
39
39
|
throw new A2ANegotiationError('agent_card.interface_invalid', `supportedInterfaces[${preference}] must contain an absolute URL`);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
assertAbsoluteInterfaceUrl(entry.url, `supportedInterfaces[${preference}].url`);
|
|
42
42
|
const interfaceVersion = normalizeProtocolVersion(entry.protocolVersion);
|
|
43
43
|
if (entry.protocolVersion !== undefined && !interfaceVersion) {
|
|
44
44
|
throw new A2ANegotiationError('agent_card.interface_version_invalid', `supportedInterfaces[${preference}].protocolVersion is unsupported`);
|
|
@@ -133,4 +133,15 @@ function assertAbsoluteUrl(value, field) {
|
|
|
133
133
|
throw new A2ANegotiationError('agent_card.url_invalid', `${field} must be an absolute HTTP(S) URL`);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
function assertAbsoluteInterfaceUrl(value, field) {
|
|
137
|
+
try {
|
|
138
|
+
const url = new URL(value);
|
|
139
|
+
if (!['http:', 'https:', 'ws:', 'wss:'].includes(url.protocol)) {
|
|
140
|
+
throw new Error('unsupported interface scheme');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
throw new A2ANegotiationError('agent_card.url_invalid', `${field} must be an absolute HTTP(S) or WS(S) URL`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
136
147
|
//# sourceMappingURL=protocol.js.map
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import { ACTIVITY_OPERATIONS, formatEntry, formatUtcTimestamp, isActivityOperation, } from './types.js';
|
|
19
19
|
import { parseLog, parseUtcDate } from './parser.js';
|
|
20
20
|
import { resolveStorage } from '../storage/index.js';
|
|
21
|
+
import { redactText } from '../governance/redaction.js';
|
|
21
22
|
const LOG_PATH = 'activity.log';
|
|
22
23
|
const DEFAULT_LIMIT = 20;
|
|
23
24
|
export async function main(args) {
|
|
@@ -74,7 +75,9 @@ async function handleAppend(args) {
|
|
|
74
75
|
` Valid operations: ${ACTIVITY_OPERATIONS.join(', ')}`);
|
|
75
76
|
}
|
|
76
77
|
const op = args[0];
|
|
77
|
-
const summary = args.slice(1).join(' ').trim()
|
|
78
|
+
const summary = redactText(args.slice(1).join(' ').trim(), {
|
|
79
|
+
limits: { maxInputBytes: 16 * 1024 },
|
|
80
|
+
}).text;
|
|
78
81
|
if (!isActivityOperation(op)) {
|
|
79
82
|
throw new Error(`Invalid operation "${op}". Valid operations: ${ACTIVITY_OPERATIONS.join(', ')}`);
|
|
80
83
|
}
|
package/dist/src/api/index.d.ts
CHANGED
|
@@ -18,5 +18,6 @@ export * from '../providers/transformation-receipt-integration.js';
|
|
|
18
18
|
export * from '../marketplace/artifact-attestation.js';
|
|
19
19
|
export * from '../uhp/index.js';
|
|
20
20
|
export * from '../mission-protocol/index.js';
|
|
21
|
+
export * from '../governance/index.js';
|
|
21
22
|
export { ARTIFACT_TRUST_ROOT_MEDIA_TYPE, ARTIFACT_TRUST_ROOT_SCHEMA_VERSION, ARTIFACT_TRUST_STATE_SCHEMA_VERSION, bootstrapTrustRoot, parseTrustRoot, parseTrustState, readTrustState, validateTrustRoot, validateTrustState, verifyRootTransition, writeTrustState, channelStateKey, canonicalJson, dssePae, publicKeyFingerprint, sha256, type ArtifactTrustRoot, type ArtifactTrustState, type RootBootstrapResult, type RootTransitionResult, type ArtifactTrustPolicySettings, type TrustedChannelState, } from '../security/artifact-trust.js';
|
|
22
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/api/index.js
CHANGED
|
@@ -18,5 +18,6 @@ export * from '../providers/transformation-receipt-integration.js';
|
|
|
18
18
|
export * from '../marketplace/artifact-attestation.js';
|
|
19
19
|
export * from '../uhp/index.js';
|
|
20
20
|
export * from '../mission-protocol/index.js';
|
|
21
|
+
export * from '../governance/index.js';
|
|
21
22
|
export { ARTIFACT_TRUST_ROOT_MEDIA_TYPE, ARTIFACT_TRUST_ROOT_SCHEMA_VERSION, ARTIFACT_TRUST_STATE_SCHEMA_VERSION, bootstrapTrustRoot, parseTrustRoot, parseTrustState, readTrustState, validateTrustRoot, validateTrustState, verifyRootTransition, writeTrustState, channelStateKey, canonicalJson, dssePae, publicKeyFingerprint, sha256, } from '../security/artifact-trust.js';
|
|
22
23
|
//# sourceMappingURL=index.js.map
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* @source @src/artifacts/graph-backend.ts
|
|
12
12
|
* @tests @test/unit/artifacts/graphology-backend.test.ts
|
|
13
13
|
*/
|
|
14
|
+
import { compareGraphIds, pageGraphIds } from '../graph-backend.js';
|
|
14
15
|
import { normalizeEdges } from '../types.js';
|
|
15
16
|
import { loadFeaturePackage } from '../../features/runtime.js';
|
|
16
17
|
/**
|
|
@@ -73,7 +74,15 @@ export class GraphologyBackend {
|
|
|
73
74
|
return this.graph.getNodeAttributes(id);
|
|
74
75
|
}
|
|
75
76
|
nodes() {
|
|
76
|
-
return this.graph.nodes();
|
|
77
|
+
return this.graph.nodes().sort(compareGraphIds);
|
|
78
|
+
}
|
|
79
|
+
queryNodes(filters) {
|
|
80
|
+
return this.graph.nodes()
|
|
81
|
+
.filter((id) => Object.entries(filters).every(([key, value]) => this.graph.getNodeAttribute(id, key) === value))
|
|
82
|
+
.sort(compareGraphIds);
|
|
83
|
+
}
|
|
84
|
+
pageNodes(limit, after) {
|
|
85
|
+
return pageGraphIds(this.graph.nodes(), limit, after);
|
|
77
86
|
}
|
|
78
87
|
// --- Traversal ---
|
|
79
88
|
neighbors(nodeId, direction, edgeType) {
|
|
@@ -98,7 +107,7 @@ export class GraphologyBackend {
|
|
|
98
107
|
// Add the "other" node
|
|
99
108
|
results.add(src === nodeId ? tgt : src);
|
|
100
109
|
}
|
|
101
|
-
return [...results];
|
|
110
|
+
return [...results].sort(compareGraphIds);
|
|
102
111
|
}
|
|
103
112
|
// --- Set operations ---
|
|
104
113
|
intersection(setA, setB) {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* @source @src/artifacts/graph-backend.ts
|
|
10
10
|
* @tests @test/unit/artifacts/graph-backend.test.ts
|
|
11
11
|
*/
|
|
12
|
+
import { compareGraphIds, pageGraphIds } from '../graph-backend.js';
|
|
12
13
|
import { normalizeEdges } from '../types.js';
|
|
13
14
|
/**
|
|
14
15
|
* JSON-backed graph using plain objects and JS Set operations.
|
|
@@ -52,7 +53,16 @@ export class JsonGraphBackend {
|
|
|
52
53
|
return this.graph.get(id)?.attrs;
|
|
53
54
|
}
|
|
54
55
|
nodes() {
|
|
55
|
-
return [...this.graph.keys()];
|
|
56
|
+
return [...this.graph.keys()].sort(compareGraphIds);
|
|
57
|
+
}
|
|
58
|
+
queryNodes(filters) {
|
|
59
|
+
return [...this.graph]
|
|
60
|
+
.filter(([, node]) => Object.entries(filters).every(([key, value]) => node.attrs[key] === value))
|
|
61
|
+
.map(([id]) => id)
|
|
62
|
+
.sort(compareGraphIds);
|
|
63
|
+
}
|
|
64
|
+
pageNodes(limit, after) {
|
|
65
|
+
return pageGraphIds([...this.graph.keys()], limit, after);
|
|
56
66
|
}
|
|
57
67
|
// --- Traversal ---
|
|
58
68
|
neighbors(nodeId, direction, edgeType) {
|
|
@@ -74,7 +84,7 @@ export class JsonGraphBackend {
|
|
|
74
84
|
}
|
|
75
85
|
}
|
|
76
86
|
}
|
|
77
|
-
return [...results];
|
|
87
|
+
return [...results].sort(compareGraphIds);
|
|
78
88
|
}
|
|
79
89
|
// --- Set operations ---
|
|
80
90
|
intersection(setA, setB) {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* @source @src/artifacts/graph-backend.ts
|
|
14
14
|
* @tests @test/unit/artifacts/sqlite-backend.test.ts
|
|
15
15
|
*/
|
|
16
|
+
import { compareGraphIds, pageGraphIds } from '../graph-backend.js';
|
|
16
17
|
import { normalizeEdges } from '../types.js';
|
|
17
18
|
import { requireFeaturePackage } from '../../features/runtime.js';
|
|
18
19
|
const SCHEMA_VERSION = 1;
|
|
@@ -131,23 +132,35 @@ export class SqliteGraphBackend {
|
|
|
131
132
|
return JSON.parse(row.attrs);
|
|
132
133
|
}
|
|
133
134
|
nodes() {
|
|
134
|
-
return this.db.prepare('SELECT id FROM nodes').all()
|
|
135
|
+
return this.db.prepare('SELECT id FROM nodes ORDER BY id COLLATE BINARY').all()
|
|
136
|
+
.map((r) => r.id);
|
|
135
137
|
}
|
|
136
138
|
queryNodes(filters) {
|
|
137
139
|
const clauses = [];
|
|
138
140
|
const values = [];
|
|
139
141
|
if (filters.type !== undefined) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
if (filters.type === null)
|
|
143
|
+
clauses.push('type IS NULL');
|
|
144
|
+
else {
|
|
145
|
+
clauses.push('type = ?');
|
|
146
|
+
values.push(filters.type);
|
|
147
|
+
}
|
|
142
148
|
}
|
|
143
149
|
if (filters.phase !== undefined) {
|
|
144
|
-
|
|
145
|
-
|
|
150
|
+
if (filters.phase === null)
|
|
151
|
+
clauses.push('phase IS NULL');
|
|
152
|
+
else {
|
|
153
|
+
clauses.push('phase = ?');
|
|
154
|
+
values.push(filters.phase);
|
|
155
|
+
}
|
|
146
156
|
}
|
|
147
157
|
const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : '';
|
|
148
|
-
return this.db.prepare(`SELECT id FROM nodes ${where} ORDER BY id`).all(...values)
|
|
158
|
+
return this.db.prepare(`SELECT id FROM nodes ${where} ORDER BY id COLLATE BINARY`).all(...values)
|
|
149
159
|
.map((row) => row.id);
|
|
150
160
|
}
|
|
161
|
+
pageNodes(limit, after) {
|
|
162
|
+
return pageGraphIds(this.nodes(), limit, after);
|
|
163
|
+
}
|
|
151
164
|
// --- Traversal ---
|
|
152
165
|
neighbors(nodeId, direction, edgeType) {
|
|
153
166
|
const results = new Set();
|
|
@@ -171,7 +184,7 @@ export class SqliteGraphBackend {
|
|
|
171
184
|
for (const row of rows)
|
|
172
185
|
results.add(row.target);
|
|
173
186
|
}
|
|
174
|
-
return [...results];
|
|
187
|
+
return [...results].sort(compareGraphIds);
|
|
175
188
|
}
|
|
176
189
|
// --- Set operations (native SQL) ---
|
|
177
190
|
intersection(setA, setB) {
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { parse as parseYaml } from "yaml";
|
|
5
6
|
import { GRAPH_CONFIGS, getProjectIndexRoot, loadGlobalGraphConfigs } from "./types.js";
|
|
6
7
|
import { buildAiwgFortemiIndexExport, } from "./browser-export.js";
|
|
7
8
|
import { loadGraphIndexFile } from "./index-reader.js";
|
|
@@ -263,4 +264,40 @@ export function getFortemiCorePrebuiltStatus(graph = "framework") {
|
|
|
263
264
|
reason: !exportExists ? "prebuilt manifest exists but export file is missing" : reason,
|
|
264
265
|
};
|
|
265
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Compare source script declarations with their compact prebuilt records.
|
|
269
|
+
* This is deliberately independent of cache freshness: a checksum-valid index
|
|
270
|
+
* can still be operationally broken when compaction drops runtime metadata.
|
|
271
|
+
*/
|
|
272
|
+
export function getFortemiCoreExecutableSkillStatus(graph = "framework", packageRoot) {
|
|
273
|
+
const root = packageRoot ?? findPackageRoot(path.dirname(fileURLToPath(import.meta.url)));
|
|
274
|
+
if (!root)
|
|
275
|
+
return { sourceExecutableCount: 0, packagedExecutableCount: 0, missing: [] };
|
|
276
|
+
const exportPath = path.join(root, "prebuilt", "fortemi-core", graph, "aiwg-fortemi-index-v2.json");
|
|
277
|
+
if (!fs.existsSync(exportPath))
|
|
278
|
+
return { sourceExecutableCount: 0, packagedExecutableCount: 0, missing: [] };
|
|
279
|
+
const exported = JSON.parse(fs.readFileSync(exportPath, "utf-8"));
|
|
280
|
+
let sourceExecutableCount = 0;
|
|
281
|
+
let packagedExecutableCount = 0;
|
|
282
|
+
const missing = [];
|
|
283
|
+
for (const item of exported.items ?? []) {
|
|
284
|
+
if (item.type !== "aiwg.skill" || typeof item.source?.path !== "string")
|
|
285
|
+
continue;
|
|
286
|
+
const sourcePath = path.join(root, item.source.path);
|
|
287
|
+
if (!fs.existsSync(sourcePath))
|
|
288
|
+
continue;
|
|
289
|
+
const match = fs.readFileSync(sourcePath, "utf-8").match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
|
|
290
|
+
if (!match)
|
|
291
|
+
continue;
|
|
292
|
+
const script = parseYaml(match[1])?.script;
|
|
293
|
+
if (!script)
|
|
294
|
+
continue;
|
|
295
|
+
sourceExecutableCount += 1;
|
|
296
|
+
if (item.search?.frontmatter?.aiwg_script)
|
|
297
|
+
packagedExecutableCount += 1;
|
|
298
|
+
else
|
|
299
|
+
missing.push(item.name ?? item.id);
|
|
300
|
+
}
|
|
301
|
+
return { sourceExecutableCount, packagedExecutableCount, missing };
|
|
302
|
+
}
|
|
266
303
|
//# sourceMappingURL=fortemi-core-sync.js.map
|
|
@@ -10,6 +10,22 @@
|
|
|
10
10
|
* @source @src/artifacts/types.ts
|
|
11
11
|
* @tests @test/unit/artifacts/graph-backend.test.ts
|
|
12
12
|
*/
|
|
13
|
+
/** SQLite BINARY collation and local backends share this UTF-8 byte order. */
|
|
14
|
+
export function compareGraphIds(left, right) {
|
|
15
|
+
return Buffer.compare(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8'));
|
|
16
|
+
}
|
|
17
|
+
export function pageGraphIds(ids, limit, after) {
|
|
18
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 10_000) {
|
|
19
|
+
throw new Error('graph page limit must be an integer from 1 through 10000');
|
|
20
|
+
}
|
|
21
|
+
const ordered = [...ids].sort(compareGraphIds);
|
|
22
|
+
const eligible = after === undefined ? ordered : ordered.filter(id => compareGraphIds(id, after) > 0);
|
|
23
|
+
const nodes = eligible.slice(0, limit);
|
|
24
|
+
return {
|
|
25
|
+
nodes,
|
|
26
|
+
...(eligible.length > limit && nodes.length ? { nextCursor: nodes[nodes.length - 1] } : {}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
13
29
|
/**
|
|
14
30
|
* Create a graph backend instance.
|
|
15
31
|
*
|
|
@@ -10,9 +10,8 @@
|
|
|
10
10
|
import { createHash, randomUUID } from 'node:crypto';
|
|
11
11
|
import { appendFile, chmod, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
12
12
|
import { dirname } from 'node:path';
|
|
13
|
+
import { redactStructured } from '../governance/redaction.js';
|
|
13
14
|
export const OPERATOR_DECISION_SCHEMA = 'operator-decision.aiwg.io/v1';
|
|
14
|
-
const secretKey = /token|secret|password|credential|authorization|cookie|csrf|api[_-]?key/i;
|
|
15
|
-
const secretValue = /(?:bearer\s+\S+|\bsk-[a-z0-9_-]+|\bgh[pousr]_[a-z0-9_]+)/i;
|
|
16
15
|
export function digestDecisionContext(context) {
|
|
17
16
|
const safe = redact(context).value;
|
|
18
17
|
return `sha256:${createHash('sha256').update(canonicalJson(safe)).digest('hex')}`;
|
|
@@ -167,28 +166,13 @@ function canonicalJson(value) {
|
|
|
167
166
|
return JSON.stringify(value);
|
|
168
167
|
}
|
|
169
168
|
function redact(value, path = '$') {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (secretKey.test(key)) {
|
|
179
|
-
output[key] = '[redacted]';
|
|
180
|
-
paths.push(`${path}.${key}`);
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
const child = redact(item, `${path}.${key}`);
|
|
184
|
-
output[key] = child.value;
|
|
185
|
-
paths.push(...child.paths);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
return { value: output, paths };
|
|
189
|
-
}
|
|
190
|
-
if (typeof value === 'string' && secretValue.test(value))
|
|
191
|
-
return { value: '[redacted]', paths: [path] };
|
|
192
|
-
return { value, paths: [] };
|
|
169
|
+
const result = redactStructured(value);
|
|
170
|
+
return {
|
|
171
|
+
value: result.value,
|
|
172
|
+
paths: result.findings.map((finding) => {
|
|
173
|
+
const suffix = finding.path?.replaceAll('/', '.').replace(/^\./, '') ?? '';
|
|
174
|
+
return suffix ? `${path}.${suffix}` : path;
|
|
175
|
+
}),
|
|
176
|
+
};
|
|
193
177
|
}
|
|
194
178
|
//# sourceMappingURL=operator-decision.js.map
|
|
@@ -32,15 +32,16 @@ export const FEATURE_CATALOG = [
|
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
name: 'sqlite',
|
|
35
|
-
description: 'SQLite
|
|
35
|
+
description: 'SQLite runtime for session catalogs and persistent storage backends',
|
|
36
36
|
packages: ['better-sqlite3'],
|
|
37
37
|
packageSpecs: { 'better-sqlite3': '12.8.0' },
|
|
38
38
|
scriptPackages: ['better-sqlite3'],
|
|
39
39
|
enables: [
|
|
40
|
+
'aiwg sessions list / discover / import-discovered / timeline / search',
|
|
40
41
|
'storage.config: backend=sqlite for any subsystem',
|
|
41
42
|
'transactional reads/writes against `.aiwg/storage/`',
|
|
42
43
|
],
|
|
43
|
-
cost: '~5 MB — native compile via node-gyp',
|
|
44
|
+
cost: '~5 MB — platform prebuild when available, otherwise a native compile via node-gyp',
|
|
44
45
|
},
|
|
45
46
|
{
|
|
46
47
|
name: 'postgres',
|