@evomap/evolver 1.90.0 → 2.0.0-beta.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.
Files changed (231) hide show
  1. package/README.md +27 -564
  2. package/bin/evolver-llm-proxy.js +3 -0
  3. package/bin/evolver-mcp.js +2 -0
  4. package/bin/evolver-proxy.js +3 -0
  5. package/bin/evolver.js +4 -0
  6. package/index.js +1 -3670
  7. package/package.json +32 -62
  8. package/CONTRIBUTING.md +0 -19
  9. package/LICENSE +0 -641
  10. package/README.ja-JP.md +0 -521
  11. package/README.ko-KR.md +0 -520
  12. package/README.zh-CN.md +0 -531
  13. package/SKILL.md +0 -365
  14. package/assets/cover.png +0 -0
  15. package/assets/gep/genes.seed.json +0 -496
  16. package/conformance/savings-core/constants.json +0 -30
  17. package/conformance/savings-core/golden-vectors.json +0 -333
  18. package/scripts/a2a_export.js +0 -63
  19. package/scripts/a2a_ingest.js +0 -79
  20. package/scripts/a2a_promote.js +0 -118
  21. package/scripts/analyze_by_skill.js +0 -121
  22. package/scripts/build_binaries.js +0 -479
  23. package/scripts/check-changelog.js +0 -166
  24. package/scripts/extract_log.js +0 -85
  25. package/scripts/generate_history.js +0 -75
  26. package/scripts/gep_append_event.js +0 -96
  27. package/scripts/gep_personality_report.js +0 -234
  28. package/scripts/human_report.js +0 -147
  29. package/scripts/recall-verify-report.js +0 -234
  30. package/scripts/recover_loop.js +0 -61
  31. package/scripts/refresh_stars_badge.js +0 -168
  32. package/scripts/seed-merchants.js +0 -91
  33. package/scripts/skill2recipes.js +0 -118
  34. package/scripts/suggest_version.js +0 -89
  35. package/scripts/validate-modules.js +0 -38
  36. package/scripts/validate-suite.js +0 -78
  37. package/src/adapters/claudeCode.js +0 -194
  38. package/src/adapters/codex.js +0 -216
  39. package/src/adapters/cursor.js +0 -91
  40. package/src/adapters/hookAdapter.js +0 -469
  41. package/src/adapters/kiro.js +0 -195
  42. package/src/adapters/opencode.js +0 -326
  43. package/src/adapters/scripts/_lockPaths.js +0 -74
  44. package/src/adapters/scripts/_memoryFiltering.js +0 -35
  45. package/src/adapters/scripts/_runtimePaths.js +0 -440
  46. package/src/adapters/scripts/evolver-session-end.js +0 -321
  47. package/src/adapters/scripts/evolver-session-start.js +0 -587
  48. package/src/adapters/scripts/evolver-signal-detect.js +0 -98
  49. package/src/adapters/scripts/evolver-task-recall.js +0 -173
  50. package/src/atp/atpExecute.js +0 -283
  51. package/src/atp/atpTaskPickup.js +0 -233
  52. package/src/atp/autoBuyer.js +0 -382
  53. package/src/atp/autoDeliver.js +0 -215
  54. package/src/atp/cli.js +0 -354
  55. package/src/atp/cliAutobuyPrompt.js +0 -154
  56. package/src/atp/consumerAgent.js +0 -157
  57. package/src/atp/defaultHandler.js +0 -69
  58. package/src/atp/heartbeatSignalsHandler.js +0 -254
  59. package/src/atp/hubClient.js +0 -317
  60. package/src/atp/index.js +0 -38
  61. package/src/atp/merchantAgent.js +0 -118
  62. package/src/atp/protocol.js +0 -41
  63. package/src/atp/questionComposer.js +0 -133
  64. package/src/atp/serviceHelper.js +0 -92
  65. package/src/canary.js +0 -13
  66. package/src/config.js +0 -351
  67. package/src/evolve/guards.js +0 -1
  68. package/src/evolve/pipeline/collect.js +0 -1
  69. package/src/evolve/pipeline/dispatch.js +0 -1
  70. package/src/evolve/pipeline/enrich.js +0 -1
  71. package/src/evolve/pipeline/hub.js +0 -1
  72. package/src/evolve/pipeline/select.js +0 -1
  73. package/src/evolve/pipeline/signals.js +0 -1
  74. package/src/evolve/utils.js +0 -1
  75. package/src/evolve.js +0 -1
  76. package/src/experiment/agentRunner.js +0 -229
  77. package/src/experiment/cli.js +0 -159
  78. package/src/experiment/comparison.js +0 -233
  79. package/src/experiment/metrics.js +0 -75
  80. package/src/forceUpdate.js +0 -991
  81. package/src/gep/a2a.js +0 -173
  82. package/src/gep/a2aProtocol.js +0 -1
  83. package/src/gep/analyzer.js +0 -35
  84. package/src/gep/antiAbuseTelemetry.js +0 -1
  85. package/src/gep/assetCallLog.js +0 -197
  86. package/src/gep/assetStore.js +0 -723
  87. package/src/gep/assets.js +0 -36
  88. package/src/gep/autoDistillConv.js +0 -1
  89. package/src/gep/autoDistillLlm.js +0 -1
  90. package/src/gep/bridge.js +0 -138
  91. package/src/gep/candidateEval.js +0 -1
  92. package/src/gep/candidates.js +0 -1
  93. package/src/gep/claimNudge.js +0 -121
  94. package/src/gep/cliContracts.js +0 -1190
  95. package/src/gep/contentHash.js +0 -1
  96. package/src/gep/conversationDistiller.js +0 -1
  97. package/src/gep/conversationSniffer.js +0 -1
  98. package/src/gep/crypto.js +0 -1
  99. package/src/gep/curriculum.js +0 -1
  100. package/src/gep/deviceId.js +0 -1
  101. package/src/gep/directoryClient.js +0 -115
  102. package/src/gep/envFingerprint.js +0 -1
  103. package/src/gep/epigenetics.js +0 -1
  104. package/src/gep/execBridge.js +0 -1
  105. package/src/gep/executionTrace.js +0 -291
  106. package/src/gep/explore.js +0 -1
  107. package/src/gep/featureFlags.js +0 -121
  108. package/src/gep/gitOps.js +0 -265
  109. package/src/gep/hash.js +0 -1
  110. package/src/gep/hostErrorClassifier.js +0 -34
  111. package/src/gep/hubFetch.js +0 -1
  112. package/src/gep/hubReview.js +0 -1
  113. package/src/gep/hubSearch.js +0 -1
  114. package/src/gep/hubVerify.js +0 -1
  115. package/src/gep/idleScheduler.js +0 -400
  116. package/src/gep/issueReporter.js +0 -416
  117. package/src/gep/learningSignals.js +0 -1
  118. package/src/gep/llmReview.js +0 -92
  119. package/src/gep/localStateAwareness.js +0 -243
  120. package/src/gep/mailboxTransport.js +0 -119
  121. package/src/gep/memoryGraph.js +0 -1
  122. package/src/gep/memoryGraphAdapter.js +0 -1
  123. package/src/gep/mutation.js +0 -1
  124. package/src/gep/narrativeMemory.js +0 -1
  125. package/src/gep/oauthLogin.js +0 -181
  126. package/src/gep/openPRRegistry.js +0 -1
  127. package/src/gep/paths.js +0 -522
  128. package/src/gep/personality.js +0 -1
  129. package/src/gep/policyCheck.js +0 -1
  130. package/src/gep/portable.js +0 -103
  131. package/src/gep/privacyClient.js +0 -235
  132. package/src/gep/prompt.js +0 -1
  133. package/src/gep/questionGenerator.js +0 -518
  134. package/src/gep/recallInject.js +0 -1
  135. package/src/gep/recallVerifier.js +0 -1
  136. package/src/gep/reflection.js +0 -1
  137. package/src/gep/sanitize.js +0 -264
  138. package/src/gep/savingsCore.js +0 -1
  139. package/src/gep/schemas/capsule.js +0 -170
  140. package/src/gep/schemas/gene.js +0 -154
  141. package/src/gep/schemas/index.js +0 -8
  142. package/src/gep/schemas/protocol.js +0 -51
  143. package/src/gep/schemas/task.js +0 -74
  144. package/src/gep/selector.js +0 -1
  145. package/src/gep/selfPR.js +0 -469
  146. package/src/gep/signals.js +0 -776
  147. package/src/gep/skill2gep.js +0 -1056
  148. package/src/gep/skill2gepAudit.js +0 -303
  149. package/src/gep/skill2recipes.js +0 -511
  150. package/src/gep/skillDistiller.js +0 -1
  151. package/src/gep/skillPublisher.js +0 -358
  152. package/src/gep/solidify.js +0 -1
  153. package/src/gep/strategy.js +0 -1
  154. package/src/gep/taskReceiver.js +0 -575
  155. package/src/gep/tokenSavings.js +0 -1
  156. package/src/gep/trajectoryExport.js +0 -1
  157. package/src/gep/validationReport.js +0 -55
  158. package/src/gep/validator/index.js +0 -411
  159. package/src/gep/validator/reporter.js +0 -210
  160. package/src/gep/validator/sandboxExecutor.js +0 -480
  161. package/src/gep/validator/stakeBootstrap.js +0 -357
  162. package/src/gep/workspaceKeychain.js +0 -1
  163. package/src/ops/cleanup.js +0 -80
  164. package/src/ops/commentary.js +0 -60
  165. package/src/ops/health_check.js +0 -104
  166. package/src/ops/index.js +0 -11
  167. package/src/ops/innovation.js +0 -67
  168. package/src/ops/lifecycle.js +0 -798
  169. package/src/ops/self_repair.js +0 -76
  170. package/src/ops/skills_monitor.js +0 -147
  171. package/src/ops/trigger.js +0 -33
  172. package/src/proxy/clientSettings.js +0 -405
  173. package/src/proxy/envelope.js +0 -59
  174. package/src/proxy/extensions/dmHandler.js +0 -45
  175. package/src/proxy/extensions/sessionHandler.js +0 -141
  176. package/src/proxy/extensions/skillUpdater.js +0 -64
  177. package/src/proxy/extensions/traceControl.js +0 -1
  178. package/src/proxy/index.js +0 -1395
  179. package/src/proxy/inject.js +0 -1
  180. package/src/proxy/lifecycle/manager.js +0 -1568
  181. package/src/proxy/mailbox/state.js +0 -207
  182. package/src/proxy/mailbox/store.js +0 -602
  183. package/src/proxy/router/cache_passthrough.js +0 -26
  184. package/src/proxy/router/features.js +0 -84
  185. package/src/proxy/router/gemini_route.js +0 -154
  186. package/src/proxy/router/messages_route.js +0 -535
  187. package/src/proxy/router/model_router.js +0 -113
  188. package/src/proxy/router/models_route.js +0 -52
  189. package/src/proxy/router/ollama_route.js +0 -103
  190. package/src/proxy/router/responses_route.js +0 -170
  191. package/src/proxy/router/vertex_route.js +0 -110
  192. package/src/proxy/server/http.js +0 -363
  193. package/src/proxy/server/routes.js +0 -558
  194. package/src/proxy/server/settings.js +0 -115
  195. package/src/proxy/sync/engine.js +0 -179
  196. package/src/proxy/sync/inbound.js +0 -211
  197. package/src/proxy/sync/outbound.js +0 -361
  198. package/src/proxy/task/monitor.js +0 -131
  199. package/src/proxy/trace/extractor.js +0 -1
  200. package/src/proxy/trace/usage.js +0 -1
  201. package/src/solo/breaker.js +0 -25
  202. package/src/solo/gitGuard.js +0 -65
  203. package/src/webui/client/clientJs/assets.js +0 -111
  204. package/src/webui/client/clientJs/bootstrap.js +0 -92
  205. package/src/webui/client/clientJs/common.js +0 -77
  206. package/src/webui/client/clientJs/i18n.js +0 -366
  207. package/src/webui/client/clientJs/index.js +0 -35
  208. package/src/webui/client/clientJs/interactions.js +0 -351
  209. package/src/webui/client/clientJs/overview.js +0 -152
  210. package/src/webui/client/clientJs/personality.js +0 -285
  211. package/src/webui/client/clientJs/pipelines.js +0 -330
  212. package/src/webui/client/indexHtml.js +0 -221
  213. package/src/webui/client/static.js +0 -23
  214. package/src/webui/client/stylesCss.js +0 -639
  215. package/src/webui/client/vendor/README.md +0 -15
  216. package/src/webui/client/vendor/echarts.min.js +0 -45
  217. package/src/webui/index.js +0 -14
  218. package/src/webui/observer/assets.js +0 -146
  219. package/src/webui/observer/index.js +0 -37
  220. package/src/webui/observer/interactions.js +0 -127
  221. package/src/webui/observer/jsonl.js +0 -75
  222. package/src/webui/observer/paths.js +0 -46
  223. package/src/webui/observer/personality.js +0 -43
  224. package/src/webui/observer/pipelineEvents.js +0 -58
  225. package/src/webui/observer/redact.js +0 -63
  226. package/src/webui/observer/runs.js +0 -356
  227. package/src/webui/observer/safety.js +0 -57
  228. package/src/webui/observer/skills.js +0 -70
  229. package/src/webui/observer/status.js +0 -71
  230. package/src/webui/server/http.js +0 -138
  231. package/src/webui/server/routes.js +0 -41
