@evomap/evolver 1.29.0 → 1.29.4

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/index.js CHANGED
@@ -24,6 +24,37 @@ function readJsonSafe(p) {
24
24
  }
25
25
  }
26
26
 
27
+ function rejectPendingRun(statePath) {
28
+ try {
29
+ const { getRepoRoot } = require('./src/gep/paths');
30
+ const { execSync } = require('child_process');
31
+ const repoRoot = getRepoRoot();
32
+
33
+ execSync('git checkout -- .', { cwd: repoRoot, encoding: 'utf8', timeout: 30000 });
34
+ execSync('git clean -fd', { cwd: repoRoot, encoding: 'utf8', timeout: 30000 });
35
+ } catch (e) {
36
+ console.warn('[Loop] Pending run rollback failed: ' + (e.message || e));
37
+ }
38
+
39
+ try {
40
+ const state = readJsonSafe(statePath);
41
+ if (state && state.last_run && state.last_run.run_id) {
42
+ state.last_solidify = {
43
+ run_id: state.last_run.run_id,
44
+ rejected: true,
45
+ reason: 'loop_bridge_disabled_autoreject',
46
+ timestamp: new Date().toISOString(),
47
+ };
48
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
49
+ return true;
50
+ }
51
+ } catch (e) {
52
+ console.warn('[Loop] Failed to clear pending run state: ' + (e.message || e));
53
+ }
54
+
55
+ return false;
56
+ }
57
+
27
58
  function isPendingSolidify(state) {
28
59
  const lastRun = state && state.last_run ? state.last_run : null;
29
60
  const lastSolid = state && state.last_solidify ? state.last_solidify : null;
@@ -80,6 +111,16 @@ async function main() {
80
111
  const isLoop = args.includes('--loop') || args.includes('--mad-dog');
81
112
 
82
113
  if (command === 'run' || command === '/evolve' || isLoop) {
114
+ if (isLoop) {
115
+ const originalLog = console.log;
116
+ const originalWarn = console.warn;
117
+ const originalError = console.error;
118
+ function ts() { return '[' + new Date().toISOString() + ']'; }
119
+ console.log = (...args) => { originalLog.call(console, ts(), ...args); };
120
+ console.warn = (...args) => { originalWarn.call(console, ts(), ...args); };
121
+ console.error = (...args) => { originalError.call(console, ts(), ...args); };
122
+ }
123
+
83
124
  console.log('Starting capability evolver...');
84
125
 
85
126
  if (isLoop) {
@@ -137,6 +178,16 @@ async function main() {
137
178
  try {
138
179
  await evolve.run();
139
180
  ok = true;
181
+
182
+ if (String(process.env.EVOLVE_BRIDGE || '').toLowerCase() === 'false') {
183
+ const stAfterRun = readJsonSafe(solidifyStatePath);
184
+ if (isPendingSolidify(stAfterRun)) {
185
+ const cleared = rejectPendingRun(solidifyStatePath);
186
+ if (cleared) {
187
+ console.warn('[Loop] Auto-rejected pending run because bridge is disabled in loop mode.');
188
+ }
189
+ }
190
+ }
140
191
  } catch (error) {
141
192
  const msg = error && error.message ? String(error.message) : String(error);
142
193
  console.error(`Evolution cycle failed: ${msg}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evomap/evolver",
3
- "version": "1.29.0",
3
+ "version": "1.29.4",
4
4
  "description": "A GEP-powered self-evolution engine for AI agents. Features automated log analysis and Genome Evolution Protocol (GEP) for auditable, reusable evolution assets.",
5
5
  "main": "index.js",
6
6
  "bin": {
package/src/evolve.js CHANGED
@@ -64,7 +64,9 @@ const TODAY_LOG = path.join(MEMORY_DIR, new Date().toISOString().split('T')[0] +
64
64
  // Ensure memory directory exists so state/cache writes work.
65
65
  try {
66
66
  if (!fs.existsSync(MEMORY_DIR)) fs.mkdirSync(MEMORY_DIR, { recursive: true });
67
- } catch (e) {}
67
+ } catch (e) {
68
+ console.warn('[Evolver] Failed to create MEMORY_DIR (may cause downstream errors):', e && e.message || e);
69
+ }
68
70
 
69
71
  function formatSessionLog(jsonlContent) {
70
72
  const result = [];
@@ -1041,7 +1043,9 @@ async function run() {
1041
1043
  try {
1042
1044
  const { tryReadMemoryGraphEvents } = require('./gep/memoryGraph');
1043
1045
  taskMemoryEvents = tryReadMemoryGraphEvents(1000);
1044
- } catch {}
1046
+ } catch (e) {
1047
+ console.warn('[TaskReceiver] MemoryGraph read failed (task selection proceeds without history):', e && e.message || e);
1048
+ }
1045
1049
  const best = selectBestTask(hubTasks, taskMemoryEvents);
1046
1050
  if (best) {
1047
1051
  const alreadyClaimed = best.status === 'claimed';
@@ -1085,7 +1089,9 @@ async function run() {
1085
1089
  }
1086
1090
  }
1087
1091
  }
1088
- } catch {}
1092
+ } catch (e) {
1093
+ console.warn('[Commitment] Overdue task check failed (non-fatal):', e && e.message || e);
1094
+ }
1089
1095
 
1090
1096
  // --- Worker Pool: select task from heartbeat available_work (deferred claim) ---
1091
1097
  // Only remember the best task and inject its signals; actual claim+complete
@@ -1099,7 +1105,9 @@ async function run() {
1099
1105
  try {
1100
1106
  const { tryReadMemoryGraphEvents } = require('./gep/memoryGraph');
1101
1107
  taskMemoryEvents = tryReadMemoryGraphEvents(1000);
1102
- } catch {}
1108
+ } catch (e) {
1109
+ console.warn('[WorkerPool] MemoryGraph read failed (task selection proceeds without history):', e && e.message || e);
1110
+ }
1103
1111
  const best = selectBestTask(workerTasks, taskMemoryEvents);
1104
1112
  if (best) {
1105
1113
  activeTask = best;
@@ -1174,7 +1182,9 @@ async function run() {
1174
1182
  for (const c of newCandidates) {
1175
1183
  try {
1176
1184
  appendCandidateJsonl(c);
1177
- } catch (e) {}
1185
+ } catch (e) {
1186
+ console.warn('[Candidates] Failed to persist candidate:', e && e.message || e);
1187
+ }
1178
1188
  }
1179
1189
  const recentCandidates = readRecentCandidates(20);
1180
1190
  const capabilityCandidatesPreview = renderCandidatesPreview(recentCandidates.slice(-8), 1600);
@@ -1237,7 +1247,9 @@ async function run() {
1237
1247
  2
1238
1248
  )}\n\`\`\``;
1239
1249
  }
1240
- } catch (e) {}
1250
+ } catch (e) {
1251
+ console.warn('[ExternalCandidates] Preview build failed (non-fatal):', e && e.message || e);
1252
+ }
1241
1253
 
1242
1254
  // Search-First Evolution: query Hub for reusable solutions before local reasoning.
1243
1255
  let hubHit = null;
@@ -1425,7 +1437,9 @@ async function run() {
1425
1437
  .split('\n')
1426
1438
  .map(l => l.trim())
1427
1439
  .filter(Boolean);
1428
- } catch (e) {}
1440
+ } catch (e) {
1441
+ console.warn('[SolidifyState] Failed to read baseline untracked files:', e && e.message || e);
1442
+ }
1429
1443
 
1430
1444
  try {
1431
1445
  const out = execSync('git rev-parse HEAD', {
@@ -1436,7 +1450,9 @@ async function run() {
1436
1450
  windowsHide: true,
1437
1451
  });
1438
1452
  baselineHead = String(out || '').trim() || null;
1439
- } catch (e) {}
1453
+ } catch (e) {
1454
+ console.warn('[SolidifyState] Failed to read git HEAD:', e && e.message || e);
1455
+ }
1440
1456
 
1441
1457
  const maxFiles =
1442
1458
  selectedGene && selectedGene.constraints && Number.isFinite(Number(selectedGene.constraints.max_files))
@@ -172,20 +172,23 @@ function buildPublishBundle(opts) {
172
172
  if (!capsule || capsule.type !== 'Capsule' || !capsule.id) {
173
173
  throw new Error('publishBundle: capsule must be a valid Capsule with type and id');
174
174
  }
175
- var geneAssetId = gene.asset_id || computeAssetId(gene);
176
- var capsuleAssetId = capsule.asset_id || computeAssetId(capsule);
177
- var nodeSecret = process.env.A2A_NODE_SECRET || getNodeId();
178
- var signatureInput = [geneAssetId, capsuleAssetId].sort().join('|');
179
- var signature = crypto.createHmac('sha256', nodeSecret).update(signatureInput).digest('hex');
180
175
  if (o.modelName && typeof o.modelName === 'string') {
181
176
  gene.model_name = o.modelName;
182
177
  capsule.model_name = o.modelName;
183
178
  }
179
+ gene.asset_id = computeAssetId(gene);
180
+ capsule.asset_id = computeAssetId(capsule);
181
+ var geneAssetId = gene.asset_id;
182
+ var capsuleAssetId = capsule.asset_id;
183
+ var nodeSecret = process.env.A2A_NODE_SECRET || getNodeId();
184
+ var signatureInput = [geneAssetId, capsuleAssetId].sort().join('|');
185
+ var signature = crypto.createHmac('sha256', nodeSecret).update(signatureInput).digest('hex');
184
186
  var assets = [gene, capsule];
185
187
  if (event && event.type === 'EvolutionEvent') {
186
188
  if (o.modelName && typeof o.modelName === 'string') {
187
189
  event.model_name = o.modelName;
188
190
  }
191
+ event.asset_id = computeAssetId(event);
189
192
  assets.push(event);
190
193
  }
191
194
  var publishPayload = {
@@ -396,6 +399,7 @@ var _heartbeatStartedAt = null;
396
399
  var _heartbeatConsecutiveFailures = 0;
397
400
  var _heartbeatTotalSent = 0;
398
401
  var _heartbeatTotalFailed = 0;
402
+ var _heartbeatFpSent = false;
399
403
  var _latestAvailableWork = [];
400
404
  var _latestOverdueTasks = [];
401
405
  var _pendingCommitmentUpdates = [];
@@ -513,6 +517,16 @@ function sendHeartbeat() {
513
517
  meta.commitment_updates = _pendingCommitmentUpdates.splice(0);
514
518
  }
515
519
 
520
+ if (!_heartbeatFpSent) {
521
+ try {
522
+ var fp = captureEnvFingerprint();
523
+ if (fp && fp.evolver_version) {
524
+ meta.env_fingerprint = fp;
525
+ _heartbeatFpSent = true;
526
+ }
527
+ } catch {}
528
+ }
529
+
516
530
  if (Object.keys(meta).length > 0) {
517
531
  bodyObj.meta = meta;
518
532
  }
@@ -177,11 +177,27 @@ function readRecentCandidates(limit = 20) {
177
177
  try {
178
178
  const p = candidatesPath();
179
179
  if (!fs.existsSync(p)) return [];
180
- const raw = fs.readFileSync(p, 'utf8');
181
- const lines = raw.split('\n').map(l => l.trim()).filter(Boolean);
182
- return lines.slice(Math.max(0, lines.length - limit)).map(l => {
183
- try { return JSON.parse(l); } catch { return null; }
184
- }).filter(Boolean);
180
+ const stat = fs.statSync(p);
181
+ if (stat.size < 1024 * 1024) {
182
+ const raw = fs.readFileSync(p, 'utf8');
183
+ const lines = raw.split('\n').map(l => l.trim()).filter(Boolean);
184
+ return lines.slice(-limit).map(l => {
185
+ try { return JSON.parse(l); } catch { return null; }
186
+ }).filter(Boolean);
187
+ }
188
+ // Large file (>1MB): only read the tail to avoid OOM.
189
+ const fd = fs.openSync(p, 'r');
190
+ try {
191
+ const chunkSize = Math.min(stat.size, limit * 4096);
192
+ const buf = Buffer.alloc(chunkSize);
193
+ fs.readSync(fd, buf, 0, chunkSize, stat.size - chunkSize);
194
+ const lines = buf.toString('utf8').split('\n').map(l => l.trim()).filter(Boolean);
195
+ return lines.slice(-limit).map(l => {
196
+ try { return JSON.parse(l); } catch { return null; }
197
+ }).filter(Boolean);
198
+ } finally {
199
+ fs.closeSync(fd);
200
+ }
185
201
  } catch { return []; }
186
202
  }
187
203
 
@@ -189,11 +205,26 @@ function readRecentExternalCandidates(limit = 50) {
189
205
  try {
190
206
  const p = externalCandidatesPath();
191
207
  if (!fs.existsSync(p)) return [];
192
- const raw = fs.readFileSync(p, 'utf8');
193
- const lines = raw.split('\n').map(l => l.trim()).filter(Boolean);
194
- return lines.slice(Math.max(0, lines.length - limit)).map(l => {
195
- try { return JSON.parse(l); } catch { return null; }
196
- }).filter(Boolean);
208
+ const stat = fs.statSync(p);
209
+ if (stat.size < 1024 * 1024) {
210
+ const raw = fs.readFileSync(p, 'utf8');
211
+ const lines = raw.split('\n').map(l => l.trim()).filter(Boolean);
212
+ return lines.slice(-limit).map(l => {
213
+ try { return JSON.parse(l); } catch { return null; }
214
+ }).filter(Boolean);
215
+ }
216
+ const fd = fs.openSync(p, 'r');
217
+ try {
218
+ const chunkSize = Math.min(stat.size, limit * 4096);
219
+ const buf = Buffer.alloc(chunkSize);
220
+ fs.readSync(fd, buf, 0, chunkSize, stat.size - chunkSize);
221
+ const lines = buf.toString('utf8').split('\n').map(l => l.trim()).filter(Boolean);
222
+ return lines.slice(-limit).map(l => {
223
+ try { return JSON.parse(l); } catch { return null; }
224
+ }).filter(Boolean);
225
+ } finally {
226
+ fs.closeSync(fd);
227
+ }
197
228
  } catch { return []; }
198
229
  }
199
230
 
@@ -15,13 +15,28 @@ function captureEnvFingerprint() {
15
15
  const repoRoot = getRepoRoot();
16
16
  let pkgVersion = null;
17
17
  let pkgName = null;
18
+
19
+ // Read evolver's own package.json via __dirname so that npm-installed
20
+ // deployments report the correct evolver version. getRepoRoot() walks
21
+ // up to the nearest .git directory, which resolves to the HOST project
22
+ // when evolver is an npm dependency -- producing a wrong name/version.
23
+ const ownPkgPath = path.resolve(__dirname, '..', '..', 'package.json');
18
24
  try {
19
- const raw = fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8');
25
+ const raw = fs.readFileSync(ownPkgPath, 'utf8');
20
26
  const pkg = JSON.parse(raw);
21
27
  pkgVersion = pkg && pkg.version ? String(pkg.version) : null;
22
28
  pkgName = pkg && pkg.name ? String(pkg.name) : null;
23
29
  } catch (e) {}
24
30
 
31
+ if (!pkgVersion) {
32
+ try {
33
+ const raw = fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8');
34
+ const pkg = JSON.parse(raw);
35
+ pkgVersion = pkg && pkg.version ? String(pkg.version) : null;
36
+ pkgName = pkg && pkg.name ? String(pkg.name) : null;
37
+ } catch (e) {}
38
+ }
39
+
25
40
  const region = (process.env.EVOLVER_REGION || '').trim().toLowerCase().slice(0, 5) || undefined;
26
41
 
27
42
  return {
package/src/gep/paths.js CHANGED
@@ -6,16 +6,33 @@ function getRepoRoot() {
6
6
  return process.env.EVOLVER_REPO_ROOT;
7
7
  }
8
8
 
9
- let dir = path.resolve(__dirname, '..', '..');
9
+ const ownDir = path.resolve(__dirname, '..', '..');
10
+
11
+ // Safety: check evolver's own directory first to prevent operating on a
12
+ // parent repo that happens to contain .git (which could cause data loss
13
+ // when git reset --hard runs in the wrong scope).
14
+ if (fs.existsSync(path.join(ownDir, '.git'))) {
15
+ return ownDir;
16
+ }
17
+
18
+ let dir = path.dirname(ownDir);
10
19
  while (dir !== '/' && dir !== '.') {
11
- const gitDir = path.join(dir, '.git');
12
- if (fs.existsSync(gitDir)) {
13
- return dir;
20
+ if (fs.existsSync(path.join(dir, '.git'))) {
21
+ if (process.env.EVOLVER_USE_PARENT_GIT === 'true') {
22
+ console.warn('[evolver] Using parent git repository at:', dir);
23
+ return dir;
24
+ }
25
+ console.warn(
26
+ '[evolver] Detected .git in parent directory', dir,
27
+ '-- ignoring. Set EVOLVER_USE_PARENT_GIT=true to override,',
28
+ 'or EVOLVER_REPO_ROOT to specify the target directory explicitly.'
29
+ );
30
+ return ownDir;
14
31
  }
15
32
  dir = path.dirname(dir);
16
33
  }
17
34
 
18
- return path.resolve(__dirname, '..', '..');
35
+ return ownDir;
19
36
  }
20
37
 
21
38
  function getWorkspaceRoot() {
@@ -223,7 +223,7 @@ function buildDistillationPrompt(analysis, existingGenes, sampleCapsules) {
223
223
  });
224
224
 
225
225
  return [
226
- 'You are a Gene synthesis engine for the GEP (Genome Evolution Protocol).',
226
+ 'You are a Gene synthesis engine for the GEP (Gene Expression Protocol).',
227
227
  '',
228
228
  'Analyze the following successful evolution capsules and extract a reusable Gene.',
229
229
  '',
@@ -476,6 +476,22 @@ function completeDistillation(responseText) {
476
476
  try { if (request.prompt_path) fs.unlinkSync(request.prompt_path); } catch (e) {}
477
477
 
478
478
  console.log('[Distiller] Distillation complete. New gene: ' + gene.id);
479
+
480
+ if (process.env.SKILL_AUTO_PUBLISH !== '0') {
481
+ try {
482
+ var skillPublisher = require('./skillPublisher');
483
+ skillPublisher.publishSkillToHub(gene).then(function (res) {
484
+ if (res.ok) {
485
+ console.log('[Distiller] Skill published to Hub: ' + (res.result?.skill_id || gene.id));
486
+ } else {
487
+ console.warn('[Distiller] Skill publish failed: ' + (res.error || 'unknown'));
488
+ }
489
+ }).catch(function () {});
490
+ } catch (e) {
491
+ console.warn('[Distiller] Skill publisher unavailable: ' + e.message);
492
+ }
493
+ }
494
+
479
495
  return { ok: true, gene: gene };
480
496
  }
481
497
 
@@ -0,0 +1,163 @@
1
+ 'use strict';
2
+
3
+ var { getHubUrl, buildHubHeaders, getNodeId } = require('./a2aProtocol');
4
+
5
+ /**
6
+ * Convert a Gene object into SKILL.md format (Claude/Anthropic style).
7
+ *
8
+ * @param {object} gene - Gene asset
9
+ * @returns {string} SKILL.md content
10
+ */
11
+ function geneToSkillMd(gene) {
12
+ var name = (gene.id || 'unnamed-skill').replace(/^gene_distilled_/, '').replace(/_/g, '-');
13
+ var desc = gene.summary || 'AI agent skill distilled from evolution experience.';
14
+
15
+ var lines = [
16
+ '---',
17
+ 'name: ' + name,
18
+ 'description: ' + desc,
19
+ '---',
20
+ '',
21
+ '# ' + name,
22
+ '',
23
+ ];
24
+
25
+ if (gene.signals_match && gene.signals_match.length > 0) {
26
+ lines.push('## Trigger Signals');
27
+ lines.push('');
28
+ gene.signals_match.forEach(function (s) {
29
+ lines.push('- `' + s + '`');
30
+ });
31
+ lines.push('');
32
+ }
33
+
34
+ if (gene.preconditions && gene.preconditions.length > 0) {
35
+ lines.push('## Preconditions');
36
+ lines.push('');
37
+ gene.preconditions.forEach(function (p) {
38
+ lines.push('- ' + p);
39
+ });
40
+ lines.push('');
41
+ }
42
+
43
+ if (gene.strategy && gene.strategy.length > 0) {
44
+ lines.push('## Strategy');
45
+ lines.push('');
46
+ gene.strategy.forEach(function (step, i) {
47
+ lines.push((i + 1) + '. ' + step);
48
+ });
49
+ lines.push('');
50
+ }
51
+
52
+ if (gene.constraints) {
53
+ lines.push('## Constraints');
54
+ lines.push('');
55
+ if (gene.constraints.max_files) {
56
+ lines.push('- Max files: ' + gene.constraints.max_files);
57
+ }
58
+ if (gene.constraints.forbidden_paths && gene.constraints.forbidden_paths.length > 0) {
59
+ lines.push('- Forbidden paths: ' + gene.constraints.forbidden_paths.join(', '));
60
+ }
61
+ lines.push('');
62
+ }
63
+
64
+ if (gene.validation && gene.validation.length > 0) {
65
+ lines.push('## Validation');
66
+ lines.push('');
67
+ gene.validation.forEach(function (cmd) {
68
+ lines.push('```bash');
69
+ lines.push(cmd);
70
+ lines.push('```');
71
+ lines.push('');
72
+ });
73
+ }
74
+
75
+ lines.push('---');
76
+ lines.push('');
77
+ lines.push('*This Skill was generated by [Evolver](https://github.com/autogame-17/evolver) and is distributed under the [EvoMap Skill License (ESL-1.0)](https://evomap.ai/terms). Unauthorized redistribution, bulk scraping, or republishing is prohibited. See LICENSE file for full terms.*');
78
+ lines.push('');
79
+
80
+ return lines.join('\n');
81
+ }
82
+
83
+ /**
84
+ * Publish a Gene as a Skill to the Hub skill store.
85
+ *
86
+ * @param {object} gene - Gene asset
87
+ * @param {object} [opts] - { category, tags }
88
+ * @returns {Promise<{ok: boolean, result?: object, error?: string}>}
89
+ */
90
+ function publishSkillToHub(gene, opts) {
91
+ opts = opts || {};
92
+ var hubUrl = getHubUrl();
93
+ if (!hubUrl) return Promise.resolve({ ok: false, error: 'no_hub_url' });
94
+
95
+ var content = geneToSkillMd(gene);
96
+ var nodeId = getNodeId();
97
+ var skillId = 'skill_' + (gene.id || 'unnamed').replace(/^gene_/, '');
98
+
99
+ var body = {
100
+ sender_id: nodeId,
101
+ skill_id: skillId,
102
+ content: content,
103
+ category: opts.category || gene.category || null,
104
+ tags: opts.tags || gene.signals_match || [],
105
+ };
106
+
107
+ var endpoint = hubUrl.replace(/\/+$/, '') + '/a2a/skill/store/publish';
108
+
109
+ return fetch(endpoint, {
110
+ method: 'POST',
111
+ headers: buildHubHeaders(),
112
+ body: JSON.stringify(body),
113
+ signal: AbortSignal.timeout(15000),
114
+ })
115
+ .then(function (res) { return res.json().then(function (data) { return { status: res.status, data: data }; }); })
116
+ .then(function (result) {
117
+ if (result.status === 201 || result.status === 200) {
118
+ return { ok: true, result: result.data };
119
+ }
120
+ if (result.status === 409) {
121
+ return updateSkillOnHub(nodeId, skillId, content, opts, gene);
122
+ }
123
+ return { ok: false, error: result.data?.error || 'publish_failed', status: result.status };
124
+ })
125
+ .catch(function (err) {
126
+ return { ok: false, error: err.message };
127
+ });
128
+ }
129
+
130
+ /**
131
+ * Update an existing Skill on the Hub (new version).
132
+ */
133
+ function updateSkillOnHub(nodeId, skillId, content, opts, gene) {
134
+ var hubUrl = getHubUrl();
135
+ if (!hubUrl) return Promise.resolve({ ok: false, error: 'no_hub_url' });
136
+
137
+ var body = {
138
+ sender_id: nodeId,
139
+ skill_id: skillId,
140
+ content: content,
141
+ category: opts.category || gene.category || null,
142
+ tags: opts.tags || gene.signals_match || [],
143
+ changelog: 'Iterative evolution update',
144
+ };
145
+
146
+ var endpoint = hubUrl.replace(/\/+$/, '') + '/a2a/skill/store/update';
147
+
148
+ return fetch(endpoint, {
149
+ method: 'PUT',
150
+ headers: buildHubHeaders(),
151
+ body: JSON.stringify(body),
152
+ signal: AbortSignal.timeout(15000),
153
+ })
154
+ .then(function (res) { return res.json(); })
155
+ .then(function (data) { return { ok: true, result: data }; })
156
+ .catch(function (err) { return { ok: false, error: err.message }; });
157
+ }
158
+
159
+ module.exports = {
160
+ geneToSkillMd: geneToSkillMd,
161
+ publishSkillToHub: publishSkillToHub,
162
+ updateSkillOnHub: updateSkillOnHub,
163
+ };