@evomap/evolver 1.87.1 → 1.87.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/README.ja-JP.md +1 -1
- package/README.ko-KR.md +1 -1
- package/README.md +9 -8
- package/README.zh-CN.md +9 -8
- package/index.js +30 -11
- package/package.json +1 -1
- package/scripts/build_binaries.js +31 -7
- package/src/atp/atpExecute.js +35 -8
- package/src/atp/autoBuyer.js +155 -21
- package/src/atp/autoDeliver.js +16 -0
- package/src/atp/cli.js +98 -0
- package/src/atp/cliAutobuyPrompt.js +57 -64
- package/src/atp/hubClient.js +42 -4
- package/src/evolve/guards.js +1 -1
- package/src/evolve/pipeline/collect.js +1 -1
- package/src/evolve/pipeline/dispatch.js +1 -1
- package/src/evolve/pipeline/enrich.js +1 -1
- package/src/evolve/pipeline/hub.js +1 -1
- package/src/evolve/pipeline/select.js +1 -1
- package/src/evolve/pipeline/signals.js +1 -1
- package/src/evolve/utils.js +1 -1
- package/src/evolve.js +1 -1
- package/src/forceUpdate.js +2 -1
- package/src/gep/a2aProtocol.js +1 -1
- package/src/gep/assetStore.js +52 -5
- package/src/gep/candidateEval.js +1 -1
- package/src/gep/candidates.js +1 -1
- package/src/gep/contentHash.js +1 -1
- package/src/gep/crypto.js +1 -1
- package/src/gep/curriculum.js +1 -1
- package/src/gep/deviceId.js +1 -1
- package/src/gep/envFingerprint.js +1 -1
- package/src/gep/epigenetics.js +1 -1
- package/src/gep/explore.js +1 -1
- package/src/gep/hash.js +1 -1
- package/src/gep/hubFetch.js +1 -1
- package/src/gep/hubReview.js +1 -1
- package/src/gep/hubSearch.js +1 -1
- package/src/gep/hubVerify.js +1 -1
- package/src/gep/learningSignals.js +1 -1
- package/src/gep/memoryGraph.js +1 -1
- package/src/gep/memoryGraphAdapter.js +1 -1
- package/src/gep/mutation.js +1 -1
- package/src/gep/narrativeMemory.js +1 -1
- package/src/gep/openPRRegistry.js +1 -1
- package/src/gep/paths.js +6 -2
- package/src/gep/personality.js +1 -1
- package/src/gep/policyCheck.js +1 -1
- package/src/gep/prompt.js +1 -1
- package/src/gep/recallVerifier.js +1 -1
- package/src/gep/reflection.js +1 -1
- package/src/gep/sanitize.js +57 -3
- package/src/gep/selector.js +1 -1
- package/src/gep/selfPR.js +34 -1
- package/src/gep/skill2gep.js +108 -29
- package/src/gep/skillDistiller.js +1 -1
- package/src/gep/solidify.js +1 -1
- package/src/gep/strategy.js +1 -1
- package/src/gep/workspaceKeychain.js +1 -1
- package/src/proxy/index.js +29 -9
- package/src/proxy/lifecycle/manager.js +97 -37
- package/src/proxy/router/messages_route.js +105 -5
- package/src/proxy/sync/engine.js +68 -31
package/src/atp/cli.js
CHANGED
|
@@ -118,6 +118,27 @@ function parseVerifyArgs(args) {
|
|
|
118
118
|
return { ok: true, opts: { orderId, action } };
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
// `evolver atp <enable|disable|status>` — manage autoBuyer consent ack file.
|
|
122
|
+
// Non-TTY users (daemon, CI, hooks) opt in here instead of the interactive
|
|
123
|
+
// first-run prompt. Returns { ok, opts: { sub } } or { ok: false, error }.
|
|
124
|
+
function parseAtpArgs(args) {
|
|
125
|
+
if (!Array.isArray(args) || args.length === 0) {
|
|
126
|
+
return { ok: false, error: 'atp requires <enable|disable|status>' };
|
|
127
|
+
}
|
|
128
|
+
let sub = null;
|
|
129
|
+
for (let i = 0; i < args.length; i++) {
|
|
130
|
+
const a = args[i];
|
|
131
|
+
if (typeof a === 'string' && !a.startsWith('--')) {
|
|
132
|
+
sub = a.toLowerCase();
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!sub || !['enable', 'disable', 'status'].includes(sub)) {
|
|
137
|
+
return { ok: false, error: 'atp subcommand must be enable|disable|status (got: ' + sub + ')' };
|
|
138
|
+
}
|
|
139
|
+
return { ok: true, opts: { sub } };
|
|
140
|
+
}
|
|
141
|
+
|
|
121
142
|
async function runBuy(opts, deps) {
|
|
122
143
|
const atp = (deps && deps.atp) || require('./index');
|
|
123
144
|
const consumerAgent = atp.consumerAgent;
|
|
@@ -226,6 +247,80 @@ async function runVerify(opts, deps) {
|
|
|
226
247
|
}
|
|
227
248
|
}
|
|
228
249
|
|
|
250
|
+
// Subcommand runner for `evolver atp enable|disable|status`. Writes the ack
|
|
251
|
+
// file via autoBuyer.setConsent so subsequent daemon starts pick it up. Does
|
|
252
|
+
// NOT mutate process.env — env override wins over the ack file by design,
|
|
253
|
+
// and we don't want a transient CLI invocation to silently shadow operator
|
|
254
|
+
// shell config.
|
|
255
|
+
// Detect whether an EVOLVER_ATP_AUTOBUY env override is currently set AND
|
|
256
|
+
// would supersede the ack we are about to write. Returns the effective env
|
|
257
|
+
// boolean (true=on, false=off) or null if env is unset / whitespace-only.
|
|
258
|
+
// Mirrors the trim-before-length-check rule from autoBuyer.getConsent so
|
|
259
|
+
// the CLI and the runtime agree on what "set" means (Bugbot PR #141).
|
|
260
|
+
function _envOverride() {
|
|
261
|
+
const raw = process.env.EVOLVER_ATP_AUTOBUY;
|
|
262
|
+
if (typeof raw !== 'string') return null;
|
|
263
|
+
const s = raw.trim().toLowerCase();
|
|
264
|
+
if (s.length === 0) return null;
|
|
265
|
+
return !(s === 'off' || s === '0' || s === 'false');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function runAtp(opts, deps) {
|
|
269
|
+
const autoBuyer = (deps && deps.autoBuyer) || require('./autoBuyer');
|
|
270
|
+
const log = (deps && deps.log) || console.log;
|
|
271
|
+
const err = (deps && deps.err) || console.error;
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
if (opts.sub === 'enable') {
|
|
275
|
+
const body = autoBuyer.setConsent(true);
|
|
276
|
+
log('[ATP] auto-spend ENABLED (consent recorded ' + body.acknowledged_at + ').');
|
|
277
|
+
log(' Caps: daily=' + (process.env.ATP_AUTOBUY_DAILY_CAP_CREDITS || '50') +
|
|
278
|
+
', per-order=' + (process.env.ATP_AUTOBUY_PER_ORDER_CAP_CREDITS || '10') + ' credits.');
|
|
279
|
+
log(' Disable: evolver atp disable (or EVOLVER_ATP_AUTOBUY=off)');
|
|
280
|
+
// Loud warning if an env override will silently undo the ack at
|
|
281
|
+
// runtime. Bugbot PR #141 Medium: without this the user gets a
|
|
282
|
+
// false confirmation and real credits keep flowing the wrong way.
|
|
283
|
+
const envEnabled = _envOverride();
|
|
284
|
+
if (envEnabled === false) {
|
|
285
|
+
log('');
|
|
286
|
+
log('[ATP] WARNING: EVOLVER_ATP_AUTOBUY=' + process.env.EVOLVER_ATP_AUTOBUY +
|
|
287
|
+
' is set in your environment and will OVERRIDE the ack file at runtime.');
|
|
288
|
+
log(' Auto-spend will stay OFF until you unset it (env wins over the ack file).');
|
|
289
|
+
return { exitCode: 0, data: body, envOverride: 'off' };
|
|
290
|
+
}
|
|
291
|
+
return { exitCode: 0, data: body };
|
|
292
|
+
}
|
|
293
|
+
if (opts.sub === 'disable') {
|
|
294
|
+
const body = autoBuyer.setConsent(false);
|
|
295
|
+
log('[ATP] auto-spend DISABLED (consent recorded ' + body.acknowledged_at + ').');
|
|
296
|
+
log(' Re-enable: evolver atp enable');
|
|
297
|
+
const envEnabled = _envOverride();
|
|
298
|
+
if (envEnabled === true) {
|
|
299
|
+
log('');
|
|
300
|
+
log('[ATP] WARNING: EVOLVER_ATP_AUTOBUY=' + process.env.EVOLVER_ATP_AUTOBUY +
|
|
301
|
+
' is set in your environment and will OVERRIDE the ack file at runtime.');
|
|
302
|
+
log(' Auto-spend will stay ON (and continue charging credits) until you unset it.');
|
|
303
|
+
return { exitCode: 0, data: body, envOverride: 'on' };
|
|
304
|
+
}
|
|
305
|
+
return { exitCode: 0, data: body };
|
|
306
|
+
}
|
|
307
|
+
// status
|
|
308
|
+
const consent = autoBuyer.getConsent();
|
|
309
|
+
const ackPath = autoBuyer.getAckPath();
|
|
310
|
+
log('[ATP] auto-spend: ' + (consent.enabled ? 'ENABLED' : 'DISABLED') +
|
|
311
|
+
' (source: ' + consent.source + ')');
|
|
312
|
+
log(' ack file: ' + ackPath);
|
|
313
|
+
if (consent.source === 'default') {
|
|
314
|
+
log(' Default policy (no ack file, no env override). Run `evolver atp disable`');
|
|
315
|
+
log(' to opt out, or `evolver atp enable` to record explicit opt-in.');
|
|
316
|
+
}
|
|
317
|
+
return { exitCode: 0, data: consent };
|
|
318
|
+
} catch (e) {
|
|
319
|
+
err('[ATP] atp command error: ' + (e && e.message || e));
|
|
320
|
+
return { exitCode: 1, error: String(e) };
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
229
324
|
function printUsage() {
|
|
230
325
|
return [
|
|
231
326
|
'ATP subcommands:',
|
|
@@ -234,6 +329,7 @@ function printUsage() {
|
|
|
234
329
|
' evolver orders [--role=consumer|merchant] [--status=pending|verified|disputed|settled]',
|
|
235
330
|
' [--limit=N] [--json]',
|
|
236
331
|
' evolver verify <orderId> [--action=confirm|ai_judge]',
|
|
332
|
+
' evolver atp <enable|disable|status> -- manage auto-spend consent',
|
|
237
333
|
].join('\n');
|
|
238
334
|
}
|
|
239
335
|
|
|
@@ -241,8 +337,10 @@ module.exports = {
|
|
|
241
337
|
parseBuyArgs,
|
|
242
338
|
parseOrdersArgs,
|
|
243
339
|
parseVerifyArgs,
|
|
340
|
+
parseAtpArgs,
|
|
244
341
|
runBuy,
|
|
245
342
|
runOrders,
|
|
246
343
|
runVerify,
|
|
344
|
+
runAtp,
|
|
247
345
|
printUsage,
|
|
248
346
|
};
|
|
@@ -9,56 +9,26 @@
|
|
|
9
9
|
* - ack file memory/atp-autobuy-ack.json does not exist (already decided)
|
|
10
10
|
*
|
|
11
11
|
* Outcomes:
|
|
12
|
-
* - user answers y ->
|
|
13
|
-
* - user answers n ->
|
|
14
|
-
* - user answers later -> no
|
|
12
|
+
* - user answers y -> autoBuyer.setConsent(true) — opts in for future sessions
|
|
13
|
+
* - user answers n -> autoBuyer.setConsent(false) — prompt never shown again
|
|
14
|
+
* - user answers later -> no ack written, prompt shown next session
|
|
15
15
|
* - any non-TTY/ack branch -> silent no-op
|
|
16
|
+
*
|
|
17
|
+
* setConsent failures (FS permission, disk full) are surfaced to the user
|
|
18
|
+
* via the output stream and returned as `reason: 'ack_write_failed'`; the
|
|
19
|
+
* decision field still reflects what the user typed.
|
|
16
20
|
*/
|
|
17
21
|
|
|
18
|
-
const fs = require("fs");
|
|
19
|
-
const path = require("path");
|
|
20
22
|
const readline = require("readline");
|
|
23
|
+
const autoBuyer = require("./autoBuyer");
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function _getAckPath() {
|
|
33
|
-
return path.join(_getMemoryDir(), ACK_FILE_NAME);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function _readAck() {
|
|
37
|
-
try {
|
|
38
|
-
const raw = fs.readFileSync(_getAckPath(), "utf8");
|
|
39
|
-
const parsed = JSON.parse(raw);
|
|
40
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
41
|
-
return parsed;
|
|
42
|
-
} catch (_) {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function _writeAck(enabled) {
|
|
48
|
-
const p = _getAckPath();
|
|
49
|
-
try {
|
|
50
|
-
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
51
|
-
const body = {
|
|
52
|
-
enabled: !!enabled,
|
|
53
|
-
acknowledged_at: new Date().toISOString(),
|
|
54
|
-
version: 1,
|
|
55
|
-
};
|
|
56
|
-
fs.writeFileSync(p, JSON.stringify(body, null, 2) + "\n", "utf8");
|
|
57
|
-
return true;
|
|
58
|
-
} catch (_) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
25
|
+
// All ack file plumbing lives on autoBuyer (filename constant, path
|
|
26
|
+
// resolution, read with strict validation, atomic write via tmp+rename).
|
|
27
|
+
// cliAutobuyPrompt always reaches it through the public surface so the
|
|
28
|
+
// two modules cannot diverge on schema or validation — pre-consolidation
|
|
29
|
+
// drift bit us twice (Bugbot PR #141: duplicate writers + lenient-vs-
|
|
30
|
+
// strict reader). No __internals re-export here either: tests import
|
|
31
|
+
// autoBuyer directly so a future rename trips a single set of asserts.
|
|
62
32
|
|
|
63
33
|
/**
|
|
64
34
|
* @returns {"ack_present"|"env_set"|"non_tty"|"eligible"}
|
|
@@ -71,7 +41,7 @@ function classify(env, stdin) {
|
|
|
71
41
|
if (!stdin || !stdin.isTTY) {
|
|
72
42
|
return "non_tty";
|
|
73
43
|
}
|
|
74
|
-
if (
|
|
44
|
+
if (autoBuyer.readAck()) {
|
|
75
45
|
return "ack_present";
|
|
76
46
|
}
|
|
77
47
|
return "eligible";
|
|
@@ -116,11 +86,12 @@ async function runPrompt(opts) {
|
|
|
116
86
|
|
|
117
87
|
try {
|
|
118
88
|
output.write("\n");
|
|
119
|
-
output.write("[ATP-AutoBuyer] Your evolver
|
|
120
|
-
output.write("ATP orders when it detects a capability gap
|
|
121
|
-
output.write("
|
|
122
|
-
output.write(" -
|
|
123
|
-
output.write(" -
|
|
89
|
+
output.write("[ATP-AutoBuyer] Your evolver will automatically place small-priced\n");
|
|
90
|
+
output.write("ATP orders when it detects a capability gap. This spends real\n");
|
|
91
|
+
output.write("credits on the EvoMap hub and is ON by default for new installs.\n");
|
|
92
|
+
output.write(" - daily hard cap: ATP_AUTOBUY_DAILY_CAP_CREDITS (default 50)\n");
|
|
93
|
+
output.write(" - per-order cap: ATP_AUTOBUY_PER_ORDER_CAP_CREDITS (default 10)\n");
|
|
94
|
+
output.write(" - change later: evolver atp enable | evolver atp disable\n");
|
|
124
95
|
output.write("\n");
|
|
125
96
|
} catch (_) {
|
|
126
97
|
return { prompted: false, decision: null, reason: "io_error" };
|
|
@@ -128,7 +99,7 @@ async function runPrompt(opts) {
|
|
|
128
99
|
|
|
129
100
|
let answer;
|
|
130
101
|
try {
|
|
131
|
-
answer = await ask("Keep
|
|
102
|
+
answer = await ask("Keep ATP auto-spend ON for future sessions? [y=keep enabled / n=disable / later=ask again next session] ", {
|
|
132
103
|
input,
|
|
133
104
|
output,
|
|
134
105
|
});
|
|
@@ -136,26 +107,48 @@ async function runPrompt(opts) {
|
|
|
136
107
|
return { prompted: true, decision: null, reason: "ask_error" };
|
|
137
108
|
}
|
|
138
109
|
|
|
110
|
+
// For y/n we persist via autoBuyer.setConsent (atomic tmp+rename, throws
|
|
111
|
+
// on FS failure). If the write fails we MUST tell the user — for the 'n'
|
|
112
|
+
// path especially, since auto-spend is default-ON and a failed disable
|
|
113
|
+
// means the user typed "off" but the runtime keeps charging credits
|
|
114
|
+
// (Bugbot PR #141 Medium: unchecked ack write). Do NOT mutate process.env
|
|
115
|
+
// on success: that would double-signal and shadow any explicit operator
|
|
116
|
+
// preference set later.
|
|
117
|
+
function _persistConsent(enabled, decision) {
|
|
118
|
+
try {
|
|
119
|
+
autoBuyer.setConsent(enabled);
|
|
120
|
+
return { prompted: true, decision, reason: enabled ? "user_accepted" : "user_declined" };
|
|
121
|
+
} catch (err) {
|
|
122
|
+
try {
|
|
123
|
+
output.write("[ATP-AutoBuyer] WARN: failed to persist consent: " + (err && err.message || err) + "\n");
|
|
124
|
+
if (enabled) {
|
|
125
|
+
output.write(" Auto-spend will keep using the default-on policy until\n");
|
|
126
|
+
output.write(" the ack is saved (capped at the configured caps).\n");
|
|
127
|
+
} else {
|
|
128
|
+
output.write(" Auto-spend will STAY ON (default policy) until your opt-out\n");
|
|
129
|
+
output.write(" can be saved — your decline was not persisted.\n");
|
|
130
|
+
}
|
|
131
|
+
output.write(" Check disk space and write permissions on the memory dir, then run\n");
|
|
132
|
+
output.write(" `evolver atp " + (enabled ? "enable" : "disable") + "` to retry.\n");
|
|
133
|
+
} catch (_) { /* output stream is broken too — nothing more we can do */ }
|
|
134
|
+
return { prompted: true, decision, reason: "ack_write_failed" };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
139
138
|
if (answer === "y" || answer === "yes") {
|
|
140
|
-
|
|
141
|
-
env.EVOLVER_ATP_AUTOBUY = "on";
|
|
142
|
-
return { prompted: true, decision: "yes", reason: "user_accepted" };
|
|
139
|
+
return _persistConsent(true, "yes");
|
|
143
140
|
}
|
|
144
141
|
if (answer === "n" || answer === "no") {
|
|
145
|
-
|
|
146
|
-
env.EVOLVER_ATP_AUTOBUY = "off";
|
|
147
|
-
return { prompted: true, decision: "no", reason: "user_declined" };
|
|
142
|
+
return _persistConsent(false, "no");
|
|
148
143
|
}
|
|
144
|
+
// Postpone: no ack written, so autoBuyer.getConsent() returns
|
|
145
|
+
// {enabled: true, source: 'default'} this session. Auto-spend keeps
|
|
146
|
+
// running under the default policy with caps; the prompt will fire again
|
|
147
|
+
// next interactive session so the user can confirm or opt out.
|
|
149
148
|
return { prompted: true, decision: "later", reason: "user_postponed" };
|
|
150
149
|
}
|
|
151
150
|
|
|
152
151
|
module.exports = {
|
|
153
152
|
runPrompt,
|
|
154
153
|
classify,
|
|
155
|
-
__internals: {
|
|
156
|
-
ACK_FILE_NAME,
|
|
157
|
-
_readAck,
|
|
158
|
-
_writeAck,
|
|
159
|
-
_getAckPath,
|
|
160
|
-
},
|
|
161
154
|
};
|
package/src/atp/hubClient.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
const http = require('http');
|
|
18
18
|
const { getHubUrl, buildHubHeaders, getNodeId } = require('../gep/a2aProtocol');
|
|
19
|
+
const { hubFetch } = require('../gep/hubFetch');
|
|
19
20
|
const { getProxyUrl, getProxyToken } = require('../proxy/server/settings');
|
|
20
21
|
|
|
21
22
|
function _isProxyMode() {
|
|
@@ -68,12 +69,33 @@ function _proxyRequest(method, path, body, timeoutMs) {
|
|
|
68
69
|
});
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
// Route through hubFetch() rather than the global `fetch()` for two
|
|
73
|
+
// reasons (both flagged by Cursor reviewers on PR #160):
|
|
74
|
+
//
|
|
75
|
+
// 1. Dispatcher mixing (Bugbot HIGH): `strictUndiciAgent` is an Agent
|
|
76
|
+
// from the *installed* `undici` package, but `global.fetch` is
|
|
77
|
+
// backed by Node's *internal* undici. Passing one to the other
|
|
78
|
+
// throws `UND_ERR_INVALID_ARG: invalid onRequestStart method` at
|
|
79
|
+
// request time — exactly the failure mode the comment at the top
|
|
80
|
+
// of hubFetch.js calls out. hubFetch already routes through
|
|
81
|
+
// `undici.fetch` from the same package as its Agent, so all calls
|
|
82
|
+
// that go through hubFetch are immune.
|
|
83
|
+
//
|
|
84
|
+
// 2. Case-sensitive scheme check (Security Reviewer MEDIUM): a hand-
|
|
85
|
+
// rolled `endpoint.startsWith('https:')` would skip the strict
|
|
86
|
+
// dispatcher for `HTTPS://...`. hubFetch's `_validateHubUrl` uses
|
|
87
|
+
// `new URL(url).protocol`, which normalises to lowercase, so
|
|
88
|
+
// routing through it eliminates the bug class.
|
|
89
|
+
//
|
|
90
|
+
// Routing through hubFetch also inherits the URL-scheme enforcement and
|
|
91
|
+
// the EVOMAP_HUB_ALLOW_INSECURE escape hatch automatically; we no
|
|
92
|
+
// longer need the explicit `enforceHubScheme` guard here.
|
|
71
93
|
function _hubPost(pathSuffix, body, timeoutMs) {
|
|
72
94
|
const hubUrl = getHubUrl();
|
|
73
95
|
if (!hubUrl) return Promise.resolve({ ok: false, error: 'no_hub_url' });
|
|
74
96
|
const endpoint = hubUrl.replace(/\/+$/, '') + pathSuffix;
|
|
75
97
|
const timeout = timeoutMs || require('../config').HTTP_TRANSPORT_TIMEOUT_MS;
|
|
76
|
-
return
|
|
98
|
+
return hubFetch(endpoint, {
|
|
77
99
|
method: 'POST',
|
|
78
100
|
headers: buildHubHeaders(),
|
|
79
101
|
body: JSON.stringify(body),
|
|
@@ -83,7 +105,17 @@ function _hubPost(pathSuffix, body, timeoutMs) {
|
|
|
83
105
|
if (!res.ok) return res.text().then(function (t) { return { ok: false, status: res.status, error: t.slice(0, 400) }; });
|
|
84
106
|
return res.json().then(function (data) { return { ok: true, data: data }; });
|
|
85
107
|
})
|
|
86
|
-
.catch(function (err) {
|
|
108
|
+
.catch(function (err) {
|
|
109
|
+
// hubFetch throws synchronously (rejected Promise) when the URL
|
|
110
|
+
// fails scheme validation in secure mode. Translate to the same
|
|
111
|
+
// structured envelope the previous in-line guard produced so the
|
|
112
|
+
// caller contract is unchanged.
|
|
113
|
+
const msg = (err && err.message) || String(err);
|
|
114
|
+
if (msg.indexOf('[hubFetch]') !== -1) {
|
|
115
|
+
return { ok: false, error: 'tls_refused: ' + msg };
|
|
116
|
+
}
|
|
117
|
+
return { ok: false, error: msg };
|
|
118
|
+
});
|
|
87
119
|
}
|
|
88
120
|
|
|
89
121
|
function _hubGet(pathSuffix, timeoutMs) {
|
|
@@ -91,7 +123,7 @@ function _hubGet(pathSuffix, timeoutMs) {
|
|
|
91
123
|
if (!hubUrl) return Promise.resolve({ ok: false, error: 'no_hub_url' });
|
|
92
124
|
const endpoint = hubUrl.replace(/\/+$/, '') + pathSuffix;
|
|
93
125
|
const timeout = timeoutMs || require('../config').HTTP_TRANSPORT_TIMEOUT_MS;
|
|
94
|
-
return
|
|
126
|
+
return hubFetch(endpoint, {
|
|
95
127
|
method: 'GET',
|
|
96
128
|
headers: buildHubHeaders(),
|
|
97
129
|
signal: AbortSignal.timeout(timeout),
|
|
@@ -100,7 +132,13 @@ function _hubGet(pathSuffix, timeoutMs) {
|
|
|
100
132
|
if (!res.ok) return res.text().then(function (t) { return { ok: false, status: res.status, error: t.slice(0, 400) }; });
|
|
101
133
|
return res.json().then(function (data) { return { ok: true, data: data }; });
|
|
102
134
|
})
|
|
103
|
-
.catch(function (err) {
|
|
135
|
+
.catch(function (err) {
|
|
136
|
+
const msg = (err && err.message) || String(err);
|
|
137
|
+
if (msg.indexOf('[hubFetch]') !== -1) {
|
|
138
|
+
return { ok: false, error: 'tls_refused: ' + msg };
|
|
139
|
+
}
|
|
140
|
+
return { ok: false, error: msg };
|
|
141
|
+
});
|
|
104
142
|
}
|
|
105
143
|
|
|
106
144
|
// Dispatcher: choose proxy or direct hub based on env + proxy availability.
|