@@ -1,1190 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const util = require('util');
4
- const { getGepAssetsDir } = require('./paths');
5
- const { computeAssetId } = require('./contentHash');
6
- const { sanitizePayload, redactString, fullLeakCheck } = require('./sanitize');
7
-
8
- const REUSE_CONTRACT = 'reuse.v1';
9
- const PUBLISH_CONTRACT = 'publish.v1';
10
- const REVERSIBILITY = 'irreversible';
11
- const DRY_RUN_NODE_ID = 'node_000000000000';
12
- const MAX_ASSETS = 50;
13
- const ASSET_FLAGS = new Set(['--asset', '--gene', '--capsule', '--event']);
14
- const ASSET_FLAG_LIST = Array.from(ASSET_FLAGS);
15
- const MACHINE_JSON_STDOUT_WRITE = Symbol('machineJsonStdoutWrite');
16
- const NODE_SCOPED_ENDPOINT_PATHS = new Set(['/a2a/fetch', '/a2a/validate', '/a2a/publish']);
17
- const REUSE_FAILURE_REASONS = new Set([
18
- 'missing_id',
19
- 'cli_unavailable',
20
- 'auth_required',
21
- 'not_found',
22
- 'network_error',
23
- 'unsupported',
24
- 'internal_error',
25
- ]);
26
- const PUBLISH_FAILURE_REASONS = new Set([
27
- 'redaction_unavailable',
28
- 'leak_detected',
29
- 'schema_invalid',
30
- 'bundle_required',
31
- 'quality_gate_failed',
32
- 'auth_required',
33
- 'insufficient_credits',
34
- 'network_error',
35
- 'unsupported',
36
- 'cli_unavailable',
37
- 'internal_error',
38
- ]);
39
- const HUB_METADATA_KEYS = new Set([
40
- 'credit_cost',
41
- 'gdi_score',
42
- 'success_rate',
43
- 'reuse_count',
44
- 'ranking_score',
45
- 'source_node_id',
46
- 'fetched_at',
47
- 'receipt',
48
- 'hub_receipt',
49
- 'already_purchased',
50
- '_semantic_similarity',
51
- 'semantic_similarity',
52
- '_search_score',
53
- 'search_score',
54
- '_match_score',
55
- 'match_score',
56
- '_retrieval_rank',
57
- 'retrieval_rank',
58
- ]);
59
-
60
- async function runReuseCommand(args, deps) {
61
- deps = deps || {};
62
- const out = deps.out || process.stdout;
63
- const parsed = parseReuseArgs(args || []);
64
- const machineJson = parsed.jsonOut || out === process.stdout;
65
- const write = (value, code) => writeJson(out, value, code, deps, machineJson);
66
- if (!parsed.ok) return write(reuseFailure(parsed.reason, parsed.message), 1);
67
- return withMachineJsonConsole(machineJson, deps, async () => {
68
- try {
69
- const asset = deps.fetchAssetById
70
- ? await deps.fetchAssetById(parsed.assetId)
71
- : await fetchAssetById(parsed.assetId, deps);
72
- if (!asset) return write(reuseFailure('not_found', 'asset not found'), 1);
73
- const cleaned = stripHubMetadata(asset);
74
- const verifiedId = verifyReuseAssetId(cleaned, parsed.assetId);
75
- if (!verifiedId.ok) {
76
- return write(reuseFailure('internal_error', 'asset integrity verification failed'), 1);
77
- }
78
- if (!isReuseAssetStoreStable(cleaned)) {
79
- return write(reuseFailure('internal_error', 'asset integrity verification failed'), 1);
80
- }
81
- const store = prepareReuseAssetStore(cleaned, deps);
82
- const provenance = prepareHubProvenance(deps);
83
- markHubProvenance(parsed.assetId, deps, provenance);
84
- let storedId;
85
- try {
86
- storedId = storeReusedAsset(cleaned, deps, store);
87
- if (storedId !== parsed.assetId) {
88
- throw new ContractError('internal_error', 'asset integrity verification failed');
89
- }
90
- } catch (e) {
91
- rollbackHubProvenance(provenance);
92
- throw e;
93
- }
94
- return write({
95
- ok: true,
96
- contract: REUSE_CONTRACT,
97
- status: 'ok',
98
- asset_id: storedId,
99
- action: 'reused',
100
- }, 0);
101
- } catch (e) {
102
- const f = classifyError(e, 'reuse');
103
- return write(reuseFailure(f.reason, f.message), 1);
104
- }
105
- });
106
- }
107
-
108
- async function runPublishCommand(args, deps) {
109
- deps = deps || {};
110
- const out = deps.out || process.stdout;
111
- const parsed = parsePublishArgs(args || []);
112
- const machineJson = parsed.jsonOut || out === process.stdout;
113
- const write = (value, code) => writeJson(out, value, code, deps, machineJson);
114
- if (!parsed.ok) return write(publishFailure(parsed.reason, parsed.message, { retryable: false }), 1);
115
- return withMachineJsonConsole(machineJson, deps, async () => {
116
- try {
117
- const bundle = buildPublishBundle(parsed.assetRefs, Object.assign({}, deps, { noAssetStoreInit: parsed.dryRun }));
118
- if (!bundle.ok) {
119
- return write(publishFailure(bundle.reason, bundle.message, { retryable: false, gates: bundle.gates }), 1);
120
- }
121
- const initialBlocked = writeBlockedPublishResult(bundle, parsed, write);
122
- if (initialBlocked !== null) return initialBlocked;
123
-
124
- const message = buildPublishMessage(bundle.sanitized, deps, { preview: parsed.dryRun });
125
- syncBundleFromPublishMessage(bundle, message, deps);
126
- const finalBlocked = writeBlockedPublishResult(bundle, parsed, write);
127
- if (finalBlocked !== null) return finalBlocked;
128
- if (parsed.dryRun) {
129
- const validation = await postValidate(message, deps);
130
- const credits = extractCredits(validation.body);
131
- if (!validation.ok) {
132
- const reason = publishReasonFromResponse(validation.status, validation.body, validation.reason);
133
- if (reason === 'quality_gate_failed') {
134
- bundle.gates.quality = 'fail';
135
- if (!bundle.blockReasons.includes('quality_gate_failed')) {
136
- bundle.blockReasons.push('quality_gate_failed');
137
- }
138
- return write(dryRunEnvelope(bundle, credits), 0);
139
- }
140
- return write(publishFailure(reason, publishReasonMessage(reason), {
141
- retryable: publishRetryable(reason),
142
- mode: 'dry_run',
143
- gates: bundle.gates,
144
- assets: bundle.assets,
145
- credits,
146
- }), 1);
147
- }
148
- return write(dryRunEnvelope(bundle, credits), 0);
149
- }
150
-
151
- const validation = await postValidate(message, deps);
152
- if (!validation.ok) {
153
- const reason = publishReasonFromResponse(validation.status, validation.body, validation.reason);
154
- return write(publishFailure(reason, publishReasonMessage(reason), {
155
- retryable: publishRetryable(reason),
156
- mode: 'publish',
157
- gates: Object.assign({}, bundle.gates, { quality: 'fail' }),
158
- assets: bundle.assets,
159
- credits: extractCredits(validation.body),
160
- }), 1);
161
- }
162
-
163
- const published = await postPublish(message, deps);
164
- if (!published.ok) {
165
- const reason = publishReasonFromResponse(published.status, published.body, published.reason);
166
- return write(publishFailure(reason, publishReasonMessage(reason), {
167
- retryable: publishRetryable(reason),
168
- mode: 'publish',
169
- gates: bundle.gates,
170
- assets: bundle.assets,
171
- credits: extractCredits(published.body),
172
- }), 1);
173
- }
174
- const decision = publishDecision(published.body);
175
- if (decision === 'quarantine') {
176
- return write(publishFailure('quality_gate_failed', publishReasonMessage('quality_gate_failed'), {
177
- retryable: false,
178
- mode: 'publish',
179
- gates: Object.assign({}, bundle.gates, { quality: 'fail' }),
180
- assets: bundle.assets,
181
- credits: extractCredits(published.body),
182
- }), 1);
183
- }
184
- const body = record(published.body) || {};
185
- const payload = record(body.payload) || body;
186
- const status = normalizePublishStatus(published.body);
187
- const receiptId = stringField(payload, 'receipt_id');
188
- const bundleId = stringField(payload, 'bundle_id');
189
- const credits = extractCredits(published.body) || creditsFromPayload(payload);
190
- if (!status) {
191
- return write(publishFailure('internal_error', 'Hub publish response missing lifecycle status', {
192
- retryable: false,
193
- mode: 'publish',
194
- gates: bundle.gates,
195
- assets: bundle.assets,
196
- credits,
197
- }), 1);
198
- }
199
- return write({
200
- ok: true,
201
- contract: PUBLISH_CONTRACT,
202
- mode: 'publish',
203
- ...(status ? { status } : {}),
204
- reversibility: REVERSIBILITY,
205
- ...(receiptId ? { receipt_id: receiptId } : {}),
206
- ...(bundleId ? { bundle_id: bundleId } : {}),
207
- assets: bundle.assets,
208
- ...(credits ? { credits } : {}),
209
- }, 0);
210
- } catch (e) {
211
- const f = classifyError(e, 'publish');
212
- return write(publishFailure(f.reason, f.message, {
213
- retryable: f.retryable,
214
- mode: parsed.dryRun ? 'dry_run' : 'publish',
215
- }), 1);
216
- }
217
- });
218
- }
219
-
220
- function parseReuseArgs(args) {
221
- let id = null;
222
- let jsonOut = false;
223
- for (let i = 0; i < args.length; i++) {
224
- const token = args[i];
225
- if (!token) continue;
226
- if (token === '--json') { jsonOut = true; continue; }
227
- if (token === '--id') {
228
- const next = args[i + 1];
229
- if (!next || String(next).startsWith('--')) return { ok: false, reason: 'missing_id', message: 'reuse requires --id <asset_id>' };
230
- id = String(next).trim();
231
- i++;
232
- continue;
233
- }
234
- if (typeof token === 'string' && token.startsWith('--id=')) {
235
- id = token.slice('--id='.length).trim();
236
- continue;
237
- }
238
- if (String(token).startsWith('--')) return { ok: false, reason: 'unsupported', message: 'unsupported reuse flag' };
239
- return { ok: false, reason: 'unsupported', message: 'unsupported reuse argument' };
240
- }
241
- if (!jsonOut) return { ok: false, reason: 'unsupported', message: 'reuse requires --json' };
242
- if (!id) return { ok: false, reason: 'missing_id', message: 'reuse requires --id <asset_id>' };
243
- if (id.length > 200) return { ok: false, reason: 'missing_id', message: 'asset id must be <= 200 characters' };
244
- return { ok: true, assetId: id, jsonOut };
245
- }
246
-
247
- function parsePublishArgs(args) {
248
- const assetRefs = [];
249
- let dryRun = false;
250
- let jsonOut = false;
251
- for (let i = 0; i < args.length; i++) {
252
- const token = args[i];
253
- if (!token) continue;
254
- if (token === '--dry-run') { dryRun = true; continue; }
255
- if (token === '--json') { jsonOut = true; continue; }
256
- const equalFlag = ASSET_FLAG_LIST.find(flag => token.startsWith(flag + '='));
257
- if (equalFlag) {
258
- const value = token.slice(equalFlag.length + 1).trim();
259
- if (!value) return { ok: false, reason: 'bundle_required', message: equalFlag + ' requires a value' };
260
- assetRefs.push(value);
261
- continue;
262
- }
263
- if (ASSET_FLAGS.has(token)) {
264
- const next = args[i + 1];
265
- if (!next || String(next).startsWith('--')) return { ok: false, reason: 'bundle_required', message: token + ' requires a value' };
266
- assetRefs.push(String(next).trim());
267
- i++;
268
- continue;
269
- }
270
- if (!String(token).startsWith('--')) return { ok: false, reason: 'unsupported', message: 'unsupported publish argument' };
271
- return { ok: false, reason: 'unsupported', message: 'unsupported publish flag' };
272
- }
273
- if (!jsonOut) return { ok: false, reason: 'unsupported', message: 'publish requires --json' };
274
- const refs = assetRefs.filter(Boolean);
275
- if (refs.length === 0) return { ok: false, reason: 'bundle_required', message: 'publish requires --asset <id|path>' };
276
- if (refs.length > MAX_ASSETS) return { ok: false, reason: 'bundle_required', message: 'publish supports at most ' + MAX_ASSETS + ' assets' };
277
- return { ok: true, assetRefs: refs, dryRun, jsonOut };
278
- }
279
-
280
- function writeBlockedPublishResult(bundle, parsed, write) {
281
- if (!bundle.blockReasons || bundle.blockReasons.length === 0) return null;
282
- if (parsed.dryRun) return write(dryRunEnvelope(bundle), 0);
283
- const reason = bundle.blockReasons[0] || 'internal_error';
284
- return write(publishFailure(reason, publishReasonMessage(reason), {
285
- retryable: false,
286
- mode: 'publish',
287
- gates: bundle.gates,
288
- assets: bundle.assets,
289
- }), 1);
290
- }
291
-
292
- function buildPublishBundle(refs, deps) {
293
- let original;
294
- try {
295
- original = loadAssetRefs(refs, deps);
296
- } catch (e) {
297
- return { ok: false, reason: 'schema_invalid', message: 'asset schema is invalid', gates: { schema: 'fail' } };
298
- }
299
- const bundleCheck = checkBundle(original);
300
- if (!bundleCheck.ok) return { ok: false, reason: 'bundle_required', message: bundleCheck.message, gates: { schema: 'pass', bundle: 'fail' } };
301
- let sanitized;
302
- try {
303
- sanitized = original.map(asset => {
304
- const clean = sanitizeForContract(asset, deps);
305
- clean.asset_id = computeAssetId(clean);
306
- return clean;
307
- });
308
- } catch (_) {
309
- return { ok: false, reason: 'redaction_unavailable', message: 'redaction unavailable', gates: { redaction: 'unavailable' } };
310
- }
311
- const leak = leakCheck(sanitized);
312
- const blockReasons = leak.blocked ? ['leak_detected'] : [];
313
- const gates = {
314
- redaction: 'pass',
315
- leak: leak.blocked ? 'fail' : 'pass',
316
- schema: 'pass',
317
- bundle: 'pass',
318
- quality: 'pass',
319
- };
320
- return {
321
- ok: true,
322
- original,
323
- sanitized,
324
- blockReasons,
325
- gates,
326
- assets: summarizePublishAssets(sanitized),
327
- };
328
- }
329
-
330
- function buildPublishMessage(sanitized, deps, opts) {
331
- const initialAssets = sanitizePublishAssets(sanitized, deps);
332
- const message = buildPublishMessageFromAssets(initialAssets, deps, opts);
333
- return finalizePublishMessage(message, deps, opts);
334
- }
335
-
336
- function buildPublishMessageFromAssets(assets, deps, opts) {
337
- const preview = Boolean(opts && opts.preview);
338
- const a2a = deps.a2a || require('./a2aProtocol');
339
- const gene = assets.find(asset => asset.type === 'Gene');
340
- const capsule = assets.find(asset => asset.type === 'Capsule');
341
- const event = assets.find(asset => asset.type === 'EvolutionEvent') || null;
342
- if (!gene || !capsule) throw new ContractError('bundle_required', 'publish requires Gene + Capsule bundle');
343
- if (preview) {
344
- const previewNodeId = nonPersistedNodeId(deps) || DRY_RUN_NODE_ID;
345
- try {
346
- return a2a.buildPublishBundle({ gene, capsule, event, nodeId: previewNodeId });
347
- } catch (e) {
348
- if (!isPublishSigningAuthError(e)) throw e;
349
- if (!hasHubAuthorization(deps)) throw new ContractError('auth_required', 'Hub authentication required');
350
- return buildUnsignedPublishPreviewMessage({ gene, capsule, event, nodeId: previewNodeId });
351
- }
352
- }
353
- if (!getHubUrl(deps)) throw new ContractError('auth_required', 'Hub URL is required');
354
- if (!hasHubAuthorization(deps)) throw new ContractError('auth_required', 'Hub authentication required');
355
- try {
356
- return a2a.buildPublishBundle({ gene, capsule, event });
357
- } catch (e) {
358
- if (!isPublishSigningAuthError(e)) throw e;
359
- return buildUnsignedPublishPreviewMessage({ gene, capsule, event, nodeId: nonPersistedNodeId(deps) || DRY_RUN_NODE_ID });
360
- }
361
- }
362
-
363
- function finalizePublishMessage(message, deps, opts) {
364
- const signedAssets = publishPayloadAssets(message);
365
- const finalAssets = sanitizePublishAssets(signedAssets, deps);
366
- if (sameJson(finalAssets, signedAssets)) return message;
367
- const resigned = buildPublishMessageFromAssets(finalAssets, deps, opts);
368
- const resignedAssets = publishPayloadAssets(resigned);
369
- const finalResignedAssets = sanitizePublishAssets(resignedAssets, deps);
370
- if (!sameJson(finalResignedAssets, resignedAssets)) {
371
- throw new ContractError('redaction_unavailable', 'redaction unavailable');
372
- }
373
- return resigned;
374
- }
375
-
376
- function buildUnsignedPublishPreviewMessage(opts) {
377
- const assets = [opts.gene, opts.capsule]
378
- .concat(opts.event ? [opts.event] : [])
379
- .filter(Boolean)
380
- .map(asset => {
381
- const copy = cloneJson(asset);
382
- copy.asset_id = computeAssetId(copy);
383
- return copy;
384
- });
385
- return {
386
- protocol: 'gep-a2a',
387
- protocol_version: '1.0.0',
388
- message_type: 'publish',
389
- message_id: 'msg_preview_' + Date.now(),
390
- sender_id: opts.nodeId || DRY_RUN_NODE_ID,
391
- timestamp: new Date().toISOString(),
392
- payload: { assets },
393
- };
394
- }
395
-
396
- function nonPersistedNodeId(deps) {
397
- if (deps && typeof deps.nodeId === 'string' && deps.nodeId.trim()) return deps.nodeId.trim();
398
- if (process.env.A2A_NODE_ID && String(process.env.A2A_NODE_ID).trim()) {
399
- return String(process.env.A2A_NODE_ID).trim();
400
- }
401
- const homeNodeId = readNodeIdFile(path.join(process.env.EVOLVER_HOME || path.join(osHomedir(), '.evomap'), 'node_id'));
402
- if (homeNodeId) return homeNodeId;
403
- return readNodeIdFile(path.resolve(__dirname, '..', '..', '.evomap_node_id'));
404
- }
405
-
406
- function readNodeIdFile(filePath) {
407
- try {
408
- if (!fs.existsSync(filePath)) return '';
409
- return fs.readFileSync(filePath, 'utf8').trim();
410
- } catch (_) {
411
- return '';
412
- }
413
- }
414
-
415
- function osHomedir() {
416
- try { return require('os').homedir(); } catch (_) { return ''; }
417
- }
418
-
419
- function isPublishSigningAuthError(error) {
420
- const msg = String(error && error.message || error || '');
421
- return /node_secret|signing|authentication required|Hub URL is required/i.test(msg);
422
- }
423
-
424
- function cloneJson(value) {
425
- return JSON.parse(JSON.stringify(value));
426
- }
427
-
428
- function loadAssetRef(ref, deps) {
429
- if (looksLikeFile(ref)) return normalizeAsset(JSON.parse(fs.readFileSync(path.resolve(ref), 'utf8')));
430
- const all = deps.noAssetStoreInit && !deps.assetStore
431
- ? loadLocalAssetsReadOnly(deps)
432
- : loadLocalAssetsFromStore(deps);
433
- const found = all.find(asset => asset && (asset.asset_id === ref || asset.id === ref));
434
- if (!found) throw new ContractError('schema_invalid', 'asset not found: ' + ref);
435
- return normalizeAsset(found);
436
- }
437
-
438
- function loadAssetRefs(refs, deps) {
439
- const all = needsLocalAssetLookup(refs)
440
- ? (deps.noAssetStoreInit && !deps.assetStore ? loadLocalAssetsReadOnly(deps) : loadLocalAssetsFromStore(deps))
441
- : [];
442
- return refs.map(ref => loadAssetRefFromLookup(ref, all));
443
- }
444
-
445
- function needsLocalAssetLookup(refs) {
446
- return (refs || []).some(ref => !looksLikeFile(ref));
447
- }
448
-
449
- function loadAssetRefFromLookup(ref, all) {
450
- if (looksLikeFile(ref)) return normalizeAsset(JSON.parse(fs.readFileSync(path.resolve(ref), 'utf8')));
451
- const found = all.find(asset => asset && (asset.asset_id === ref || asset.id === ref));
452
- if (!found) throw new ContractError('schema_invalid', 'asset not found: ' + ref);
453
- return normalizeAsset(found);
454
- }
455
-
456
- function loadLocalAssetsFromStore(deps) {
457
- const store = deps.assetStore || require('./assetStore');
458
- const genes = typeof store.loadGenes === 'function' ? (store.loadGenes() || []) : [];
459
- const capsules = typeof store.loadCapsules === 'function' ? (store.loadCapsules() || []) : [];
460
- const events = typeof store.readAllEvents === 'function' ? (store.readAllEvents() || []) : [];
461
- return genes.concat(capsules, events);
462
- }
463
-
464
- function loadLocalAssetsReadOnly(deps) {
465
- const dir = deps.assetsDir || getGepAssetsDir();
466
- return []
467
- .concat(readJsonArray(path.join(dir, 'genes.json'), 'genes'))
468
- .concat(readJsonLines(path.join(dir, 'genes.jsonl'), 'Gene'))
469
- .concat(readJsonArray(path.join(dir, 'capsules.json'), 'capsules'))
470
- .concat(readJsonLines(path.join(dir, 'capsules.jsonl'), 'Capsule'))
471
- .concat(readJsonLines(path.join(dir, 'events.jsonl'), 'EvolutionEvent'));
472
- }
473
-
474
- function readJsonArray(filePath, key) {
475
- try {
476
- if (!fs.existsSync(filePath)) return [];
477
- const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
478
- const rows = key ? parsed && parsed[key] : parsed;
479
- return Array.isArray(rows) ? rows.filter(record) : [];
480
- } catch (e) {
481
- throw new ContractError('schema_invalid', 'asset schema is invalid');
482
- }
483
- }
484
-
485
- function readJsonLines(filePath, type) {
486
- try {
487
- if (!fs.existsSync(filePath)) return [];
488
- return fs.readFileSync(filePath, 'utf8')
489
- .split('\n')
490
- .map(line => line.trim())
491
- .filter(Boolean)
492
- .map(line => JSON.parse(line))
493
- .filter(asset => record(asset) && (!type || asset.type === type));
494
- } catch (e) {
495
- throw new ContractError('schema_invalid', 'asset schema is invalid');
496
- }
497
- }
498
-
499
- function normalizeAsset(value) {
500
- if (!value || typeof value !== 'object' || Array.isArray(value)) throw new ContractError('schema_invalid', 'asset is not an object');
501
- const type = canonicalAssetType(value.type);
502
- if (!type) throw new ContractError('schema_invalid', 'asset type must be Gene, Capsule, or EvolutionEvent');
503
- return Object.assign({}, value, { type, asset_id: value.asset_id || 'IGNORED' });
504
- }
505
-
506
- function checkBundle(bundle) {
507
- const genes = bundle.filter(asset => asset.type === 'Gene');
508
- const capsules = bundle.filter(asset => asset.type === 'Capsule');
509
- const events = bundle.filter(asset => asset.type === 'EvolutionEvent');
510
- if (genes.length > 1 || capsules.length > 1 || events.length > 1) {
511
- return { ok: false, message: 'publish supports one Gene + one Capsule + optional one EvolutionEvent bundle' };
512
- }
513
- if (genes.length === 0 || capsules.length === 0) return { ok: false, message: 'publish requires Gene + Capsule bundle' };
514
- for (const gene of genes) {
515
- const ids = new Set([gene.asset_id, gene.id].filter(Boolean));
516
- const paired = capsules.some(capsule => ids.has(capsule.gene));
517
- if (!paired) return { ok: false, message: 'gene must publish with its capsule' };
518
- }
519
- return { ok: true };
520
- }
521
-
522
- function leakCheck(bundle) {
523
- const result = fullLeakCheck(JSON.stringify(bundle));
524
- const hardLeaks = (result.leaks || []).filter(leak => leak && leak.type !== 'local_path');
525
- return { blocked: hardLeaks.length > 0 };
526
- }
527
-
528
- function verifyReuseAssetId(asset, requestedId) {
529
- if (!asset || asset.asset_id !== requestedId) return { ok: false };
530
- return { ok: computeAssetId(asset) === requestedId };
531
- }
532
-
533
- function isReuseAssetStoreStable(asset) {
534
- return Boolean(asset && asset.schema_version);
535
- }
536
-
537
- async function fetchAssetById(assetId, deps) {
538
- const hubUrl = getHubUrl(deps);
539
- if (!hubUrl) throw new ContractError('auth_required', 'Hub URL is required');
540
- if (!hasHubAuthorization(deps)) throw new ContractError('auth_required', 'Hub authentication required');
541
- const a2a = deps.a2a || require('./a2aProtocol');
542
- const message = a2a.buildFetch({ assetIds: [assetId] });
543
- const posted = await postEnvelope('/a2a/fetch', message, deps);
544
- if (!posted.ok) {
545
- const stableReason = stableHubReason(posted.body, REUSE_FAILURE_REASONS);
546
- if (stableReason === 'unsupported' || stableReason === 'cli_unavailable') {
547
- throw new ContractError(stableReason, reuseReasonMessage(stableReason));
548
- }
549
- if (posted.status === 401 || posted.status === 403) throw new ContractError('auth_required', 'Hub authentication required');
550
- if (posted.status === 404) return null;
551
- throw new ContractError('network_error', 'Hub fetch failed');
552
- }
553
- const assets = assetsFromBody(posted.body);
554
- return assets.find(asset => asset && asset.asset_id === assetId) || null;
555
- }
556
-
557
- async function postValidate(message, deps) {
558
- if (deps.validate) return normalizeValidateResult(await deps.validate(message));
559
- const validateMessage = Object.assign({}, message, { message_type: 'validate' });
560
- const result = await postEnvelope('/a2a/validate', validateMessage, deps);
561
- return normalizeValidateResult(result);
562
- }
563
-
564
- function normalizeValidateResult(result) {
565
- const raw = record(result) || {};
566
- const status = typeof raw.status === 'number' ? raw.status : (raw.ok ? 200 : 0);
567
- const body = raw.body;
568
- if (!raw.ok) {
569
- return {
570
- ok: false,
571
- status,
572
- reason: stableReason(raw.reason, PUBLISH_FAILURE_REASONS) || hubReason(body) || ('hub ' + status),
573
- body,
574
- };
575
- }
576
- const payload = record(body && body.payload);
577
- const passed = Boolean(payload && (payload.valid === true || payload.ok === true));
578
- return {
579
- ok: passed,
580
- status,
581
- reason: stringField(payload, 'reason') || stringField(payload, 'error'),
582
- body,
583
- };
584
- }
585
-
586
- async function postPublish(message, deps) {
587
- if (deps.publish) return deps.publish(message);
588
- const result = await postEnvelope('/a2a/publish', message, deps);
589
- if (!result.ok) return { ok: false, status: result.status, reason: hubReason(result.body) || ('hub ' + result.status), body: result.body };
590
- return { ok: true, status: result.status, body: result.body };
591
- }
592
-
593
- async function postEnvelope(endpointPath, message, deps) {
594
- const hubUrl = getHubUrl(deps);
595
- if (!hubUrl) throw new ContractError('auth_required', 'Hub URL is required');
596
- const a2a = deps.a2a || require('./a2aProtocol');
597
- const headers = buildEnvelopeHeaders(endpointPath, deps, a2a);
598
- if (!hasAuthorizationHeader(headers)) throw new ContractError('auth_required', 'Hub authentication required');
599
- const fetchImpl = deps.hubFetch || require('./hubFetch').hubFetch;
600
- const timeoutMs = deps.timeoutMs || 30000;
601
- const endpoint = hubUrl.replace(/\/+$/, '') + endpointPath;
602
- try {
603
- const res = await fetchImpl(endpoint, {
604
- method: 'POST',
605
- headers,
606
- body: JSON.stringify(message),
607
- signal: AbortSignal.timeout(timeoutMs),
608
- });
609
- const body = await safeJson(res);
610
- return { ok: Boolean(res && res.ok), status: res && res.status || 0, body };
611
- } catch (e) {
612
- return { ok: false, status: 0, body: { error: 'network_error' } };
613
- }
614
- }
615
-
616
- async function safeJson(res) {
617
- if (!res) return {};
618
- if (typeof res.text === 'function') {
619
- try {
620
- const text = await res.text();
621
- try { return JSON.parse(text); } catch (_) {
622
- return text ? { error: String(text).slice(0, 200) } : {};
623
- }
624
- } catch (_) {}
625
- }
626
- if (typeof res.json === 'function') {
627
- try { return await res.json(); } catch (_) {}
628
- }
629
- return {};
630
- }
631
-
632
- function prepareReuseAssetStore(asset, deps) {
633
- const store = deps.assetStore || require('./assetStore');
634
- if (asset.type === 'Gene' && typeof store.upsertGene === 'function') {
635
- assertNoLocalReuseIdConflict(asset, store, 'Gene');
636
- return store;
637
- }
638
- if (asset.type === 'Capsule' && typeof store.upsertCapsule === 'function') {
639
- assertNoLocalReuseIdConflict(asset, store, 'Capsule');
640
- return store;
641
- }
642
- if (asset.type === 'EvolutionEvent' && typeof store.appendEventJsonl === 'function') {
643
- return store;
644
- }
645
- throw new ContractError('unsupported', 'unsupported asset type');
646
- }
647
-
648
- function storeReusedAsset(asset, deps, preparedStore) {
649
- const store = preparedStore || prepareReuseAssetStore(asset, deps);
650
- if (asset.type === 'Gene' && typeof store.upsertGene === 'function') {
651
- assertNoLocalReuseIdConflict(asset, store, 'Gene');
652
- store.upsertGene(asset);
653
- } else if (asset.type === 'Capsule' && typeof store.upsertCapsule === 'function') {
654
- assertNoLocalReuseIdConflict(asset, store, 'Capsule');
655
- store.upsertCapsule(asset);
656
- } else if (asset.type === 'EvolutionEvent' && typeof store.appendEventJsonl === 'function') {
657
- store.appendEventJsonl(asset);
658
- } else {
659
- throw new ContractError('unsupported', 'unsupported asset type');
660
- }
661
- return asset.asset_id || computeAssetId(asset);
662
- }
663
-
664
- function assertNoLocalReuseIdConflict(asset, store, type) {
665
- if (!asset || !asset.id) return;
666
- const local = findLocalAssetByTypeAndId(store, type, asset.id);
667
- if (!local) return;
668
- const incomingAssetId = asset.asset_id || computeAssetId(asset);
669
- const localAssetId = local.asset_id || computeAssetId(local);
670
- if (incomingAssetId !== localAssetId) {
671
- throw new ContractError('internal_error', 'local asset id conflict');
672
- }
673
- }
674
-
675
- function findLocalAssetByTypeAndId(store, type, id) {
676
- const loader = type === 'Gene' ? store.loadGenes : store.loadCapsules;
677
- if (typeof loader !== 'function') return null;
678
- const assets = loader.call(store) || [];
679
- return assets.find(asset => asset && asset.type === type && String(asset.id) === String(id)) || null;
680
- }
681
-
682
- function prepareHubProvenance(deps) {
683
- const dir = deps.assetsDir || getGepAssetsDir();
684
- const file = path.join(dir, 'provenance.jsonl');
685
- try {
686
- fs.mkdirSync(dir, { recursive: true });
687
- const stat = fs.existsSync(file) ? fs.statSync(file) : null;
688
- if (stat && !stat.isFile()) throw new Error('not a file');
689
- return { file, existed: Boolean(stat), size: stat ? stat.size : 0 };
690
- } catch (_) {
691
- throw new ContractError('internal_error', 'provenance write failed');
692
- }
693
- }
694
-
695
- function markHubProvenance(assetId, deps, prepared) {
696
- if (!assetId) throw new ContractError('internal_error', 'provenance write failed');
697
- const target = prepared || prepareHubProvenance(deps);
698
- const line = JSON.stringify({
699
- assetId,
700
- source: 'hub',
701
- trusted: false,
702
- at: new Date().toISOString(),
703
- }) + '\n';
704
- try {
705
- fs.appendFileSync(target.file, line, 'utf8');
706
- target.entry = line;
707
- } catch (_) {
708
- throw new ContractError('internal_error', 'provenance write failed');
709
- }
710
- }
711
-
712
- function rollbackHubProvenance(prepared) {
713
- if (!prepared || !prepared.file) return;
714
- try {
715
- if (!fs.existsSync(prepared.file)) return;
716
- const stat = fs.statSync(prepared.file);
717
- if (!stat.isFile() || stat.size < prepared.size) return;
718
- if (prepared.entry) {
719
- const fd = fs.openSync(prepared.file, 'r');
720
- try {
721
- const tailLength = stat.size - prepared.size;
722
- const buf = Buffer.alloc(tailLength);
723
- fs.readSync(fd, buf, 0, tailLength, prepared.size);
724
- if (buf.toString('utf8') !== prepared.entry) return;
725
- } finally {
726
- fs.closeSync(fd);
727
- }
728
- }
729
- fs.truncateSync(prepared.file, prepared.size);
730
- if (!prepared.existed && prepared.size === 0) {
731
- try { fs.unlinkSync(prepared.file); } catch (_) {}
732
- }
733
- } catch (_) {}
734
- }
735
-
736
- function stripHubMetadata(asset) {
737
- const out = {};
738
- for (const key of Object.keys(asset || {})) {
739
- if (!HUB_METADATA_KEYS.has(key)) out[key] = asset[key];
740
- }
741
- return out;
742
- }
743
-
744
- function dryRunEnvelope(bundle, credits) {
745
- const blockReasons = Array.isArray(bundle.blockReasons) ? bundle.blockReasons : [];
746
- const envelope = {
747
- ok: true,
748
- contract: PUBLISH_CONTRACT,
749
- mode: 'dry_run',
750
- reversibility: REVERSIBILITY,
751
- blocked: blockReasons.length > 0,
752
- block_reasons: blockReasons,
753
- assets: bundle.assets,
754
- gates: bundle.gates,
755
- ...(credits ? { credits } : {}),
756
- };
757
- if (!blockReasons.includes('leak_detected')) {
758
- envelope.payload = { assets: bundle.sanitized };
759
- }
760
- return envelope;
761
- }
762
-
763
- function syncBundleFromPublishMessage(bundle, message) {
764
- const finalAssets = publishPayloadAssets(message);
765
- if (finalAssets.length === 0) throw new ContractError('bundle_required', 'publish requires a complete asset bundle');
766
- bundle.sanitized = finalAssets;
767
- bundle.assets = summarizePublishAssets(finalAssets);
768
- const leak = leakCheck(finalAssets);
769
- if (leak.blocked && !bundle.blockReasons.includes('leak_detected')) {
770
- bundle.blockReasons.push('leak_detected');
771
- }
772
- bundle.gates.leak = leak.blocked ? 'fail' : 'pass';
773
- }
774
-
775
- function publishPayloadAssets(message) {
776
- const payload = record(message && message.payload) || {};
777
- return Array.isArray(payload.assets) ? payload.assets.filter(asset => record(asset)) : [];
778
- }
779
-
780
- function sanitizePublishAssets(assets, deps) {
781
- return (assets || []).filter(asset => record(asset)).map(asset => {
782
- const clean = sanitizeForContract(asset, deps);
783
- clean.asset_id = computeAssetId(clean);
784
- return clean;
785
- });
786
- }
787
-
788
- function summarizePublishAssets(assets) {
789
- return (assets || []).filter(asset => record(asset)).map(asset => {
790
- const out = {};
791
- const assetId = stringField(asset, 'asset_id');
792
- const type = canonicalAssetType(asset.type);
793
- if (assetId) out.asset_id = assetId;
794
- if (type) out.type = type;
795
- return out;
796
- });
797
- }
798
-
799
- function publishFailure(reason, message, opts) {
800
- opts = opts || {};
801
- return {
802
- ok: false,
803
- contract: PUBLISH_CONTRACT,
804
- ...(opts.mode ? { mode: opts.mode } : {}),
805
- ...(opts.status ? { status: opts.status } : {}),
806
- ...(opts.gates ? { gates: opts.gates } : {}),
807
- ...(opts.assets ? { assets: opts.assets } : {}),
808
- ...(opts.credits ? { credits: opts.credits } : {}),
809
- reason,
810
- retryable: Boolean(opts.retryable),
811
- message,
812
- };
813
- }
814
-
815
- function reuseFailure(reason, message) {
816
- return { ok: false, contract: REUSE_CONTRACT, reason, message };
817
- }
818
-
819
- function writeJson(out, value, code, deps, machineJson) {
820
- const line = JSON.stringify(sanitizeForContract(value, deps)) + '\n';
821
- const originalStdoutWrite = deps && deps[MACHINE_JSON_STDOUT_WRITE];
822
- if (machineJson && out === process.stdout && typeof originalStdoutWrite === 'function') {
823
- originalStdoutWrite.call(process.stdout, line);
824
- } else {
825
- out.write(line);
826
- }
827
- return code;
828
- }
829
-
830
- async function withMachineJsonConsole(enabled, deps, fn) {
831
- if (!enabled) return fn();
832
- const original = {
833
- log: console.log,
834
- info: console.info,
835
- warn: console.warn,
836
- error: console.error,
837
- debug: console.debug,
838
- stdoutWrite: process.stdout.write,
839
- stderrWrite: process.stderr.write,
840
- };
841
- const hadOriginalStdoutWrite = Object.prototype.hasOwnProperty.call(deps, MACHINE_JSON_STDOUT_WRITE);
842
- const previousOriginalStdoutWrite = deps[MACHINE_JSON_STDOUT_WRITE];
843
- const write = (...args) => {
844
- try { process.stderr.write(sanitizeText(util.format(...args), deps) + '\n'); } catch (_) {}
845
- };
846
- deps[MACHINE_JSON_STDOUT_WRITE] = original.stdoutWrite;
847
- process.stdout.write = function (chunk, encoding, callback) {
848
- const clean = sanitizeStderrChunk(chunk, encoding, deps);
849
- if (typeof encoding === 'function') return original.stderrWrite.call(process.stderr, clean, encoding);
850
- return original.stderrWrite.call(process.stderr, clean, encoding, callback);
851
- };
852
- process.stderr.write = function (chunk, encoding, callback) {
853
- const clean = sanitizeStderrChunk(chunk, encoding, deps);
854
- if (typeof encoding === 'function') return original.stderrWrite.call(process.stderr, clean, encoding);
855
- return original.stderrWrite.call(process.stderr, clean, encoding, callback);
856
- };
857
- console.log = write;
858
- console.info = write;
859
- console.warn = write;
860
- console.error = write;
861
- console.debug = write;
862
- try {
863
- return await fn();
864
- } finally {
865
- console.log = original.log;
866
- console.info = original.info;
867
- console.warn = original.warn;
868
- console.error = original.error;
869
- console.debug = original.debug;
870
- process.stdout.write = original.stdoutWrite;
871
- process.stderr.write = original.stderrWrite;
872
- if (hadOriginalStdoutWrite) {
873
- deps[MACHINE_JSON_STDOUT_WRITE] = previousOriginalStdoutWrite;
874
- } else {
875
- delete deps[MACHINE_JSON_STDOUT_WRITE];
876
- }
877
- }
878
- }
879
-
880
- function classifyError(e, command) {
881
- if (e instanceof ContractError) return { reason: e.reason, message: e.safeMessage, retryable: e.reason === 'network_error' };
882
- const msg = e && e.message ? String(e.message) : '';
883
- if (/node_secret|credential|auth|401|403/i.test(msg)) return { reason: 'auth_required', message: 'Hub authentication required', retryable: false };
884
- if (/A2A_HUB_URL|Hub URL/i.test(msg)) return { reason: 'auth_required', message: 'Hub URL is required', retryable: false };
885
- return { reason: 'internal_error', message: 'evolver ' + command + ' failed', retryable: false };
886
- }
887
-
888
- function publishReasonFromStatus(status) {
889
- if (status === 401 || status === 403) return 'auth_required';
890
- if (status === 402) return 'insufficient_credits';
891
- if (status === 429 || status >= 500 || status === 0) return 'network_error';
892
- return 'quality_gate_failed';
893
- }
894
-
895
- function publishReasonFromResponse(status, body, reason) {
896
- return stableReason(reason, PUBLISH_FAILURE_REASONS)
897
- || stableHubReason(body, PUBLISH_FAILURE_REASONS)
898
- || publishReasonFromStatus(status);
899
- }
900
-
901
- function publishRetryable(reason) {
902
- return reason === 'network_error';
903
- }
904
-
905
- function sanitizeForContract(value, deps) {
906
- return redactKnownSecrets(sanitizePayload(value), deps);
907
- }
908
-
909
- function sanitizeText(value, deps) {
910
- return redactKnownSecretsInString(redactString(String(value)), deps);
911
- }
912
-
913
- function sanitizeStderrChunk(chunk, encoding, deps) {
914
- const text = Buffer.isBuffer(chunk)
915
- ? chunk.toString(typeof encoding === 'string' ? encoding : 'utf8')
916
- : String(chunk);
917
- return sanitizeText(text, deps);
918
- }
919
-
920
- function redactKnownSecrets(value, deps, known) {
921
- const secrets = known || knownLocalSecrets(deps);
922
- if (typeof value === 'string') return redactKnownSecretsInString(value, deps, secrets);
923
- if (!value || typeof value !== 'object') return value;
924
- if (Array.isArray(value)) return value.map(item => redactKnownSecrets(item, deps, secrets));
925
- const out = {};
926
- for (const key of Object.keys(value)) {
927
- const cleanKey = uniqueObjectKey(out, sanitizeObjectKey(key, deps, secrets));
928
- out[cleanKey] = redactKnownSecrets(value[key], deps, secrets);
929
- }
930
- return out;
931
- }
932
-
933
- function sanitizeObjectKey(key, deps, secrets) {
934
- const clean = redactKnownSecretsInString(redactString(String(key)), deps, secrets);
935
- return clean || '[REDACTED]';
936
- }
937
-
938
- function uniqueObjectKey(target, key) {
939
- if (!Object.prototype.hasOwnProperty.call(target, key)) return key;
940
- let i = 2;
941
- while (Object.prototype.hasOwnProperty.call(target, key + '_' + i)) i++;
942
- return key + '_' + i;
943
- }
944
-
945
- function redactKnownSecretsInString(value, deps, known) {
946
- let result = value;
947
- const secrets = known || knownLocalSecrets(deps);
948
- for (const secret of secrets) result = result.split(secret).join('[REDACTED]');
949
- return result;
950
- }
951
-
952
- function knownLocalSecrets(deps) {
953
- const secrets = new Set();
954
- const add = value => {
955
- if (typeof value === 'string' && value.length >= 8) secrets.add(value);
956
- };
957
- if (deps) add(deps.nodeSecret);
958
- try { add(getHubNodeSecret(deps || {})); } catch (_) {}
959
- for (const [key, value] of Object.entries(process.env)) {
960
- if (/SECRET|TOKEN|API[_-]?KEY|PASSWORD|AUTH|CREDENTIAL/i.test(key)) add(value);
961
- }
962
- return Array.from(secrets).sort((a, b) => b.length - a.length);
963
- }
964
-
965
- function hasHubAuthorization(deps) {
966
- if (getHubNodeSecret(deps)) return true;
967
- const a2a = deps.a2a || require('./a2aProtocol');
968
- return hasAuthorizationHeader(buildHubHeadersSafe(a2a));
969
- }
970
-
971
- function buildEnvelopeHeaders(endpointPath, deps, a2a) {
972
- if (NODE_SCOPED_ENDPOINT_PATHS.has(endpointPath)) {
973
- return buildNodeScopedHubHeadersSafe(a2a, deps);
974
- }
975
- return buildHubHeadersSafe(a2a);
976
- }
977
-
978
- function buildNodeScopedHubHeadersSafe(a2a, deps) {
979
- if (!deps || deps.nodeSecret === undefined) {
980
- try {
981
- if (typeof a2a.buildNodeScopedHubHeaders === 'function') {
982
- const headers = a2a.buildNodeScopedHubHeaders() || {};
983
- if (hasAuthorizationHeader(headers)) return headers;
984
- }
985
- } catch (_) {}
986
- }
987
- const nodeSecret = getHubNodeSecret(deps || {});
988
- if (!nodeSecret) return {};
989
- const headers = {
990
- 'Content-Type': 'application/json',
991
- Authorization: 'Bearer ' + nodeSecret,
992
- };
993
- const secretVersion = getHubNodeSecretVersionSafe(a2a);
994
- if (secretVersion) headers['X-EvoMap-Node-Secret-Version'] = String(secretVersion);
995
- return headers;
996
- }
997
-
998
- function getHubNodeSecretVersionSafe(a2a) {
999
- try {
1000
- return typeof a2a.getHubNodeSecretVersion === 'function' ? a2a.getHubNodeSecretVersion() : null;
1001
- } catch (_) {
1002
- return null;
1003
- }
1004
- }
1005
-
1006
- function buildHubHeadersSafe(a2a) {
1007
- try {
1008
- return typeof a2a.buildHubHeaders === 'function' ? (a2a.buildHubHeaders() || {}) : {};
1009
- } catch (_) {
1010
- return {};
1011
- }
1012
- }
1013
-
1014
- function hasAuthorizationHeader(headers) {
1015
- const value = headers && (headers.Authorization || headers.authorization);
1016
- return typeof value === 'string' && /^Bearer\s+\S+$/i.test(value);
1017
- }
1018
-
1019
- function stableHubReason(body, allowed) {
1020
- return stableReason(hubReason(body), allowed);
1021
- }
1022
-
1023
- function stableReason(reason, allowed) {
1024
- const safe = safeTokenField(reason);
1025
- return safe && allowed.has(safe) ? safe : null;
1026
- }
1027
-
1028
- function canonicalAssetType(value) {
1029
- if (value === 'Gene' || value === 'gene') return 'Gene';
1030
- if (value === 'Capsule' || value === 'capsule') return 'Capsule';
1031
- if (value === 'EvolutionEvent' || value === 'event' || value === 'Evolutionevent') return 'EvolutionEvent';
1032
- return null;
1033
- }
1034
-
1035
- function assetsFromBody(body) {
1036
- const b = record(body) || {};
1037
- const p = record(b.payload) || {};
1038
- const rows = Array.isArray(p.results) ? p.results
1039
- : Array.isArray(p.assets) ? p.assets
1040
- : Array.isArray(b.results) ? b.results
1041
- : Array.isArray(b.assets) ? b.assets
1042
- : [];
1043
- return rows.filter(row => row && typeof row === 'object' && !Array.isArray(row));
1044
- }
1045
-
1046
- function extractCredits(body) {
1047
- const b = record(body) || {};
1048
- const p = record(b.payload) || b;
1049
- const c = record(p.credits) || record(p.credit_cost) || record(p.economic) || p;
1050
- return creditsFromPayload(c);
1051
- }
1052
-
1053
- function creditsFromPayload(payload) {
1054
- const required = numberField(payload, 'required');
1055
- const available = numberField(payload, 'available');
1056
- const estimated = firstNumberField(payload, ['estimated', 'estimate']);
1057
- const charged = numberField(payload, 'charged');
1058
- const balanceKind = safeTokenField(stringField(payload, 'balance_kind') || stringField(payload, 'balanceKind'));
1059
- const out = {};
1060
- if (required !== undefined) out.required = required;
1061
- if (available !== undefined) out.available = available;
1062
- if (estimated !== undefined) out.estimated = estimated;
1063
- if (charged !== undefined) out.charged = charged;
1064
- if (balanceKind) out.balance_kind = balanceKind;
1065
- return Object.keys(out).length ? out : undefined;
1066
- }
1067
-
1068
- function normalizePublishStatus(body) {
1069
- const b = record(body) || {};
1070
- const p = record(b.payload) || b;
1071
- const status = stringField(p, 'status');
1072
- if (status) {
1073
- if (status === 'queued' || status === 'accepted' || status === 'published') return status;
1074
- return null;
1075
- }
1076
- const decision = stringField(p, 'decision');
1077
- if (!decision) return null;
1078
- if (decision === 'accept') return 'accepted';
1079
- if ((decision === 'reject' || decision === 'rejected') && stringField(p, 'reason') === 'already_published') return 'published';
1080
- return null;
1081
- }
1082
-
1083
- function publishDecision(body) {
1084
- const b = record(body) || {};
1085
- const p = record(b.payload) || b;
1086
- return stringField(p, 'decision');
1087
- }
1088
-
1089
- function hubReason(body) {
1090
- const b = record(body) || {};
1091
- const p = record(b.payload) || b;
1092
- return stringField(p, 'reason') || stringField(p, 'error');
1093
- }
1094
-
1095
- function publishReasonMessage(reason) {
1096
- const map = {
1097
- redaction_unavailable: 'redaction unavailable',
1098
- leak_detected: 'leak detected after redaction',
1099
- schema_invalid: 'asset schema is invalid',
1100
- bundle_required: 'publish requires a complete asset bundle',
1101
- quality_gate_failed: 'Hub quality gate failed',
1102
- auth_required: 'Hub authentication required',
1103
- insufficient_credits: 'insufficient credits',
1104
- network_error: 'Hub unreachable',
1105
- unsupported: 'publish unsupported',
1106
- cli_unavailable: 'evolver CLI unavailable',
1107
- internal_error: 'evolver publish failed',
1108
- };
1109
- return map[reason] || map.internal_error;
1110
- }
1111
-
1112
- function reuseReasonMessage(reason) {
1113
- const map = {
1114
- missing_id: 'reuse requires --id <asset_id>',
1115
- cli_unavailable: 'evolver CLI unavailable',
1116
- auth_required: 'Hub authentication required',
1117
- not_found: 'asset not found',
1118
- network_error: 'Hub fetch failed',
1119
- unsupported: 'reuse unsupported',
1120
- internal_error: 'evolver reuse failed',
1121
- };
1122
- return map[reason] || map.internal_error;
1123
- }
1124
-
1125
- function getHubUrl(deps) {
1126
- if (deps.hubUrl) return deps.hubUrl;
1127
- const a2a = deps.a2a || require('./a2aProtocol');
1128
- return typeof a2a.getHubUrl === 'function' ? a2a.getHubUrl() : process.env.A2A_HUB_URL;
1129
- }
1130
-
1131
- function getHubNodeSecret(deps) {
1132
- if (deps.nodeSecret !== undefined) return deps.nodeSecret;
1133
- const a2a = deps.a2a || require('./a2aProtocol');
1134
- return typeof a2a.getHubNodeSecret === 'function' ? a2a.getHubNodeSecret() : process.env.A2A_NODE_SECRET;
1135
- }
1136
-
1137
- function looksLikeFile(value) {
1138
- try { return fs.existsSync(value) && fs.statSync(value).isFile(); } catch (_) { return false; }
1139
- }
1140
-
1141
- function record(value) {
1142
- return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
1143
- }
1144
-
1145
- function stringField(value, key) {
1146
- const obj = record(value);
1147
- const raw = obj && obj[key];
1148
- return typeof raw === 'string' && raw.trim() ? raw.trim() : undefined;
1149
- }
1150
-
1151
- function safeTokenField(value) {
1152
- if (!value) return undefined;
1153
- return /^[A-Za-z0-9_.:-]{1,64}$/.test(value) ? value : undefined;
1154
- }
1155
-
1156
- function firstNumberField(value, keys) {
1157
- for (const key of keys) {
1158
- const n = numberField(value, key);
1159
- if (n !== undefined) return n;
1160
- }
1161
- return undefined;
1162
- }
1163
-
1164
- function numberField(value, key) {
1165
- const obj = record(value);
1166
- const raw = obj && obj[key];
1167
- const n = typeof raw === 'number' ? raw : (typeof raw === 'string' && raw.trim() ? Number(raw) : NaN);
1168
- return Number.isSafeInteger(n) ? n : undefined;
1169
- }
1170
-
1171
- function sameJson(left, right) {
1172
- return JSON.stringify(left) === JSON.stringify(right);
1173
- }
1174
-
1175
- class ContractError extends Error {
1176
- constructor(reason, safeMessage) {
1177
- super(safeMessage);
1178
- this.name = 'ContractError';
1179
- this.reason = reason;
1180
- this.safeMessage = safeMessage;
1181
- }
1182
- }
1183
-
1184
- module.exports = {
1185
- parseReuseArgs,
1186
- parsePublishArgs,
1187
- buildPublishBundle,
1188
- runReuseCommand,
1189
- runPublishCommand,
1190
- };