promigence 0.1.0-beta.13 → 0.1.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/sdk/core.d.ts +1 -1
- package/dist/sdk/core.js +1 -1
- package/dist/sdk/http2.js +195 -23
- package/dist/sdk/trace.js +16 -0
- package/dist/sdk/transport-worker.js +72 -12
- package/llms.txt +3 -3
- package/package.json +1 -1
package/dist/sdk/core.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Promigence SDK — constants, error hierarchy and shared types. */
|
|
2
|
-
export declare const VERSION = "0.1.0-beta.
|
|
2
|
+
export declare const VERSION = "0.1.0-beta.14";
|
|
3
3
|
export declare const DOCS_URL = "https://promigence.ai";
|
|
4
4
|
/** The error reference; each code is an anchor on it (the API's `docs` link has the same form). */
|
|
5
5
|
export declare const ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
|
package/dist/sdk/core.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hintArg } from "./hint.js";
|
|
2
|
-
export const VERSION = "0.1.0-beta.
|
|
2
|
+
export const VERSION = "0.1.0-beta.14";
|
|
3
3
|
export const DOCS_URL = "https://promigence.ai";
|
|
4
4
|
export const ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
|
|
5
5
|
export const DEFAULT_API_URL = "https://api.promigence.ai/v1";
|
package/dist/sdk/http2.js
CHANGED
|
@@ -6,11 +6,12 @@ import * as zlib from "node:zlib";
|
|
|
6
6
|
import { __caGuardInternals, guardCertChain } from "./ca-guard.js";
|
|
7
7
|
import { DEFAULT_API_URL } from "./core.js";
|
|
8
8
|
import { customDispatcher } from "./dispatcher.js";
|
|
9
|
-
import { mark, TRACE_ON, traceEvent, traceEventAt } from "./trace.js";
|
|
9
|
+
import { mark, onTraceFlush, TRACE_ON, traceEvent, traceEventAt } from "./trace.js";
|
|
10
10
|
const MAX_SESSIONS = 4;
|
|
11
11
|
const FIRST_SESSIONS = 2;
|
|
12
12
|
const BURST_MIN = 8;
|
|
13
13
|
const HOLD_MS = 10;
|
|
14
|
+
const STANDBY_AFTER_MS = 300;
|
|
14
15
|
const LOAD_FIRST_USE_MS = 120_000;
|
|
15
16
|
const CONNECT_TIMEOUT_MS = 10_000;
|
|
16
17
|
const IDLE_CLOSE_MS = 10_000;
|
|
@@ -561,6 +562,7 @@ export const tuning = {
|
|
|
561
562
|
holdMs: HOLD_MS,
|
|
562
563
|
burstMin: BURST_MIN,
|
|
563
564
|
loadFirstUseMs: LOAD_FIRST_USE_MS,
|
|
565
|
+
standbyAfterMs: STANDBY_AFTER_MS,
|
|
564
566
|
earlyPlatform: undefined,
|
|
565
567
|
earlyServers: undefined,
|
|
566
568
|
earlyHostsFile: undefined,
|
|
@@ -844,7 +846,9 @@ function apiLookup(asked, observe, ipv4First = v4First(), earlyOk = true) {
|
|
|
844
846
|
const earlies = new Map();
|
|
845
847
|
const MAX_EARLIES = 4;
|
|
846
848
|
const EARLY_MAX_AGE_MS = 60_000;
|
|
847
|
-
const
|
|
849
|
+
const LOOKUP_FIRST_MS = 150;
|
|
850
|
+
const LOOKUP_TRIES = 3;
|
|
851
|
+
const LOOKUP_AT_MS = [0, 100, 250];
|
|
848
852
|
const DNS_AT_LOAD = (() => {
|
|
849
853
|
try {
|
|
850
854
|
return nodeDns();
|
|
@@ -865,8 +869,8 @@ function lookupReplaced() {
|
|
|
865
869
|
const EARLY_WORKER = `
|
|
866
870
|
const { workerData } = require("node:worker_threads");
|
|
867
871
|
const { isIPv4 } = require("node:net");
|
|
868
|
-
const { port, host, timeoutMs, servers, hostsFile } = workerData;
|
|
869
|
-
const done = (m) => { try { port.postMessage(m); } catch {} try { port.close(); } catch {} };
|
|
872
|
+
const { port, host, timeoutMs, tries, atMs, servers, hostsFile } = workerData;
|
|
873
|
+
const done = (m) => { m.hr = Number(process.hrtime.bigint()) / 1e6; try { port.postMessage(m); } catch {} try { port.close(); } catch {} };
|
|
870
874
|
try {
|
|
871
875
|
let named = false;
|
|
872
876
|
const v4 = [];
|
|
@@ -880,12 +884,28 @@ try {
|
|
|
880
884
|
} catch {}
|
|
881
885
|
if (named) done(v4.length > 0 ? { host, addrs: v4, ttl: 60, at: Date.now() } : { host, none: "hosts" });
|
|
882
886
|
else {
|
|
883
|
-
const
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
});
|
|
887
|
+
const { Resolver } = require("node:dns");
|
|
888
|
+
const rs = [];
|
|
889
|
+
const timers = [];
|
|
890
|
+
let fin = false, asked = 0, failed = 0;
|
|
891
|
+
const finish = (m) => { if (fin) return; fin = true; for (const t of timers) clearTimeout(t); for (const r of rs) { try { r.cancel(); } catch {} } done(m); };
|
|
892
|
+
const gaveUp = (why) => { if (++failed < asked) return; if (asked < atMs.length) ask(); else finish({ host, none: why }); };
|
|
893
|
+
const ask = () => {
|
|
894
|
+
if (fin || asked >= atMs.length) return;
|
|
895
|
+
const k = asked++;
|
|
896
|
+
try {
|
|
897
|
+
const r = new Resolver({ timeout: timeoutMs, tries });
|
|
898
|
+
const list = Array.isArray(servers) && servers.length > 0 ? servers : r.getServers();
|
|
899
|
+
if (list.length > 0) r.setServers(list.map((_, i) => list[(i + k) % list.length]));
|
|
900
|
+
rs.push(r);
|
|
901
|
+
r.resolve4(host, { ttl: true }, (err, recs) => {
|
|
902
|
+
if (fin) return;
|
|
903
|
+
if (!err && Array.isArray(recs) && recs.length > 0) return finish({ host, addrs: recs.map((x) => x.address), ttl: Math.min(...recs.map((x) => x.ttl)), at: Date.now() });
|
|
904
|
+
gaveUp((err && err.code) || "empty");
|
|
905
|
+
});
|
|
906
|
+
} catch { gaveUp("error"); }
|
|
907
|
+
};
|
|
908
|
+
for (const ms of atMs) { if (ms <= 0) ask(); else timers.push(setTimeout(ask, ms)); }
|
|
889
909
|
}
|
|
890
910
|
} catch { done({ host, none: "error" }); }
|
|
891
911
|
`;
|
|
@@ -916,7 +936,7 @@ function earlyLookupFor(host) {
|
|
|
916
936
|
const worker = new Worker(EARLY_WORKER, {
|
|
917
937
|
eval: true,
|
|
918
938
|
workerData: {
|
|
919
|
-
port: port2, host, timeoutMs:
|
|
939
|
+
port: port2, host, timeoutMs: LOOKUP_FIRST_MS, tries: LOOKUP_TRIES, atMs: LOOKUP_AT_MS,
|
|
920
940
|
servers: tuning.earlyServers ?? (Array.isArray(earlySeam().servers) ? earlySeam().servers : undefined),
|
|
921
941
|
hostsFile: tuning.earlyHostsFile ?? (typeof earlySeam().hostsFile === "string" ? earlySeam().hostsFile : undefined),
|
|
922
942
|
},
|
|
@@ -964,6 +984,10 @@ function settleEarly(e, message) {
|
|
|
964
984
|
else {
|
|
965
985
|
e.state = "none";
|
|
966
986
|
}
|
|
987
|
+
if (TRACE_ON) {
|
|
988
|
+
const at = typeof m?.hr === "number" ? m.hr - (Number(process.hrtime.bigint()) / 1e6 - performance.now()) : performance.now();
|
|
989
|
+
traceEventAt(e.state === "answered" ? `dns:early-answer ${addrs.length}` : `dns:early-none ${typeof m?.none === "string" ? m.none : "gone"}`, at);
|
|
990
|
+
}
|
|
967
991
|
safely(() => { e.port.close(); });
|
|
968
992
|
const waiters = e.waiters.splice(0);
|
|
969
993
|
for (const w of waiters)
|
|
@@ -1327,6 +1351,8 @@ class TransportThread {
|
|
|
1327
1351
|
everReady = false;
|
|
1328
1352
|
closedReason = "";
|
|
1329
1353
|
onFail;
|
|
1354
|
+
startedAt = performance.now();
|
|
1355
|
+
startWaiters = [];
|
|
1330
1356
|
inflight = new Map();
|
|
1331
1357
|
nextId = 1;
|
|
1332
1358
|
listening = false;
|
|
@@ -1366,10 +1392,41 @@ class TransportThread {
|
|
|
1366
1392
|
}
|
|
1367
1393
|
}
|
|
1368
1394
|
ready() {
|
|
1369
|
-
if (!this.listening && this.state !== "closed")
|
|
1395
|
+
if ((!this.listening || this.state === "starting") && this.state !== "closed")
|
|
1370
1396
|
this.poll();
|
|
1371
1397
|
return this.state === "ready" && this.sessions > 0 && this.inflight.size < this.sessions * this.limit && this.freeMarks.length > 0;
|
|
1372
1398
|
}
|
|
1399
|
+
starting() {
|
|
1400
|
+
if (this.state === "starting")
|
|
1401
|
+
this.poll();
|
|
1402
|
+
return this.state === "starting";
|
|
1403
|
+
}
|
|
1404
|
+
whenStarted(cb) {
|
|
1405
|
+
if (!this.starting()) {
|
|
1406
|
+
queueMicrotask(cb);
|
|
1407
|
+
return () => { };
|
|
1408
|
+
}
|
|
1409
|
+
this.startWaiters.push(cb);
|
|
1410
|
+
this.listen();
|
|
1411
|
+
return () => {
|
|
1412
|
+
const i = this.startWaiters.indexOf(cb);
|
|
1413
|
+
if (i >= 0)
|
|
1414
|
+
this.startWaiters.splice(i, 1);
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
started() {
|
|
1418
|
+
for (const cb of this.startWaiters.splice(0))
|
|
1419
|
+
safely(cb);
|
|
1420
|
+
}
|
|
1421
|
+
listen() {
|
|
1422
|
+
if (this.listening)
|
|
1423
|
+
return;
|
|
1424
|
+
this.listening = true;
|
|
1425
|
+
this.poll();
|
|
1426
|
+
this.port.on("message", (m) => { this.onMessage(m); });
|
|
1427
|
+
if (!this.refd)
|
|
1428
|
+
safely(() => { this.port.unref(); });
|
|
1429
|
+
}
|
|
1373
1430
|
onMessage(m) {
|
|
1374
1431
|
const msg = m;
|
|
1375
1432
|
this.messages++;
|
|
@@ -1392,8 +1449,16 @@ class TransportThread {
|
|
|
1392
1449
|
if (typeof c.index === "number" && typeof c.addr === "string" && typeof c.localPort === "number")
|
|
1393
1450
|
traceEventAt(`thread:conn${c.index} ${c.addr} ${c.localPort}`, t);
|
|
1394
1451
|
}
|
|
1452
|
+
if (first)
|
|
1453
|
+
this.started();
|
|
1395
1454
|
}
|
|
1396
1455
|
return;
|
|
1456
|
+
case "ev": {
|
|
1457
|
+
const e = msg;
|
|
1458
|
+
if (typeof e.n === "string" && typeof e.hr === "number")
|
|
1459
|
+
traceEventAt(`thread:w-${e.n}`, e.hr - this.hrOffset);
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1397
1462
|
case "sessions":
|
|
1398
1463
|
if (typeof msg.sessions === "number")
|
|
1399
1464
|
this.sessions = msg.sessions;
|
|
@@ -1404,6 +1469,7 @@ class TransportThread {
|
|
|
1404
1469
|
if (this.closedReason === "")
|
|
1405
1470
|
this.closedReason = msg.reason ?? "draining";
|
|
1406
1471
|
traceEvent("thread:draining");
|
|
1472
|
+
this.started();
|
|
1407
1473
|
}
|
|
1408
1474
|
return;
|
|
1409
1475
|
case "closed":
|
|
@@ -1484,6 +1550,7 @@ class TransportThread {
|
|
|
1484
1550
|
this.port.postMessage({ t: "drain" });
|
|
1485
1551
|
}
|
|
1486
1552
|
catch { }
|
|
1553
|
+
this.started();
|
|
1487
1554
|
}
|
|
1488
1555
|
close(reason) {
|
|
1489
1556
|
if (this.state === "closed")
|
|
@@ -1492,6 +1559,7 @@ class TransportThread {
|
|
|
1492
1559
|
if (this.closedReason === "")
|
|
1493
1560
|
this.closedReason = reason;
|
|
1494
1561
|
traceEvent("thread:closed");
|
|
1562
|
+
this.started();
|
|
1495
1563
|
this.outbox = [];
|
|
1496
1564
|
for (const [id, p] of [...this.inflight]) {
|
|
1497
1565
|
const sent = this.acked(p);
|
|
@@ -1514,11 +1582,7 @@ class TransportThread {
|
|
|
1514
1582
|
}
|
|
1515
1583
|
}
|
|
1516
1584
|
ref() {
|
|
1517
|
-
|
|
1518
|
-
this.listening = true;
|
|
1519
|
-
this.poll();
|
|
1520
|
-
this.port.on("message", (m) => { this.onMessage(m); });
|
|
1521
|
-
}
|
|
1585
|
+
this.listen();
|
|
1522
1586
|
if (this.refd)
|
|
1523
1587
|
return;
|
|
1524
1588
|
this.refd = true;
|
|
@@ -1659,6 +1723,55 @@ function threadFor(origin) {
|
|
|
1659
1723
|
return undefined;
|
|
1660
1724
|
return t.ready() && !lookupReplaced() ? t : undefined;
|
|
1661
1725
|
}
|
|
1726
|
+
function startingThread(origin) {
|
|
1727
|
+
const t = transportThread;
|
|
1728
|
+
if (t === undefined || t.origin !== origin || lookupReplaced())
|
|
1729
|
+
return undefined;
|
|
1730
|
+
return t.starting() ? t : undefined;
|
|
1731
|
+
}
|
|
1732
|
+
let raceTraced = false;
|
|
1733
|
+
function firstUp(t, pool, deadline, signal) {
|
|
1734
|
+
const first = !raceTraced;
|
|
1735
|
+
if (first) {
|
|
1736
|
+
raceTraced = true;
|
|
1737
|
+
traceEvent("h2:race");
|
|
1738
|
+
}
|
|
1739
|
+
const stop = new AbortController();
|
|
1740
|
+
const both = signal === undefined ? stop.signal : AbortSignal.any([signal, stop.signal]);
|
|
1741
|
+
return new Promise((resolve, reject) => {
|
|
1742
|
+
let done = false;
|
|
1743
|
+
const forget = t.whenStarted(() => {
|
|
1744
|
+
if (done)
|
|
1745
|
+
return;
|
|
1746
|
+
const ready = threadFor(t.origin);
|
|
1747
|
+
if (ready === undefined)
|
|
1748
|
+
return;
|
|
1749
|
+
done = true;
|
|
1750
|
+
if (first)
|
|
1751
|
+
traceEvent("h2:race-thread");
|
|
1752
|
+
stop.abort();
|
|
1753
|
+
resolve({ thread: ready });
|
|
1754
|
+
});
|
|
1755
|
+
pool.acquire(deadline, both).then((entry) => {
|
|
1756
|
+
if (done) {
|
|
1757
|
+
if (entry !== null)
|
|
1758
|
+
pool.end(entry);
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
done = true;
|
|
1762
|
+
forget();
|
|
1763
|
+
if (first)
|
|
1764
|
+
traceEvent("h2:race-pool");
|
|
1765
|
+
resolve({ entry });
|
|
1766
|
+
}, (err) => {
|
|
1767
|
+
if (done)
|
|
1768
|
+
return;
|
|
1769
|
+
done = true;
|
|
1770
|
+
forget();
|
|
1771
|
+
reject(err);
|
|
1772
|
+
});
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1662
1775
|
function ownFile() {
|
|
1663
1776
|
const frame = (new Error().stack ?? "").split("\n")[1] ?? "";
|
|
1664
1777
|
const m = /\((.*):\d+:\d+\)\s*$/.exec(frame) ?? /at (.*):\d+:\d+\s*$/.exec(frame);
|
|
@@ -1701,7 +1814,7 @@ function startTransportThread(origin, onFail) {
|
|
|
1701
1814
|
workerData: {
|
|
1702
1815
|
promigenceTransport: true, port: port2, acks, origin: origin.origin, sessions: knobs.sessions, gapUs: knobs.gapUs,
|
|
1703
1816
|
firstUseMs: tuning.loadFirstUseMs, idleMs: IDLE_CLOSE_MS, connectTimeoutMs: CONNECT_TIMEOUT_MS,
|
|
1704
|
-
lookupTimeoutMs:
|
|
1817
|
+
lookupTimeoutMs: LOOKUP_FIRST_MS, lookupTries: LOOKUP_TRIES, lookupAtMs: LOOKUP_AT_MS, trace: TRACE_ON,
|
|
1705
1818
|
servers: tuning.earlyServers ?? (Array.isArray(seam.servers) ? seam.servers : undefined),
|
|
1706
1819
|
hostsFile: tuning.earlyHostsFile ?? (typeof seam.hostsFile === "string" ? seam.hostsFile : undefined),
|
|
1707
1820
|
distrustedForTest: __caGuardInternals.testHashes(),
|
|
@@ -1719,6 +1832,11 @@ function startTransportThread(origin, onFail) {
|
|
|
1719
1832
|
worker.on("exit", () => { t.poll(); t.close("exit"); });
|
|
1720
1833
|
transportThread = t;
|
|
1721
1834
|
traceEvent("thread:start");
|
|
1835
|
+
onTraceFlush(() => {
|
|
1836
|
+
t.poll();
|
|
1837
|
+
for (const e of earlies.values())
|
|
1838
|
+
pollEarly(e);
|
|
1839
|
+
});
|
|
1722
1840
|
}
|
|
1723
1841
|
catch {
|
|
1724
1842
|
transportThread = undefined;
|
|
@@ -1805,8 +1923,34 @@ export class Http2Transport {
|
|
|
1805
1923
|
const pool = poolFor(target.origin);
|
|
1806
1924
|
if (!pool.usable())
|
|
1807
1925
|
return this.viaFetch(url, init, deadline);
|
|
1926
|
+
let raced = false;
|
|
1808
1927
|
for (;;) {
|
|
1809
|
-
|
|
1928
|
+
let entry = pool.tryAcquire();
|
|
1929
|
+
if (entry === undefined) {
|
|
1930
|
+
const starting = raced || resends > 0 || replays > 0 ? undefined : startingThread(target.origin);
|
|
1931
|
+
if (starting !== undefined) {
|
|
1932
|
+
raced = true;
|
|
1933
|
+
const won = await firstUp(starting, pool, deadline, init.signal);
|
|
1934
|
+
if (won.thread !== undefined) {
|
|
1935
|
+
if (tr !== undefined)
|
|
1936
|
+
mark(tr, "race");
|
|
1937
|
+
const out = await won.thread.send(target, init, replayable, deadline);
|
|
1938
|
+
if (out === VIA_FETCH)
|
|
1939
|
+
return this.viaFetch(url, init, deadline);
|
|
1940
|
+
if (!(out instanceof HandBack))
|
|
1941
|
+
return out;
|
|
1942
|
+
resends = out.resends;
|
|
1943
|
+
replays = out.replays;
|
|
1944
|
+
if (tr !== undefined)
|
|
1945
|
+
delete tr.via;
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
entry = won.entry;
|
|
1949
|
+
}
|
|
1950
|
+
else {
|
|
1951
|
+
entry = await pool.acquire(deadline, init.signal);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1810
1954
|
if (tr !== undefined)
|
|
1811
1955
|
mark(tr, "acq");
|
|
1812
1956
|
if (entry === null)
|
|
@@ -1905,7 +2049,7 @@ export const __http2Internals = {
|
|
|
1905
2049
|
if (t === undefined)
|
|
1906
2050
|
return undefined;
|
|
1907
2051
|
t.poll();
|
|
1908
|
-
return { state: t.state, sessions: t.sessions, limit: t.limit, answered: t.answered, everReady: t.everReady, closedReason: t.closedReason, messages: t.messages, posts: t.posts };
|
|
2052
|
+
return { state: t.state, sessions: t.sessions, limit: t.limit, answered: t.answered, everReady: t.everReady, closedReason: t.closedReason, messages: t.messages, posts: t.posts, startedAt: t.startedAt };
|
|
1909
2053
|
},
|
|
1910
2054
|
threadKnobs(env) {
|
|
1911
2055
|
return threadKnobs(env);
|
|
@@ -1954,6 +2098,15 @@ function isMainThread() {
|
|
|
1954
2098
|
return false;
|
|
1955
2099
|
}
|
|
1956
2100
|
}
|
|
2101
|
+
function standby(url) {
|
|
2102
|
+
try {
|
|
2103
|
+
traceEvent("h2:standby");
|
|
2104
|
+
warmTls();
|
|
2105
|
+
poolFor(url.origin).warm(tuning.loadFirstUseMs);
|
|
2106
|
+
}
|
|
2107
|
+
catch {
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
1957
2110
|
function connectAtLoad() {
|
|
1958
2111
|
try {
|
|
1959
2112
|
if (!isNode() || Number(process.versions.node.split(".")[0]) < 22)
|
|
@@ -1974,6 +2127,8 @@ function connectAtLoad() {
|
|
|
1974
2127
|
if (url.protocol === "https:" && !customDispatcher() && earlyPlatform() && v4First() && !threadOff && earlySeam().transportThread !== false) {
|
|
1975
2128
|
startTransportThread(url, () => { if (loadTurnRan)
|
|
1976
2129
|
warmAtLoad(); });
|
|
2130
|
+
if (isIP(host) === 0)
|
|
2131
|
+
earlyLookupFor(host);
|
|
1977
2132
|
}
|
|
1978
2133
|
else if (url.protocol === "https:" && !customDispatcher() && isIP(host) === 0 && earlyPlatform() && v4First())
|
|
1979
2134
|
earlyLookupFor(host);
|
|
@@ -1992,10 +2147,27 @@ function connectAtLoad() {
|
|
|
1992
2147
|
const url = new URL(raw ? raw : DEFAULT_API_URL);
|
|
1993
2148
|
if (url.protocol !== "https:" || customDispatcher())
|
|
1994
2149
|
return;
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2150
|
+
const t = transportThread;
|
|
2151
|
+
if (t !== undefined && t.origin === url.origin) {
|
|
2152
|
+
t.poll();
|
|
2153
|
+
if (t.everReady)
|
|
2154
|
+
return;
|
|
2155
|
+
if (t.state !== "closed") {
|
|
2156
|
+
const check = () => {
|
|
2157
|
+
t.poll();
|
|
2158
|
+
if (t.everReady || t.state === "closed")
|
|
2159
|
+
return;
|
|
2160
|
+
const wait = t.startedAt + tuning.standbyAfterMs - performance.now();
|
|
2161
|
+
if (wait > 0) {
|
|
2162
|
+
const timer = setTimeout(check, wait);
|
|
2163
|
+
timer.unref?.();
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
standby(url);
|
|
2167
|
+
};
|
|
2168
|
+
check();
|
|
1998
2169
|
return;
|
|
2170
|
+
}
|
|
1999
2171
|
}
|
|
2000
2172
|
warmTls();
|
|
2001
2173
|
poolFor(url.origin).warm(tuning.loadFirstUseMs);
|
package/dist/sdk/trace.js
CHANGED
|
@@ -60,7 +60,23 @@ function modulePath() {
|
|
|
60
60
|
const m = /\((.*):\d+:\d+\)\s*$/.exec(frame) ?? /at (.*):\d+:\d+\s*$/.exec(frame);
|
|
61
61
|
return m?.[1] ?? "";
|
|
62
62
|
}
|
|
63
|
+
const beforeFlush = [];
|
|
64
|
+
export function onTraceFlush(fn) {
|
|
65
|
+
if (!TRACE_ON)
|
|
66
|
+
return;
|
|
67
|
+
beforeFlush.push(fn);
|
|
68
|
+
if (!hooked) {
|
|
69
|
+
hooked = true;
|
|
70
|
+
process.once("exit", flush);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
63
73
|
function flush() {
|
|
74
|
+
for (const fn of beforeFlush.splice(0)) {
|
|
75
|
+
try {
|
|
76
|
+
fn();
|
|
77
|
+
}
|
|
78
|
+
catch { }
|
|
79
|
+
}
|
|
64
80
|
const head = {
|
|
65
81
|
trace: "promigence-sdk",
|
|
66
82
|
version: VERSION,
|
|
@@ -34,6 +34,11 @@ function start(init) {
|
|
|
34
34
|
}
|
|
35
35
|
catch { }
|
|
36
36
|
};
|
|
37
|
+
const ev = (n) => {
|
|
38
|
+
if (init.trace === true)
|
|
39
|
+
post({ t: "ev", n, hr: hrNow() });
|
|
40
|
+
};
|
|
41
|
+
ev("boot");
|
|
37
42
|
const live = () => sessions.filter((e) => !e.retired && !e.session.closed && !e.session.destroyed);
|
|
38
43
|
const end = (reason) => {
|
|
39
44
|
if (closed)
|
|
@@ -94,14 +99,14 @@ function start(init) {
|
|
|
94
99
|
catch {
|
|
95
100
|
context = undefined;
|
|
96
101
|
}
|
|
97
|
-
const connect = (address) => new Promise((resolve) => {
|
|
102
|
+
const connect = (address, k) => new Promise((resolve) => {
|
|
98
103
|
let done = false;
|
|
99
104
|
const finish = (e) => { if (!done) {
|
|
100
105
|
done = true;
|
|
101
106
|
clearTimeout(timer);
|
|
102
107
|
resolve(e);
|
|
103
108
|
} };
|
|
104
|
-
const timer = setTimeout(() => { try {
|
|
109
|
+
const timer = setTimeout(() => { ev(`timeout${k}`); try {
|
|
105
110
|
socket.destroy();
|
|
106
111
|
}
|
|
107
112
|
catch { } finish(null); }, init.connectTimeoutMs);
|
|
@@ -113,8 +118,11 @@ function start(init) {
|
|
|
113
118
|
checkServerIdentity: guardCertChain,
|
|
114
119
|
...(context === undefined ? {} : { secureContext: context }),
|
|
115
120
|
});
|
|
116
|
-
|
|
121
|
+
if (init.trace === true)
|
|
122
|
+
socket.once("connect", () => { ev(`tcp${k}`); });
|
|
123
|
+
socket.once("error", (e) => { ev(`fail${k} ${e?.code ?? "error"}`); finish(null); });
|
|
117
124
|
socket.once("secureConnect", () => {
|
|
125
|
+
ev(`tls${k}`);
|
|
118
126
|
if (socket.alpnProtocol !== "h2") {
|
|
119
127
|
socket.destroy();
|
|
120
128
|
finish(null);
|
|
@@ -167,14 +175,66 @@ function start(init) {
|
|
|
167
175
|
resolve(v4);
|
|
168
176
|
return;
|
|
169
177
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
const at = init.lookupAtMs.length > 0 ? init.lookupAtMs : [0];
|
|
179
|
+
const resolvers = [];
|
|
180
|
+
const timers = [];
|
|
181
|
+
let done = false;
|
|
182
|
+
let asked = 0;
|
|
183
|
+
let failed = 0;
|
|
184
|
+
const finish = (addrs) => {
|
|
185
|
+
if (done)
|
|
186
|
+
return;
|
|
187
|
+
done = true;
|
|
188
|
+
for (const t of timers)
|
|
189
|
+
clearTimeout(t);
|
|
190
|
+
for (const r of resolvers) {
|
|
191
|
+
try {
|
|
192
|
+
r.cancel();
|
|
193
|
+
}
|
|
194
|
+
catch { }
|
|
195
|
+
}
|
|
196
|
+
resolve(addrs);
|
|
197
|
+
};
|
|
198
|
+
const gaveUp = () => {
|
|
199
|
+
if (++failed < asked)
|
|
200
|
+
return;
|
|
201
|
+
if (asked < at.length)
|
|
202
|
+
ask();
|
|
203
|
+
else
|
|
204
|
+
finish([]);
|
|
205
|
+
};
|
|
206
|
+
const ask = () => {
|
|
207
|
+
if (done || asked >= at.length)
|
|
208
|
+
return;
|
|
209
|
+
const k = asked++;
|
|
210
|
+
try {
|
|
211
|
+
const r = new dns.Resolver({ timeout: init.lookupTimeoutMs, tries: init.lookupTries });
|
|
212
|
+
const list = Array.isArray(init.servers) && init.servers.length > 0 ? init.servers : r.getServers();
|
|
213
|
+
if (list.length > 0)
|
|
214
|
+
r.setServers(list.map((_, i) => list[(i + k) % list.length]));
|
|
215
|
+
resolvers.push(r);
|
|
216
|
+
ev(`lookup${k + 1}`);
|
|
217
|
+
r.resolve4(name, (err, addrs) => {
|
|
218
|
+
if (done)
|
|
219
|
+
return;
|
|
220
|
+
if (!err && Array.isArray(addrs) && addrs.length > 0) {
|
|
221
|
+
ev(`addrs${k + 1} ${addrs.length}`);
|
|
222
|
+
finish(addrs);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
ev(`nolookup${k + 1} ${err?.code ?? "empty"}`);
|
|
226
|
+
gaveUp();
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
gaveUp();
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
for (const ms of at) {
|
|
234
|
+
if (ms <= 0)
|
|
235
|
+
ask();
|
|
236
|
+
else
|
|
237
|
+
timers.push(setTimeout(ask, ms));
|
|
178
238
|
}
|
|
179
239
|
});
|
|
180
240
|
const limitOf = (e) => {
|
|
@@ -196,7 +256,7 @@ function start(init) {
|
|
|
196
256
|
}
|
|
197
257
|
const n = Math.max(1, init.sessions);
|
|
198
258
|
pendingConnects = n;
|
|
199
|
-
const tries = Array.from({ length: n }, (_, i) => connect(addrs[i % addrs.length]).then((e) => {
|
|
259
|
+
const tries = Array.from({ length: n }, (_, i) => connect(addrs[i % addrs.length], i).then((e) => {
|
|
200
260
|
pendingConnects--;
|
|
201
261
|
if (closed) {
|
|
202
262
|
if (e)
|
package/llms.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Promigence — promigence@0.1.0-beta.
|
|
1
|
+
# Promigence — promigence@0.1.0-beta.14
|
|
2
2
|
|
|
3
3
|
> Verified, reproducible, forkable execution environments for agents. The CLI is the
|
|
4
4
|
> `promigence` command (package `promigence`, which is also the TypeScript SDK).
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
|
|
128
128
|
---
|
|
129
129
|
|
|
130
|
-
# Promigence — agent guide (promigence@0.1.0-beta.
|
|
130
|
+
# Promigence — agent guide (promigence@0.1.0-beta.14)
|
|
131
131
|
|
|
132
132
|
Promigence is an **environment runtime**: verified, reproducible, forkable execution
|
|
133
133
|
environments, billed per second (vCPU + memory). The model is one line:
|
|
@@ -205,5 +205,5 @@ Highest-precedence code wins: 2 > 5 > 6 > 7/8/9/10 > 11 > 3 > 12 > 4 > 1 > 0.
|
|
|
205
205
|
· `promigence <command> --help` · https://promigence.ai
|
|
206
206
|
Same surface as MCP tools: `promigence mcp` (stdio; `claude mcp add promigence -- promigence mcp`).
|
|
207
207
|
|
|
208
|
-
Live in v0.1.0-beta.
|
|
208
|
+
Live in v0.1.0-beta.14: login · signup · logout · auth login · keys list/create/revoke · org list/create/switch/members/invite/invites/uninvite/resend/role/remove/leave/join/audit/rename/delete · project list/create/rename/delete/cap/move · me · billing card/usage/history/portal · doctor · ping · init · examples · templates +show · agent-guide · mcp · snapshot create/status/list/inspect/verify/export/delete/builds/alias · image resolve · github connect/status/disconnect/untrack · repos · gitlab connect/status/disconnect/untrack · run +quote/report/status/cancel/kill/replay/open/captures/list · fork · exec +output/ps/stdin/kill · files put/get/ls/search/mkdir/mv/cp/chmod/rm · agent start · sandbox kill/network/shell/ssh/list/pause/resume/snapshot/info/extend/autopause/probe · secrets set/list/delete · bench burst · runners github/gitlab · status · referrals +apply · pricing · credits +claim
|
|
209
209
|
Not on the hosted service yet: port expose/list/revoke (preview URLs).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "promigence",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.14",
|
|
4
4
|
"description": "The fastest sandboxes for AI agents and code. Snapshot an environment once, fork it as often as you need, run commands in every copy, and get a report that tells environment failures from task failures. Billed per second, with a quote before a run and a hard spend cap. One package: the promigence command and the TypeScript SDK.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://www.promigence.ai",
|