@wowok/agent-mcp 2.5.1 → 2.5.3
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/dist/index.js +46 -6
- package/dist/knowledge/guard-design-patterns.js +104 -1
- package/dist/knowledge/guard-ledger.js +3 -3
- package/dist/knowledge/guard-lint.js +3 -3
- package/dist/knowledge/guard-risk.js +23 -15
- package/dist/knowledge/guard-templates.js +174 -2
- package/dist/knowledge/guard-translation.js +3 -3
- package/dist/knowledge/index.d.ts +3 -0
- package/dist/knowledge/index.js +9 -0
- package/dist/knowledge/machine-context.d.ts +1 -1
- package/dist/knowledge/machine-context.js +21 -0
- package/dist/knowledge/machine-ledger.d.ts +1 -0
- package/dist/knowledge/machine-ledger.js +31 -11
- package/dist/knowledge/machine-templates.js +32 -78
- package/dist/knowledge/mcp-schema-audit.js +3 -2
- package/dist/knowledge/overrides-loader.d.ts +13 -0
- package/dist/knowledge/overrides-loader.js +67 -0
- package/dist/knowledge/payment-risk.d.ts +4 -0
- package/dist/knowledge/payment-risk.js +67 -0
- package/dist/knowledge/repository-confirm.d.ts +4 -0
- package/dist/knowledge/repository-confirm.js +44 -0
- package/dist/knowledge/repository-risk.d.ts +8 -2
- package/dist/knowledge/repository-risk.js +61 -0
- package/dist/knowledge/repository-translation.js +23 -0
- package/dist/knowledge/scenario-modes.js +22 -5
- package/dist/knowledge/service-templates.js +38 -0
- package/dist/knowledge/treasury-templates.js +42 -0
- package/dist/loop-engineering/context-collect.d.ts +7 -0
- package/dist/loop-engineering/context-collect.js +83 -0
- package/dist/loop-engineering/improve.d.ts +29 -0
- package/dist/loop-engineering/improve.js +178 -0
- package/dist/project/business-puzzle.d.ts +1 -0
- package/dist/project/business-puzzle.js +11 -0
- package/dist/project/context-assembly.d.ts +16 -0
- package/dist/project/context-assembly.js +130 -0
- package/dist/project/deployment-doc.d.ts +126 -0
- package/dist/project/deployment-doc.js +632 -0
- package/dist/project/index.d.ts +25 -0
- package/dist/project/index.js +78 -0
- package/dist/project/migration.d.ts +22 -0
- package/dist/project/migration.js +125 -0
- package/dist/project/project-store.d.ts +110 -0
- package/dist/project/project-store.js +425 -0
- package/dist/project/risk-aggregator.d.ts +7 -0
- package/dist/project/risk-aggregator.js +15 -0
- package/dist/project/rollback-policy.d.ts +45 -0
- package/dist/project/rollback-policy.js +290 -0
- package/dist/project/stage-gate.d.ts +49 -0
- package/dist/project/stage-gate.js +310 -0
- package/dist/schema/call/allocation.d.ts +5 -0
- package/dist/schema/call/arbitration.d.ts +5 -0
- package/dist/schema/call/base.d.ts +3 -0
- package/dist/schema/call/base.js +3 -2
- package/dist/schema/call/bridge.d.ts +24 -0
- package/dist/schema/call/contact.d.ts +5 -0
- package/dist/schema/call/demand.d.ts +5 -0
- package/dist/schema/call/guard.d.ts +15 -0
- package/dist/schema/call/machine.d.ts +10 -0
- package/dist/schema/call/order.d.ts +5 -0
- package/dist/schema/call/payment.d.ts +5 -0
- package/dist/schema/call/permission.d.ts +5 -0
- package/dist/schema/call/personal.d.ts +5 -0
- package/dist/schema/call/progress.d.ts +5 -0
- package/dist/schema/call/proof.d.ts +10 -0
- package/dist/schema/call/repository.d.ts +5 -0
- package/dist/schema/call/reward.d.ts +5 -0
- package/dist/schema/call/service.d.ts +5 -0
- package/dist/schema/call/treasury.d.ts +5 -0
- package/dist/schema/operations.d.ts +210 -0
- package/dist/schema/operations.js +24 -0
- package/dist/schema/project/index.d.ts +2798 -85
- package/dist/schema/project/index.js +667 -2
- package/dist/schema/query/index.d.ts +601 -0
- package/dist/schema/query/index.js +61 -0
- package/dist/schemas/bridge_operation.schema.json +4 -0
- package/dist/schemas/guard2file.schema.json +4 -0
- package/dist/schemas/index.json +1 -1
- package/dist/schemas/machineNode2file.schema.json +4 -0
- package/dist/schemas/onchain_operations.schema.json +20 -0
- package/dist/schemas/onchain_operations_allocation.schema.json +4 -0
- package/dist/schemas/onchain_operations_arbitration.schema.json +4 -0
- package/dist/schemas/onchain_operations_contact.schema.json +4 -0
- package/dist/schemas/onchain_operations_demand.schema.json +4 -0
- package/dist/schemas/onchain_operations_gen_passport.schema.json +8 -0
- package/dist/schemas/onchain_operations_gen_proof.schema.json +8 -0
- package/dist/schemas/onchain_operations_guard.schema.json +4 -577
- package/dist/schemas/onchain_operations_machine.schema.json +4 -0
- package/dist/schemas/onchain_operations_order.schema.json +4 -0
- package/dist/schemas/onchain_operations_payment.schema.json +4 -0
- package/dist/schemas/onchain_operations_permission.schema.json +4 -0
- package/dist/schemas/onchain_operations_personal.schema.json +4 -0
- package/dist/schemas/onchain_operations_progress.schema.json +4 -0
- package/dist/schemas/onchain_operations_proof.schema.json +4 -0
- package/dist/schemas/onchain_operations_repository.schema.json +4 -0
- package/dist/schemas/onchain_operations_reward.schema.json +4 -0
- package/dist/schemas/onchain_operations_service.schema.json +4 -0
- package/dist/schemas/onchain_operations_treasury.schema.json +4 -0
- package/dist/schemas/project_operation.output.json +1955 -0
- package/dist/schemas/project_operation.schema.json +133 -2
- package/dist/schemas/query_toolkit.output.json +407 -1
- package/dist/schemas/query_toolkit.schema.json +132 -0
- package/dist/tools/handlers/project.js +788 -2
- package/dist/tools/handlers/query.js +38 -2
- package/dist/tools/index.js +346 -37
- package/dist/tools/wrap.js +61 -7
- package/package.json +2 -2
- package/dist/schemas/onchain_operations.output.json +0 -2819
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, copyFileSync, statSync, appendFileSync, } from "fs";
|
|
2
|
+
import { dirname, join } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
export const CURRENT_SCHEMA_VERSION = "1.0.0";
|
|
5
|
+
export function projectsRootDir() {
|
|
6
|
+
const base = process.env.WOWOK_CONFIG_DIR
|
|
7
|
+
? process.env.WOWOK_CONFIG_DIR
|
|
8
|
+
: join(homedir(), ".wowok");
|
|
9
|
+
return join(base, "projects");
|
|
10
|
+
}
|
|
11
|
+
export function projectDir(prefix, version) {
|
|
12
|
+
return join(projectsRootDir(), `${prefix}_${version}`);
|
|
13
|
+
}
|
|
14
|
+
export function deploymentDir(prefix, version) {
|
|
15
|
+
return join(projectDir(prefix, version), "deployment");
|
|
16
|
+
}
|
|
17
|
+
function manifestPath(prefix, version) {
|
|
18
|
+
return join(projectDir(prefix, version), "manifest.json");
|
|
19
|
+
}
|
|
20
|
+
function stageStatePath(prefix, version) {
|
|
21
|
+
return join(projectDir(prefix, version), "stage.json");
|
|
22
|
+
}
|
|
23
|
+
function auditLogPath(prefix, version) {
|
|
24
|
+
return join(projectDir(prefix, version), "audit.jsonl");
|
|
25
|
+
}
|
|
26
|
+
export function ensureProjectDir(prefix, version) {
|
|
27
|
+
const dir = projectDir(prefix, version);
|
|
28
|
+
if (!existsSync(dir)) {
|
|
29
|
+
mkdirSync(dir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
return dir;
|
|
32
|
+
}
|
|
33
|
+
export function initProject(prefix, version, options = {}) {
|
|
34
|
+
ensureProjectDir(prefix, version);
|
|
35
|
+
const now = new Date().toISOString();
|
|
36
|
+
const manifest = {
|
|
37
|
+
schema_version: CURRENT_SCHEMA_VERSION,
|
|
38
|
+
mcp_version: options.mcpVersion ?? "unknown",
|
|
39
|
+
prefix,
|
|
40
|
+
version,
|
|
41
|
+
created_at: now,
|
|
42
|
+
updated_at: now,
|
|
43
|
+
forked_from: options.forkedFrom,
|
|
44
|
+
};
|
|
45
|
+
writeFileSync(manifestPath(prefix, version), JSON.stringify(manifest, null, 2), "utf-8");
|
|
46
|
+
const stageState = {
|
|
47
|
+
stage: 1,
|
|
48
|
+
updated_at: Date.now(),
|
|
49
|
+
stage_meta: {
|
|
50
|
+
1: { committed: false, gate_passed: false, blocking_issues: [], completion: 0 },
|
|
51
|
+
2: { committed: false, gate_passed: false, blocking_issues: [], completion: 0 },
|
|
52
|
+
3: { committed: false, gate_passed: false, blocking_issues: [], completion: 0 },
|
|
53
|
+
4: { committed: false, gate_passed: false, blocking_issues: [], completion: 0 },
|
|
54
|
+
5: { committed: false, gate_passed: false, blocking_issues: [], completion: 0 },
|
|
55
|
+
},
|
|
56
|
+
has_published_object: false,
|
|
57
|
+
archived: false,
|
|
58
|
+
};
|
|
59
|
+
saveStageState(prefix, version, stageState);
|
|
60
|
+
appendAudit(prefix, version, {
|
|
61
|
+
timestamp: now,
|
|
62
|
+
event: "stage_commit",
|
|
63
|
+
stage: 1,
|
|
64
|
+
reason: `project initialized${options.forkedFrom ? ` (forked from ${options.forkedFrom})` : ""}`,
|
|
65
|
+
affected_files: ["manifest.json", "stage.json"],
|
|
66
|
+
});
|
|
67
|
+
return manifest;
|
|
68
|
+
}
|
|
69
|
+
export function loadManifest(prefix, version) {
|
|
70
|
+
const p = manifestPath(prefix, version);
|
|
71
|
+
if (!existsSync(p)) {
|
|
72
|
+
throw new Error(`project not initialized: ${prefix}_${version} (missing manifest.json)`);
|
|
73
|
+
}
|
|
74
|
+
const raw = readFileSync(p, "utf-8");
|
|
75
|
+
return JSON.parse(raw);
|
|
76
|
+
}
|
|
77
|
+
export function saveManifest(prefix, version, manifest) {
|
|
78
|
+
manifest.updated_at = new Date().toISOString();
|
|
79
|
+
writeFileSync(manifestPath(prefix, version), JSON.stringify(manifest, null, 2), "utf-8");
|
|
80
|
+
}
|
|
81
|
+
export function loadStageState(prefix, version) {
|
|
82
|
+
const p = stageStatePath(prefix, version);
|
|
83
|
+
if (!existsSync(p))
|
|
84
|
+
return undefined;
|
|
85
|
+
const raw = readFileSync(p, "utf-8");
|
|
86
|
+
return JSON.parse(raw);
|
|
87
|
+
}
|
|
88
|
+
export function saveStageState(prefix, version, state) {
|
|
89
|
+
state.updated_at = Date.now();
|
|
90
|
+
ensureProjectDir(prefix, version);
|
|
91
|
+
writeFileSync(stageStatePath(prefix, version), JSON.stringify(state, null, 2), "utf-8");
|
|
92
|
+
}
|
|
93
|
+
export function saveProjectFile(prefix, version, relPath, data) {
|
|
94
|
+
const abs = join(projectDir(prefix, version), relPath);
|
|
95
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
96
|
+
writeFileSync(abs, JSON.stringify(data, null, 2), "utf-8");
|
|
97
|
+
return abs;
|
|
98
|
+
}
|
|
99
|
+
export function loadProjectFile(prefix, version, relPath) {
|
|
100
|
+
const abs = join(projectDir(prefix, version), relPath);
|
|
101
|
+
if (!existsSync(abs))
|
|
102
|
+
return undefined;
|
|
103
|
+
const raw = readFileSync(abs, "utf-8");
|
|
104
|
+
return JSON.parse(raw);
|
|
105
|
+
}
|
|
106
|
+
export function saveProjectMarkdown(prefix, version, relPath, markdown) {
|
|
107
|
+
const abs = join(projectDir(prefix, version), relPath);
|
|
108
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
109
|
+
writeFileSync(abs, markdown, "utf-8");
|
|
110
|
+
return abs;
|
|
111
|
+
}
|
|
112
|
+
export function appendJsonl(prefix, version, relPath, record) {
|
|
113
|
+
const abs = join(projectDir(prefix, version), relPath);
|
|
114
|
+
ensureProjectDir(prefix, version);
|
|
115
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
116
|
+
appendFileSync(abs, JSON.stringify(record) + "\n", "utf-8");
|
|
117
|
+
return abs;
|
|
118
|
+
}
|
|
119
|
+
export function appendAudit(prefix, version, record) {
|
|
120
|
+
appendJsonl(prefix, version, "audit.jsonl", record);
|
|
121
|
+
}
|
|
122
|
+
export function readAuditLog(prefix, version) {
|
|
123
|
+
const abs = auditLogPath(prefix, version);
|
|
124
|
+
if (!existsSync(abs))
|
|
125
|
+
return [];
|
|
126
|
+
const raw = readFileSync(abs, "utf-8");
|
|
127
|
+
return raw
|
|
128
|
+
.split("\n")
|
|
129
|
+
.filter((l) => l.trim().length > 0)
|
|
130
|
+
.map((l) => JSON.parse(l));
|
|
131
|
+
}
|
|
132
|
+
export function parseProjectDirName(dirName) {
|
|
133
|
+
const m = dirName.match(/^(.+)_v(\d+)$/);
|
|
134
|
+
if (!m)
|
|
135
|
+
return undefined;
|
|
136
|
+
return { prefix: m[1], version: `v${m[2]}` };
|
|
137
|
+
}
|
|
138
|
+
export function listLocalProjects() {
|
|
139
|
+
const root = projectsRootDir();
|
|
140
|
+
if (!existsSync(root))
|
|
141
|
+
return [];
|
|
142
|
+
const entries = readdirSync(root, { withFileTypes: true });
|
|
143
|
+
const out = [];
|
|
144
|
+
for (const e of entries) {
|
|
145
|
+
if (!e.isDirectory())
|
|
146
|
+
continue;
|
|
147
|
+
const parsed = parseProjectDirName(e.name);
|
|
148
|
+
if (!parsed)
|
|
149
|
+
continue;
|
|
150
|
+
const fullPath = join(root, e.name);
|
|
151
|
+
const manifestFile = join(fullPath, "manifest.json");
|
|
152
|
+
const stageFile = join(fullPath, "stage.json");
|
|
153
|
+
let initialized = false;
|
|
154
|
+
let archived = false;
|
|
155
|
+
let needsMigration = false;
|
|
156
|
+
let forkedFrom;
|
|
157
|
+
if (existsSync(manifestFile)) {
|
|
158
|
+
initialized = true;
|
|
159
|
+
try {
|
|
160
|
+
const m = JSON.parse(readFileSync(manifestFile, "utf-8"));
|
|
161
|
+
needsMigration = m.schema_version !== CURRENT_SCHEMA_VERSION;
|
|
162
|
+
forkedFrom = m.forked_from;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (existsSync(stageFile)) {
|
|
168
|
+
try {
|
|
169
|
+
const s = JSON.parse(readFileSync(stageFile, "utf-8"));
|
|
170
|
+
archived = s.archived;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
let mtime = 0;
|
|
176
|
+
try {
|
|
177
|
+
mtime = statSync(fullPath).mtimeMs;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
}
|
|
181
|
+
out.push({
|
|
182
|
+
dir_name: e.name,
|
|
183
|
+
prefix: parsed.prefix,
|
|
184
|
+
version: parsed.version,
|
|
185
|
+
path: fullPath,
|
|
186
|
+
initialized,
|
|
187
|
+
archived,
|
|
188
|
+
needs_migration: needsMigration,
|
|
189
|
+
forked_from: forkedFrom,
|
|
190
|
+
mtime,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return out.sort((a, b) => b.mtime - a.mtime);
|
|
194
|
+
}
|
|
195
|
+
export function projectExists(prefix, version) {
|
|
196
|
+
return existsSync(manifestPath(prefix, version));
|
|
197
|
+
}
|
|
198
|
+
export function forkProject(srcPrefix, srcVersion, opts) {
|
|
199
|
+
if (!projectExists(srcPrefix, srcVersion)) {
|
|
200
|
+
throw new Error(`source project not found: ${srcPrefix}_${srcVersion}`);
|
|
201
|
+
}
|
|
202
|
+
const newPrefix = opts.newPrefix ?? srcPrefix;
|
|
203
|
+
if (opts.newVersion === srcVersion && newPrefix === srcPrefix) {
|
|
204
|
+
throw new Error("fork target must differ from source (set newVersion or newPrefix)");
|
|
205
|
+
}
|
|
206
|
+
ensureProjectDir(newPrefix, opts.newVersion);
|
|
207
|
+
const srcDir = projectDir(srcPrefix, srcVersion);
|
|
208
|
+
const dstDir = projectDir(newPrefix, opts.newVersion);
|
|
209
|
+
const copiedFiles = [];
|
|
210
|
+
function walkDir(relBase) {
|
|
211
|
+
const absBase = join(srcDir, relBase);
|
|
212
|
+
if (!existsSync(absBase))
|
|
213
|
+
return;
|
|
214
|
+
const items = readdirSync(absBase, { withFileTypes: true });
|
|
215
|
+
for (const item of items) {
|
|
216
|
+
const rel = relBase ? join(relBase, item.name) : item.name;
|
|
217
|
+
const srcAbs = join(srcDir, rel);
|
|
218
|
+
const dstAbs = join(dstDir, rel);
|
|
219
|
+
if (item.isDirectory()) {
|
|
220
|
+
mkdirSync(dstAbs, { recursive: true });
|
|
221
|
+
walkDir(rel);
|
|
222
|
+
}
|
|
223
|
+
else if (item.isFile()) {
|
|
224
|
+
if (rel === "stage.json" || rel === "audit.jsonl")
|
|
225
|
+
continue;
|
|
226
|
+
mkdirSync(dirname(dstAbs), { recursive: true });
|
|
227
|
+
copyFileSync(srcAbs, dstAbs);
|
|
228
|
+
copiedFiles.push(rel);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
walkDir("");
|
|
233
|
+
const now = new Date().toISOString();
|
|
234
|
+
const manifest = {
|
|
235
|
+
schema_version: CURRENT_SCHEMA_VERSION,
|
|
236
|
+
mcp_version: opts.mcpVersion ?? "unknown",
|
|
237
|
+
prefix: newPrefix,
|
|
238
|
+
version: opts.newVersion,
|
|
239
|
+
created_at: now,
|
|
240
|
+
updated_at: now,
|
|
241
|
+
forked_from: srcDir,
|
|
242
|
+
};
|
|
243
|
+
writeFileSync(manifestPath(newPrefix, opts.newVersion), JSON.stringify(manifest, null, 2), "utf-8");
|
|
244
|
+
const stageState = {
|
|
245
|
+
stage: 1,
|
|
246
|
+
updated_at: Date.now(),
|
|
247
|
+
stage_meta: {
|
|
248
|
+
1: { committed: false, gate_passed: false, blocking_issues: [], completion: 0 },
|
|
249
|
+
2: { committed: false, gate_passed: false, blocking_issues: [], completion: 0 },
|
|
250
|
+
3: { committed: false, gate_passed: false, blocking_issues: [], completion: 0 },
|
|
251
|
+
4: { committed: false, gate_passed: false, blocking_issues: [], completion: 0 },
|
|
252
|
+
5: { committed: false, gate_passed: false, blocking_issues: [], completion: 0 },
|
|
253
|
+
},
|
|
254
|
+
has_published_object: false,
|
|
255
|
+
archived: false,
|
|
256
|
+
};
|
|
257
|
+
saveStageState(newPrefix, opts.newVersion, stageState);
|
|
258
|
+
appendAudit(newPrefix, opts.newVersion, {
|
|
259
|
+
timestamp: now,
|
|
260
|
+
event: "fork_created",
|
|
261
|
+
reason: `forked from ${srcPrefix}_${srcVersion}; objects must be re-created (project isolation §3.0)`,
|
|
262
|
+
affected_files: ["manifest.json", "stage.json"],
|
|
263
|
+
});
|
|
264
|
+
const srcStage = loadStageState(srcPrefix, srcVersion);
|
|
265
|
+
if (srcStage) {
|
|
266
|
+
srcStage.archived = true;
|
|
267
|
+
saveStageState(srcPrefix, srcVersion, srcStage);
|
|
268
|
+
appendAudit(srcPrefix, srcVersion, {
|
|
269
|
+
timestamp: now,
|
|
270
|
+
event: "stage_commit",
|
|
271
|
+
reason: `archived after fork to ${newPrefix}_${opts.newVersion}`,
|
|
272
|
+
affected_files: ["stage.json"],
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
new_prefix: newPrefix,
|
|
277
|
+
new_version: opts.newVersion,
|
|
278
|
+
new_path: dstDir,
|
|
279
|
+
forked_from: srcDir,
|
|
280
|
+
copied_files: copiedFiles,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
export function listProjectFiles(prefix, version) {
|
|
284
|
+
const dir = projectDir(prefix, version);
|
|
285
|
+
if (!existsSync(dir))
|
|
286
|
+
return [];
|
|
287
|
+
const out = [];
|
|
288
|
+
function walk(relBase) {
|
|
289
|
+
const absBase = join(dir, relBase);
|
|
290
|
+
const items = readdirSync(absBase, { withFileTypes: true });
|
|
291
|
+
for (const item of items) {
|
|
292
|
+
const rel = relBase ? join(relBase, item.name) : item.name;
|
|
293
|
+
const abs = join(dir, rel);
|
|
294
|
+
if (item.isDirectory()) {
|
|
295
|
+
walk(rel);
|
|
296
|
+
}
|
|
297
|
+
else if (item.isFile()) {
|
|
298
|
+
let stat;
|
|
299
|
+
try {
|
|
300
|
+
stat = statSync(abs);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
let kind = "other";
|
|
306
|
+
if (abs.endsWith(".json"))
|
|
307
|
+
kind = "json";
|
|
308
|
+
else if (abs.endsWith(".md"))
|
|
309
|
+
kind = "markdown";
|
|
310
|
+
else if (abs.endsWith(".jsonl"))
|
|
311
|
+
kind = "jsonl";
|
|
312
|
+
out.push({
|
|
313
|
+
path: rel,
|
|
314
|
+
abs_path: abs,
|
|
315
|
+
size: stat.size,
|
|
316
|
+
mtime: stat.mtimeMs,
|
|
317
|
+
kind,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
walk("");
|
|
323
|
+
return out.sort((a, b) => a.path.localeCompare(b.path));
|
|
324
|
+
}
|
|
325
|
+
export function readProjectFile(prefix, version, relPath) {
|
|
326
|
+
const abs = join(projectDir(prefix, version), relPath);
|
|
327
|
+
if (!existsSync(abs))
|
|
328
|
+
return undefined;
|
|
329
|
+
return readFileSync(abs, "utf-8");
|
|
330
|
+
}
|
|
331
|
+
export function commitStage(prefix, version, stage, options = {}) {
|
|
332
|
+
const state = loadStageState(prefix, version);
|
|
333
|
+
if (!state) {
|
|
334
|
+
throw new Error(`project not initialized: ${prefix}_${version}`);
|
|
335
|
+
}
|
|
336
|
+
const gatePassed = options.gatePassed ?? false;
|
|
337
|
+
state.stage_meta[stage] = {
|
|
338
|
+
committed: true,
|
|
339
|
+
committed_at: Date.now(),
|
|
340
|
+
gate_passed: gatePassed,
|
|
341
|
+
blocking_issues: options.blockingIssues ?? [],
|
|
342
|
+
completion: options.completion ?? (gatePassed ? 100 : 0),
|
|
343
|
+
};
|
|
344
|
+
if (stage < 5 && gatePassed) {
|
|
345
|
+
state.stage = (stage + 1);
|
|
346
|
+
}
|
|
347
|
+
saveStageState(prefix, version, state);
|
|
348
|
+
appendAudit(prefix, version, {
|
|
349
|
+
timestamp: new Date().toISOString(),
|
|
350
|
+
event: "stage_commit",
|
|
351
|
+
stage,
|
|
352
|
+
reason: options.reason ?? `stage ${stage} committed`,
|
|
353
|
+
affected_files: ["stage.json"],
|
|
354
|
+
user_confirmed: options.userConfirmed,
|
|
355
|
+
});
|
|
356
|
+
return state;
|
|
357
|
+
}
|
|
358
|
+
export function rollbackStage(prefix, version, toStage, options = {}) {
|
|
359
|
+
const state = loadStageState(prefix, version);
|
|
360
|
+
if (!state) {
|
|
361
|
+
throw new Error(`project not initialized: ${prefix}_${version}`);
|
|
362
|
+
}
|
|
363
|
+
if (state.has_published_object) {
|
|
364
|
+
throw new Error(`cannot rollback: project has published objects — must fork new version (§3.3)`);
|
|
365
|
+
}
|
|
366
|
+
const oldStage = state.stage;
|
|
367
|
+
for (let s = toStage + 1; s <= 5; s++) {
|
|
368
|
+
state.stage_meta[s] = {
|
|
369
|
+
committed: false,
|
|
370
|
+
gate_passed: false,
|
|
371
|
+
blocking_issues: [],
|
|
372
|
+
completion: 0,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
state.stage = toStage;
|
|
376
|
+
saveStageState(prefix, version, state);
|
|
377
|
+
appendAudit(prefix, version, {
|
|
378
|
+
timestamp: new Date().toISOString(),
|
|
379
|
+
event: "stage_rollback",
|
|
380
|
+
stage: toStage,
|
|
381
|
+
reason: options.reason ?? `rollback from stage ${oldStage} to ${toStage}`,
|
|
382
|
+
affected_files: options.affectedFiles ?? ["stage.json"],
|
|
383
|
+
});
|
|
384
|
+
return state;
|
|
385
|
+
}
|
|
386
|
+
export function markObjectPublished(prefix, version, objectName) {
|
|
387
|
+
const state = loadStageState(prefix, version);
|
|
388
|
+
if (!state)
|
|
389
|
+
return;
|
|
390
|
+
if (state.has_published_object)
|
|
391
|
+
return;
|
|
392
|
+
state.has_published_object = true;
|
|
393
|
+
saveStageState(prefix, version, state);
|
|
394
|
+
appendAudit(prefix, version, {
|
|
395
|
+
timestamp: new Date().toISOString(),
|
|
396
|
+
event: "object_published",
|
|
397
|
+
stage: 5,
|
|
398
|
+
reason: `object published: ${objectName} — rollback now requires fork (§3.3)`,
|
|
399
|
+
affected_files: ["stage.json"],
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
export function getLocalSyncStatus(prefix, version) {
|
|
403
|
+
const exists = projectExists(prefix, version);
|
|
404
|
+
if (!exists) {
|
|
405
|
+
return { local_exists: false, initialized: false, needs_migration: false, archived: false };
|
|
406
|
+
}
|
|
407
|
+
let initialized = true;
|
|
408
|
+
let needsMigration = false;
|
|
409
|
+
let archived = false;
|
|
410
|
+
try {
|
|
411
|
+
const m = loadManifest(prefix, version);
|
|
412
|
+
needsMigration = m.schema_version !== CURRENT_SCHEMA_VERSION;
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
initialized = false;
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
const s = loadStageState(prefix, version);
|
|
419
|
+
if (s)
|
|
420
|
+
archived = s.archived;
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
}
|
|
424
|
+
return { local_exists: true, initialized, needs_migration: needsMigration, archived };
|
|
425
|
+
}
|
|
@@ -40,6 +40,13 @@ export interface RiskAggregationResult {
|
|
|
40
40
|
entry_id: string;
|
|
41
41
|
}>;
|
|
42
42
|
calibrated_at: number;
|
|
43
|
+
fix_suggestions_grouped?: Record<string, Array<{
|
|
44
|
+
risk_rule_id: string;
|
|
45
|
+
severity: RiskSeverity;
|
|
46
|
+
message: string;
|
|
47
|
+
fix_suggestion: string;
|
|
48
|
+
entry_id: string;
|
|
49
|
+
}>>;
|
|
43
50
|
warnings: string[];
|
|
44
51
|
}
|
|
45
52
|
export declare function aggregateProjectRisks(input: RiskAggregationInput): RiskAggregationResult;
|
|
@@ -179,6 +179,20 @@ export function aggregateProjectRisks(input) {
|
|
|
179
179
|
entry_id: f.entry_id,
|
|
180
180
|
}));
|
|
181
181
|
}
|
|
182
|
+
const fixSuggestionsGrouped = {};
|
|
183
|
+
for (const finding of findings) {
|
|
184
|
+
const key = finding.object_type;
|
|
185
|
+
if (!fixSuggestionsGrouped[key]) {
|
|
186
|
+
fixSuggestionsGrouped[key] = [];
|
|
187
|
+
}
|
|
188
|
+
fixSuggestionsGrouped[key].push({
|
|
189
|
+
risk_rule_id: finding.risk_rule_id,
|
|
190
|
+
severity: finding.severity,
|
|
191
|
+
message: finding.message,
|
|
192
|
+
fix_suggestion: finding.fix_suggestion,
|
|
193
|
+
entry_id: finding.entry_id,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
182
196
|
return {
|
|
183
197
|
namespace: {
|
|
184
198
|
prefix: input.project_prefix ?? "",
|
|
@@ -190,6 +204,7 @@ export function aggregateProjectRisks(input) {
|
|
|
190
204
|
status,
|
|
191
205
|
pending_confirmations,
|
|
192
206
|
calibrated_at: Date.now(),
|
|
207
|
+
fix_suggestions_grouped: fixSuggestionsGrouped,
|
|
193
208
|
warnings,
|
|
194
209
|
};
|
|
195
210
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ProjectStage, type StageState } from "./project-store.js";
|
|
2
|
+
export type ObjectLifecycleState = "created" | "referenced" | "published" | "bound";
|
|
3
|
+
export type StructReversibility = "reversible" | "limited" | "immutable";
|
|
4
|
+
export type GasImpact = "low" | "medium" | "high" | "none";
|
|
5
|
+
export interface ReversibilityEntry {
|
|
6
|
+
object_type: string;
|
|
7
|
+
lifecycle_state: ObjectLifecycleState;
|
|
8
|
+
struct_reversible: StructReversibility;
|
|
9
|
+
behavior_reversible: boolean;
|
|
10
|
+
gas_impact: GasImpact;
|
|
11
|
+
explanation: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const REVERSIBILITY_MATRIX: ReversibilityEntry[];
|
|
14
|
+
export declare function queryReversibility(objectType: string, lifecycleState: ObjectLifecycleState): ReversibilityEntry | undefined;
|
|
15
|
+
export declare function getReversibilityForType(objectType: string): ReversibilityEntry[];
|
|
16
|
+
export type IterationStrategy = "in_place" | "fork";
|
|
17
|
+
export interface IterationRecommendation {
|
|
18
|
+
strategy: IterationStrategy;
|
|
19
|
+
reason: string;
|
|
20
|
+
fork_to_version?: string;
|
|
21
|
+
published_objects?: string[];
|
|
22
|
+
}
|
|
23
|
+
export declare function recommendIterationStrategy(prefix: string, version: string, currentStage?: ProjectStage): IterationRecommendation;
|
|
24
|
+
export interface RollbackPolicyResult {
|
|
25
|
+
allowed: boolean;
|
|
26
|
+
target_stage: ProjectStage;
|
|
27
|
+
current_stage: ProjectStage;
|
|
28
|
+
block_reason?: string;
|
|
29
|
+
files_to_clean: string[];
|
|
30
|
+
warnings: string[];
|
|
31
|
+
iteration_strategy: IterationRecommendation;
|
|
32
|
+
}
|
|
33
|
+
export declare function evaluateRollbackPolicy(prefix: string, version: string, targetStage: ProjectStage): RollbackPolicyResult;
|
|
34
|
+
export interface RollbackResult {
|
|
35
|
+
success: boolean;
|
|
36
|
+
target_stage: ProjectStage;
|
|
37
|
+
previous_stage: ProjectStage;
|
|
38
|
+
files_to_clean: string[];
|
|
39
|
+
files_cleaned: string[];
|
|
40
|
+
stage_state: StageState;
|
|
41
|
+
warnings: string[];
|
|
42
|
+
iteration_strategy: IterationRecommendation;
|
|
43
|
+
message: string;
|
|
44
|
+
}
|
|
45
|
+
export declare function rollbackStageWithPolicy(prefix: string, version: string, targetStage: ProjectStage, reason?: string): RollbackResult;
|