()
| 23 | } |
| 24 | |
| 25 | function main() { |
| 26 | var args = parseArgs(process.argv.slice(2)); |
| 27 | var id = String(args.kv.get('id') || '').trim(); |
| 28 | var typeRaw = String(args.kv.get('type') || '').trim().toLowerCase(); |
| 29 | var validated = args.flags.has('validated') || String(args.kv.get('validated') || '') === 'true'; |
| 30 | var limit = Number.isFinite(Number(args.kv.get('limit'))) ? Number(args.kv.get('limit')) : 500; |
| 31 | |
| 32 | if (!id || !typeRaw) throw new Error('Usage: node scripts/a2a_promote.js --type capsule|gene|event --id <id> --validated'); |
| 33 | if (!validated) throw new Error('Refusing to promote without --validated (local verification must be done first).'); |
| 34 | |
| 35 | var type = typeRaw === 'capsule' ? 'Capsule' : typeRaw === 'gene' ? 'Gene' : typeRaw === 'event' ? 'EvolutionEvent' : ''; |
| 36 | if (!type) throw new Error('Invalid --type. Use capsule, gene, or event.'); |
| 37 | |
| 38 | var external = assetStore.readRecentExternalCandidates(limit); |
| 39 | var candidate = null; |
| 40 | for (var i = 0; i < external.length; i++) { |
| 41 | if (external[i] && external[i].type === type && String(external[i].id) === id) { candidate = external[i]; break; } |
| 42 | } |
| 43 | if (!candidate) throw new Error('Candidate not found in external zone: type=' + type + ' id=' + id); |
| 44 | |
| 45 | if (type === 'Gene') { |
| 46 | var validation = Array.isArray(candidate.validation) ? candidate.validation : []; |
| 47 | for (var j = 0; j < validation.length; j++) { |
| 48 | var c = String(validation[j] || '').trim(); |
| 49 | if (!c) continue; |
| 50 | if (!solidifyMod.isValidationCommandAllowed(c)) { |
| 51 | throw new Error('Refusing to promote Gene ' + id + ': validation command rejected by safety check: "' + c + '". Only node/npm/npx commands without shell operators are allowed.'); |
| 52 | } |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | var promoted = JSON.parse(JSON.stringify(candidate)); |
| 57 | if (!promoted.a2a || typeof promoted.a2a !== 'object') promoted.a2a = {}; |
| 58 | promoted.a2a.status = 'promoted'; |
| 59 | promoted.a2a.promoted_at = new Date().toISOString(); |
| 60 | if (!promoted.schema_version) promoted.schema_version = contentHash.SCHEMA_VERSION; |
| 61 | promoted.asset_id = contentHash.computeAssetId(promoted); |
| 62 | |
| 63 | var emitDecisions = process.env.A2A_EMIT_DECISIONS === 'true'; |
| 64 | |
| 65 | if (type === 'EvolutionEvent') { |
| 66 | assetStore.appendEventJsonl(promoted); |
| 67 | if (emitDecisions) { |
| 68 | try { |
| 69 | var dmEv = a2aProto.buildDecision({ assetId: promoted.asset_id, localId: id, decision: 'accept', reason: 'event promoted for provenance tracking' }); |
| 70 | a2aProto.getTransport().send(dmEv); |
| 71 | } catch (e) {} |
| 72 | } |
| 73 | process.stdout.write('promoted_event=' + id + '\n'); |
| 74 | return; |
| 75 | } |
| 76 | |
| 77 | if (type === 'Capsule') { |
| 78 | assetStore.appendCapsule(promoted); |
| 79 | if (emitDecisions) { |
| 80 | try { |
| 81 | var dm = a2aProto.buildDecision({ assetId: promoted.asset_id, localId: id, decision: 'accept', reason: 'capsule promoted after validation' }); |
| 82 | a2aProto.getTransport().send(dm); |
no test coverage detected