gitnexus 1.6.4-rc.99 → 1.6.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/dist/_shared/index.d.ts +8 -0
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +9 -0
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/integrations/circuit-breaker.d.ts +183 -0
- package/dist/_shared/integrations/circuit-breaker.d.ts.map +1 -0
- package/dist/_shared/integrations/circuit-breaker.js +236 -0
- package/dist/_shared/integrations/circuit-breaker.js.map +1 -0
- package/dist/_shared/integrations/resilient-fetch.d.ts +99 -0
- package/dist/_shared/integrations/resilient-fetch.d.ts.map +1 -0
- package/dist/_shared/integrations/resilient-fetch.js +204 -0
- package/dist/_shared/integrations/resilient-fetch.js.map +1 -0
- package/dist/_shared/integrations/retry.d.ts +60 -0
- package/dist/_shared/integrations/retry.d.ts.map +1 -0
- package/dist/_shared/integrations/retry.js +67 -0
- package/dist/_shared/integrations/retry.js.map +1 -0
- package/dist/_shared/integrations/understand-quickly.d.ts +79 -0
- package/dist/_shared/integrations/understand-quickly.d.ts.map +1 -0
- package/dist/_shared/integrations/understand-quickly.js +139 -0
- package/dist/_shared/integrations/understand-quickly.js.map +1 -0
- package/dist/_shared/test-helpers.d.ts +13 -0
- package/dist/_shared/test-helpers.d.ts.map +1 -0
- package/dist/_shared/test-helpers.js +13 -0
- package/dist/_shared/test-helpers.js.map +1 -0
- package/dist/cli/index.js +9 -0
- package/dist/cli/publish.d.ts +29 -0
- package/dist/cli/publish.js +174 -0
- package/dist/config/ignore-service.js +2 -0
- package/dist/core/embeddings/hf-env.d.ts +9 -37
- package/dist/core/embeddings/hf-env.js +65 -83
- package/dist/core/embeddings/http-client.d.ts +6 -0
- package/dist/core/embeddings/http-client.js +22 -18
- package/dist/core/group/bridge-db.js +7 -6
- package/dist/core/group/config-parser.js +19 -1
- package/dist/core/group/extractors/include-extractor.d.ts +39 -0
- package/dist/core/group/extractors/include-extractor.js +565 -0
- package/dist/core/group/extractors/manifest-extractor.js +8 -0
- package/dist/core/group/extractors/thrift-extractor.js +8 -0
- package/dist/core/group/matching.js +2 -0
- package/dist/core/group/storage.js +8 -1
- package/dist/core/group/sync.js +33 -0
- package/dist/core/group/types.d.ts +2 -1
- package/dist/core/ingestion/call-processor.js +6 -4
- package/dist/core/ingestion/cpp-ue-preprocessor.d.ts +12 -0
- package/dist/core/ingestion/cpp-ue-preprocessor.js +260 -0
- package/dist/core/ingestion/heritage-processor.js +9 -4
- package/dist/core/ingestion/import-processor.js +3 -2
- package/dist/core/ingestion/language-provider.d.ts +32 -0
- package/dist/core/ingestion/languages/c-cpp.js +2 -0
- package/dist/core/ingestion/parsing-processor.js +4 -0
- package/dist/core/ingestion/workers/parse-worker.js +4 -0
- package/dist/core/wiki/llm-client.d.ts +16 -0
- package/dist/core/wiki/llm-client.js +101 -63
- package/dist/server/api.js +7 -2
- package/package.json +2 -2
- package/vendor/node_modules/node-addon-api/node_addon_api.target.mk +16 -14
- package/vendor/node_modules/node-addon-api/node_addon_api_except.target.mk +16 -14
- package/vendor/node_modules/node-addon-api/node_addon_api_except_all.target.mk +16 -14
- package/vendor/node_modules/node-addon-api/node_addon_api_maybe.target.mk +16 -14
- package/web/assets/{agent-Dl2d-mDi.js → agent-BpHQleHE.js} +17 -17
- package/web/assets/architecture-YZFGNWBL-S5CXDPWN-BUzkWgDl.js +1 -0
- package/web/assets/{architectureDiagram-EMZXCZ2Q-uiiIna83.js → architectureDiagram-EMZXCZ2Q-CXbzok1G.js} +3 -3
- package/web/assets/{blockDiagram-IGV67L2C-uBKdDzXA.js → blockDiagram-IGV67L2C-DA1QJ4ZB.js} +4 -4
- package/web/assets/{c4Diagram-DFAF54RM-DTxaX43T.js → c4Diagram-DFAF54RM-DeJV1rCn.js} +2 -2
- package/web/assets/{chunk-3GS5O3IE-BxKKu7d7.js → chunk-3GS5O3IE-B8hpnia8.js} +2 -2
- package/web/assets/{chunk-3YCYZ6SJ-BJZnGFjg.js → chunk-3YCYZ6SJ-B-BVu21C.js} +1 -1
- package/web/assets/{chunk-6NTNNK5N-CIL_eO2d.js → chunk-6NTNNK5N-DW-jYSDr.js} +1 -1
- package/web/assets/{chunk-A34GCYZU-Dv4Vkem9.js → chunk-A34GCYZU-BbZl-IKy.js} +1 -1
- package/web/assets/{chunk-DJ7UZH7F-Cskl-nb3.js → chunk-DJ7UZH7F-B5tMfF87.js} +1 -1
- package/web/assets/{chunk-DKKBVRCY-D3S2BD56.js → chunk-DKKBVRCY-Djm-6K_e.js} +2 -2
- package/web/assets/{chunk-DU5LTGQ6-B8Zh51LG.js → chunk-DU5LTGQ6-QPoynYcp.js} +1 -1
- package/web/assets/{chunk-FXACKDTF-Cb4xHVUz.js → chunk-FXACKDTF-jieSmbe2.js} +3 -3
- package/web/assets/chunk-H3VCZNTA-8EMHZ3SV.js +1 -0
- package/web/assets/chunk-HN6EAY2L-CgE_agsD.js +1 -0
- package/web/assets/{chunk-O5ABG6QK-V3sk7Bps.js → chunk-O5ABG6QK-DEZV1O19.js} +1 -1
- package/web/assets/{chunk-PK6DOVAG-FvG7aKfZ.js → chunk-PK6DOVAG-BXv0JnsJ.js} +3 -3
- package/web/assets/{chunk-RNJOYNJ4-BPANsy8V.js → chunk-RNJOYNJ4-BiBkF9dN.js} +1 -1
- package/web/assets/chunk-RWUO3TPN-DTWzAhQ1.js +1 -0
- package/web/assets/chunk-TBF5ZNIQ-BSl6s9lT.js +1 -0
- package/web/assets/{chunk-TYMNRAUI-BnMe6HfP.js → chunk-TYMNRAUI-D1NDLIMG.js} +1 -1
- package/web/assets/{chunk-W7ZLLLMY-BqZFKM5j.js → chunk-W7ZLLLMY-Bu-cdLzs.js} +1 -1
- package/web/assets/{chunk-WSB5WSVC-EKS7-Nnc.js → chunk-WSB5WSVC-Dor71SOz.js} +1 -1
- package/web/assets/{chunk-XGPFEOL4-BBab-enK.js → chunk-XGPFEOL4-CUonLRle.js} +1 -1
- package/web/assets/classDiagram-PPOCWD7C-Cdd6UdVv.js +1 -0
- package/web/assets/classDiagram-v2-23LJLIIU-DNhxY2Ib.js +1 -0
- package/web/assets/{cose-bilkent-PNC4W37J-DjEQkTZ4.js → cose-bilkent-PNC4W37J-DdZq7Qz_.js} +1 -1
- package/web/assets/dagre-E77IOHMT-Cfj_6e17.js +4 -0
- package/web/assets/{diagram-H7BISOXX-CMuISoJL.js → diagram-H7BISOXX-BNvpsrBv.js} +2 -2
- package/web/assets/{diagram-JC5VWROH-DHq_eoeM.js → diagram-JC5VWROH-Cn0rtDbJ.js} +1 -1
- package/web/assets/{diagram-LXUTUG65-C0NczIgW.js → diagram-LXUTUG65-CHfoVKNm.js} +2 -2
- package/web/assets/{diagram-WEHSV5V5-WsBnpSuc.js → diagram-WEHSV5V5-BLxHksnu.js} +1 -1
- package/web/assets/{erDiagram-GCSMX5X6-DxHFHFR6.js → erDiagram-GCSMX5X6-DfdN6iqF.js} +2 -2
- package/web/assets/flowDiagram-OTCZ4VVT-aAZw4c_M.js +162 -0
- package/web/assets/ganttDiagram-MUNLMDZQ-BEfC_7Or.js +292 -0
- package/web/assets/gitGraph-7Q5UKJZL-54BCDZD5-B6ZeRLP0.js +1 -0
- package/web/assets/gitGraphDiagram-3HKGZ4G3-CG0kC8Q5.js +106 -0
- package/web/assets/{index-DDcYO1IJ.js → index-3NUCUW2s.js} +119 -119
- package/web/assets/info-OMHHGYJF-BF2H5H6G-BIOsZy0W.js +1 -0
- package/web/assets/infoDiagram-MN7RKWGX-DUoMH-pq.js +2 -0
- package/web/assets/{ishikawaDiagram-YMYX4NHK-C-JI2Ih7.js → ishikawaDiagram-YMYX4NHK-BF8AtDaj.js} +2 -2
- package/web/assets/{journeyDiagram-SO5T7YLQ-IPX4h3iD.js → journeyDiagram-SO5T7YLQ-BW8oKa3N.js} +3 -3
- package/web/assets/{kanban-definition-LJHFXRCJ-B23LtshQ.js → kanban-definition-LJHFXRCJ-DzbOsR-2.js} +6 -6
- package/web/assets/{mindmap-definition-2EUWGEK5-DJKUsrCo.js → mindmap-definition-2EUWGEK5-BIXGQALq.js} +25 -25
- package/web/assets/packet-4T2RLAQJ-EV4IVRXR-Bs7ysNMU.js +1 -0
- package/web/assets/pie-ZZUOXDRM-N23DN5KN-Csi8nnbH.js +1 -0
- package/web/assets/{pieDiagram-3IATQBI2-Bj26KTKk.js → pieDiagram-3IATQBI2-QY-u7P6a.js} +3 -3
- package/web/assets/{quadrantDiagram-E256RVCF-VAk5oIRr.js → quadrantDiagram-E256RVCF-Di4aNiU8.js} +3 -3
- package/web/assets/radar-PYXPWWZC-P6TP7ZYP-DJlyXh2-.js +1 -0
- package/web/assets/{requirementDiagram-M5DCFWZL-CmlTJ_vQ.js → requirementDiagram-M5DCFWZL-CzUUrhQD.js} +4 -4
- package/web/assets/{sankeyDiagram-L3NBLAOT-C-f_DdcX.js → sankeyDiagram-L3NBLAOT-D2fbMiIU.js} +3 -3
- package/web/assets/{sequenceDiagram-ZOUHS735-DmtNf1NF.js → sequenceDiagram-ZOUHS735-BNdkqguh.js} +6 -6
- package/web/assets/stateDiagram-MLPALWAM-51_Q2w2L.js +1 -0
- package/web/assets/stateDiagram-v2-B5LQ5ZB2-8nHs2aNE.js +1 -0
- package/web/assets/{timeline-definition-5SPVSISX-BkpqZxt2.js → timeline-definition-5SPVSISX-DkMiGj4Z.js} +9 -9
- package/web/assets/treeView-SZITEDCU-5DXDK3XO-Cy6P0_CZ.js +1 -0
- package/web/assets/treemap-W4RFUUIX-WYLRDWKO-C_pp1R7A.js +1 -0
- package/web/assets/{vennDiagram-IE5QUKF5-iAJ08JBe.js → vennDiagram-IE5QUKF5-BCxMmf9q.js} +3 -3
- package/web/assets/wardley-RL74JXVD-BCRCBASE-CMStJhpn.js +1 -0
- package/web/assets/{wardleyDiagram-XU3VSMPF-BDiKTkL4.js → wardleyDiagram-XU3VSMPF-ijlLF77s.js} +4 -4
- package/web/assets/{xychartDiagram-ZHJ5623Y-CknyZyQK.js → xychartDiagram-ZHJ5623Y-DZ7fP1_3.js} +3 -3
- package/web/index.html +1 -1
- package/web/assets/architecture-YZFGNWBL-S5CXDPWN-BYXnTu07.js +0 -1
- package/web/assets/chunk-H3VCZNTA-BOGhqvxW.js +0 -1
- package/web/assets/chunk-HN6EAY2L-CitKFy1e.js +0 -1
- package/web/assets/chunk-RWUO3TPN-BHusg2jI.js +0 -1
- package/web/assets/chunk-TBF5ZNIQ-CXamrpH3.js +0 -1
- package/web/assets/classDiagram-PPOCWD7C-B-LkKU0P.js +0 -1
- package/web/assets/classDiagram-v2-23LJLIIU-_NGI4VEh.js +0 -1
- package/web/assets/dagre-E77IOHMT-D6Xacg1x.js +0 -4
- package/web/assets/flowDiagram-OTCZ4VVT-_G1VxyQS.js +0 -162
- package/web/assets/ganttDiagram-MUNLMDZQ-BIHObj0t.js +0 -292
- package/web/assets/gitGraph-7Q5UKJZL-54BCDZD5-BXJXsixL.js +0 -1
- package/web/assets/gitGraphDiagram-3HKGZ4G3-COZUkHTD.js +0 -106
- package/web/assets/info-OMHHGYJF-BF2H5H6G-C820LK0J.js +0 -1
- package/web/assets/infoDiagram-MN7RKWGX-BeOjfPc_.js +0 -2
- package/web/assets/packet-4T2RLAQJ-EV4IVRXR-BvWtM5jq.js +0 -1
- package/web/assets/pie-ZZUOXDRM-N23DN5KN-BnDgj0TQ.js +0 -1
- package/web/assets/radar-PYXPWWZC-P6TP7ZYP-2IuDlZ6r.js +0 -1
- package/web/assets/stateDiagram-MLPALWAM-Lq7UVrkj.js +0 -1
- package/web/assets/stateDiagram-v2-B5LQ5ZB2-DCiXBPeS.js +0 -1
- package/web/assets/treeView-SZITEDCU-5DXDK3XO-Ipq1EIaF.js +0 -1
- package/web/assets/treemap-W4RFUUIX-WYLRDWKO-DESFxovu.js +0 -1
- package/web/assets/wardley-RL74JXVD-BCRCBASE-BVkhckBY.js +0 -1
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `resilientFetch` — fetch wrapped in retry + circuit breaker, with
|
|
3
|
+
* GitHub-flavoured retry classification baked in (Retry-After parsing,
|
|
4
|
+
* 401/403/404/422 treated as terminal client errors).
|
|
5
|
+
*
|
|
6
|
+
* Designed for the `gitnexus publish` GitHub `repository_dispatch`
|
|
7
|
+
* call, but the classification rules apply to any GitHub REST endpoint.
|
|
8
|
+
* Runtime-agnostic — no Node-only imports.
|
|
9
|
+
*/
|
|
10
|
+
import { CircuitOpenError, getBreaker, } from './circuit-breaker.js';
|
|
11
|
+
import { computeBackoffMs } from './retry.js';
|
|
12
|
+
export { CircuitOpenError };
|
|
13
|
+
/** Cap on any single Retry-After wait — protects CLI from a buggy registry. */
|
|
14
|
+
export const RETRY_AFTER_CAP_MS = 30_000;
|
|
15
|
+
const DEFAULT_RETRY = {
|
|
16
|
+
maxAttempts: 3,
|
|
17
|
+
baseDelayMs: 500,
|
|
18
|
+
capDelayMs: 5_000,
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Parse a `Retry-After` header value into milliseconds.
|
|
22
|
+
* Accepts either a delta-seconds integer (`"30"`) or an HTTP-date.
|
|
23
|
+
* Returns null on parse failure or negative deltas.
|
|
24
|
+
*/
|
|
25
|
+
export function parseRetryAfter(value, now = Date.now) {
|
|
26
|
+
if (!value)
|
|
27
|
+
return null;
|
|
28
|
+
const trimmed = value.trim();
|
|
29
|
+
if (trimmed === '')
|
|
30
|
+
return null;
|
|
31
|
+
if (/^[0-9]+$/.test(trimmed)) {
|
|
32
|
+
const seconds = parseInt(trimmed, 10);
|
|
33
|
+
if (Number.isNaN(seconds) || seconds < 0)
|
|
34
|
+
return null;
|
|
35
|
+
return seconds * 1000;
|
|
36
|
+
}
|
|
37
|
+
const target = Date.parse(trimmed);
|
|
38
|
+
if (Number.isNaN(target))
|
|
39
|
+
return null;
|
|
40
|
+
const delta = target - now();
|
|
41
|
+
return delta >= 0 ? delta : 0;
|
|
42
|
+
}
|
|
43
|
+
/** Exported for unit tests. */
|
|
44
|
+
export function classifyOutcome(result, now) {
|
|
45
|
+
if (result.kind === 'error') {
|
|
46
|
+
// Both timer-fired aborts (`AbortSignal.timeout()` → `TimeoutError`)
|
|
47
|
+
// and caller-driven aborts (`AbortController.abort()` → `AbortError`)
|
|
48
|
+
// are terminal: retrying against an already-aborted signal would
|
|
49
|
+
// fail again immediately, and neither outcome reflects backend
|
|
50
|
+
// health. They route through the breaker's neutral path.
|
|
51
|
+
if (result.err instanceof DOMException &&
|
|
52
|
+
(result.err.name === 'TimeoutError' || result.err.name === 'AbortError')) {
|
|
53
|
+
return { kind: 'terminal-network', err: result.err };
|
|
54
|
+
}
|
|
55
|
+
return { kind: 'retryable-network', err: result.err };
|
|
56
|
+
}
|
|
57
|
+
const resp = result.resp;
|
|
58
|
+
if (resp.status >= 200 && resp.status < 400)
|
|
59
|
+
return { kind: 'success', resp };
|
|
60
|
+
if (resp.status === 429) {
|
|
61
|
+
// `resp.headers` is always present on a real `Response`, but tests
|
|
62
|
+
// sometimes stub `fetch` with a plain `{ ok, status }` object. Be
|
|
63
|
+
// defensive — a missing `Retry-After` falls through to exponential
|
|
64
|
+
// backoff, which is the correct behaviour anyway.
|
|
65
|
+
const retryAfterHeader = typeof resp.headers?.get === 'function' ? resp.headers.get('Retry-After') : null;
|
|
66
|
+
const parsed = parseRetryAfter(retryAfterHeader, now);
|
|
67
|
+
return {
|
|
68
|
+
kind: 'retryable-status',
|
|
69
|
+
resp,
|
|
70
|
+
afterMs: parsed !== null ? Math.min(parsed, RETRY_AFTER_CAP_MS) : undefined,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (resp.status >= 500)
|
|
74
|
+
return { kind: 'retryable-status', resp, afterMs: undefined };
|
|
75
|
+
return { kind: 'terminal-client', resp };
|
|
76
|
+
}
|
|
77
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
78
|
+
function defaultBreakerKey(input) {
|
|
79
|
+
try {
|
|
80
|
+
const url = typeof input === 'string' ? new URL(input) : input;
|
|
81
|
+
return `${url.host}${url.pathname}`;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return String(input);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Final error thrown when retries are exhausted on a 5xx / 429. */
|
|
88
|
+
export class ResilientFetchExhaustedError extends Error {
|
|
89
|
+
response;
|
|
90
|
+
name = 'ResilientFetchExhaustedError';
|
|
91
|
+
constructor(response) {
|
|
92
|
+
super(`Request failed after retries (HTTP ${response.status})`);
|
|
93
|
+
this.response = response;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Wrap `fetch` with bounded retries and a per-process circuit breaker.
|
|
98
|
+
*
|
|
99
|
+
* Semantics:
|
|
100
|
+
* - 5xx and 429 responses are retried; 429 honors `Retry-After` (capped).
|
|
101
|
+
* - Network throws are retried unless they are `TimeoutError` DOMExceptions.
|
|
102
|
+
* - Timeouts and 4xx (other than 429) are returned/thrown without retry
|
|
103
|
+
* AND without incrementing the breaker — they reflect caller config
|
|
104
|
+
* or local network state, not registry health.
|
|
105
|
+
* - Each `fetch` call carries the caller-supplied `signal` (e.g. an
|
|
106
|
+
* `AbortSignal.timeout()`) — that timeout bounds each individual
|
|
107
|
+
* attempt, not the whole retry sequence.
|
|
108
|
+
* - When the breaker is open, throws `CircuitOpenError` synchronously
|
|
109
|
+
* without invoking `fetch`.
|
|
110
|
+
* - When retries are exhausted on a 5xx / 429, throws
|
|
111
|
+
* `ResilientFetchExhaustedError` carrying the last response.
|
|
112
|
+
*
|
|
113
|
+
* Cumulative wall-clock budget:
|
|
114
|
+
* maxAttempts × (per-attempt-timeout + capDelayMs)
|
|
115
|
+
* With defaults (3, 500ms base, 5000ms cap) and a typical 15s per-attempt
|
|
116
|
+
* timeout from the caller's signal, worst case is ~3 × (15s + 5s) = 60s.
|
|
117
|
+
* Callers that want a tighter total bound should reduce `maxAttempts` or
|
|
118
|
+
* wrap `resilientFetch` in their own outer `AbortSignal.timeout()`.
|
|
119
|
+
*/
|
|
120
|
+
export async function resilientFetch(input, init, opts = {}) {
|
|
121
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
122
|
+
const now = opts.now ?? (() => Date.now());
|
|
123
|
+
const breaker = opts.breaker ?? getBreaker(opts.breakerKey ?? defaultBreakerKey(input), opts.breakerOptions);
|
|
124
|
+
const retryConfig = {
|
|
125
|
+
maxAttempts: opts.retry?.maxAttempts ?? DEFAULT_RETRY.maxAttempts,
|
|
126
|
+
baseDelayMs: opts.retry?.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs,
|
|
127
|
+
capDelayMs: opts.retry?.capDelayMs ?? DEFAULT_RETRY.capDelayMs,
|
|
128
|
+
};
|
|
129
|
+
const sleep = opts.retry?.sleep ?? defaultSleep;
|
|
130
|
+
const random = opts.retry?.random ?? Math.random;
|
|
131
|
+
// Fail fast on an open breaker, before invoking fetch.
|
|
132
|
+
breaker.check();
|
|
133
|
+
for (let attempt = 0; attempt < retryConfig.maxAttempts; attempt++) {
|
|
134
|
+
let result;
|
|
135
|
+
try {
|
|
136
|
+
// CodeQL js/server-side-request-forgery — flagged because `input`
|
|
137
|
+
// is caller-supplied. Suppressed: every concrete caller passes
|
|
138
|
+
// either a hardcoded URL constant (UNDERSTAND_QUICKLY_DISPATCH_URL,
|
|
139
|
+
// OpenRouter base URL) or a value derived from configuration
|
|
140
|
+
// (env vars, saved settings, the local backend URL). User-input
|
|
141
|
+
// request fields (e.g. PR title, repo name) never flow into
|
|
142
|
+
// `input`. Validating URL shape here would push false-positive
|
|
143
|
+
// rejection onto every caller — wrong layer for the check.
|
|
144
|
+
// lgtm[js/server-side-request-forgery]
|
|
145
|
+
// codeql[js/server-side-request-forgery]
|
|
146
|
+
const resp = await fetchImpl(input, init);
|
|
147
|
+
result = { kind: 'response', resp };
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
result = { kind: 'error', err };
|
|
151
|
+
}
|
|
152
|
+
const outcome = classifyOutcome(result, now);
|
|
153
|
+
switch (outcome.kind) {
|
|
154
|
+
case 'success':
|
|
155
|
+
breaker.recordSuccess();
|
|
156
|
+
return outcome.resp;
|
|
157
|
+
case 'terminal-client':
|
|
158
|
+
// 4xx: do not count as breaker failure (the server is healthy
|
|
159
|
+
// and rejecting our request — auth, scope, or routing). But
|
|
160
|
+
// also do NOT call recordSuccess: a 401 sandwiched between
|
|
161
|
+
// 5xx responses would otherwise erase the running outage
|
|
162
|
+
// signal. The breaker's neutral path leaves state untouched.
|
|
163
|
+
breaker.recordNeutral();
|
|
164
|
+
return outcome.resp;
|
|
165
|
+
case 'terminal-network':
|
|
166
|
+
// Either `AbortSignal.timeout()` fired locally OR an external
|
|
167
|
+
// caller cancelled the request via AbortController. The server
|
|
168
|
+
// never had a chance to answer; this reflects the user's
|
|
169
|
+
// network or an explicit cancel, not registry health. Don't
|
|
170
|
+
// punish the breaker AND don't reset its outage signal.
|
|
171
|
+
breaker.recordNeutral();
|
|
172
|
+
throw outcome.err;
|
|
173
|
+
case 'retryable-status':
|
|
174
|
+
if (attempt + 1 >= retryConfig.maxAttempts) {
|
|
175
|
+
breaker.recordFailure();
|
|
176
|
+
throw new ResilientFetchExhaustedError(outcome.resp);
|
|
177
|
+
}
|
|
178
|
+
await sleep(computeBackoffMs(attempt, retryConfig.baseDelayMs, retryConfig.capDelayMs, outcome.afterMs, random));
|
|
179
|
+
break;
|
|
180
|
+
case 'retryable-network':
|
|
181
|
+
if (attempt + 1 >= retryConfig.maxAttempts) {
|
|
182
|
+
breaker.recordFailure();
|
|
183
|
+
throw outcome.err;
|
|
184
|
+
}
|
|
185
|
+
await sleep(computeBackoffMs(attempt, retryConfig.baseDelayMs, retryConfig.capDelayMs, undefined, random));
|
|
186
|
+
break;
|
|
187
|
+
default: {
|
|
188
|
+
// Exhaustiveness guard. If a sixth `Outcome` kind is added in
|
|
189
|
+
// future, TypeScript will refuse to assign it to `never` and
|
|
190
|
+
// this line forces the maintainer to add an explicit arm
|
|
191
|
+
// rather than silently fall through to retry/no-retry behaviour.
|
|
192
|
+
const _exhaustive = outcome;
|
|
193
|
+
throw new Error(`resilientFetch: unhandled outcome ${JSON.stringify(_exhaustive)}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Unreachable: every iteration of the loop either returns (success
|
|
198
|
+
// / terminal-client) or throws (terminal-network / retry exhaustion).
|
|
199
|
+
// The throw is here purely so TypeScript's control-flow analysis sees
|
|
200
|
+
// the function never falls off the end without producing `Promise<Response>`.
|
|
201
|
+
/* c8 ignore next 2 */
|
|
202
|
+
throw new Error('resilientFetch: retry loop terminated unexpectedly');
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=resilient-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resilient-fetch.js","sourceRoot":"","sources":["../../src/integrations/resilient-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAEL,gBAAgB,EAChB,UAAU,GAEX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAqB,MAAM,YAAY,CAAC;AAEjE,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAwB5B,+EAA+E;AAC/E,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAEzC,MAAM,aAAa,GAAG;IACpB,WAAW,EAAE,CAAC;IACd,WAAW,EAAE,GAAG;IAChB,UAAU,EAAE,KAAK;CAClB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAoB,EAAE,MAAoB,IAAI,CAAC,GAAG;IAChF,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAEhC,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACtD,OAAO,OAAO,GAAG,IAAI,CAAC;IACxB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,KAAK,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC;IAC7B,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAChC,CAAC;AAUD,+BAA+B;AAC/B,MAAM,UAAU,eAAe,CAC7B,MAA8E,EAC9E,GAAiB;IAEjB,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,qEAAqE;QACrE,sEAAsE;QACtE,iEAAiE;QACjE,+DAA+D;QAC/D,yDAAyD;QACzD,IACE,MAAM,CAAC,GAAG,YAAY,YAAY;YAClC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,cAAc,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,EACxE,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;QACvD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;IACxD,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACzB,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC9E,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACxB,mEAAmE;QACnE,kEAAkE;QAClE,mEAAmE;QACnE,kDAAkD;QAClD,MAAM,gBAAgB,GACpB,OAAO,IAAI,CAAC,OAAO,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACnF,MAAM,MAAM,GAAG,eAAe,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;QACtD,OAAO;YACL,IAAI,EAAE,kBAAkB;YACxB,IAAI;YACJ,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,SAAS;SAC5E,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IACtF,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;AAC3C,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,EAAU,EAAiB,EAAE,CACjD,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAEpD,SAAS,iBAAiB,CAAC,KAAmB;IAC5C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC/D,OAAO,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IAEzB;IADV,IAAI,GAAG,8BAA8B,CAAC;IACxD,YAA4B,QAAkB;QAC5C,KAAK,CAAC,sCAAsC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QADtC,aAAQ,GAAR,QAAQ,CAAU;IAE9C,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAmB,EACnB,IAA6B,EAC7B,OAA8B,EAAE;IAEhC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;IACrD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC3C,MAAM,OAAO,GACX,IAAI,CAAC,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,IAAI,iBAAiB,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAE/F,MAAM,WAAW,GAAG;QAClB,WAAW,EAAE,IAAI,CAAC,KAAK,EAAE,WAAW,IAAI,aAAa,CAAC,WAAW;QACjE,WAAW,EAAE,IAAI,CAAC,KAAK,EAAE,WAAW,IAAI,aAAa,CAAC,WAAW;QACjE,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,UAAU,IAAI,aAAa,CAAC,UAAU;KAC/D,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,IAAI,YAAY,CAAC;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;IAEjD,uDAAuD;IACvD,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACnE,IAAI,MAA8E,CAAC;QACnF,IAAI,CAAC;YACH,kEAAkE;YAClE,+DAA+D;YAC/D,oEAAoE;YACpE,6DAA6D;YAC7D,gEAAgE;YAChE,4DAA4D;YAC5D,+DAA+D;YAC/D,2DAA2D;YAC3D,uCAAuC;YACvC,yCAAyC;YACzC,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC1C,MAAM,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;QAClC,CAAC;QAED,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE7C,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;YACrB,KAAK,SAAS;gBACZ,OAAO,CAAC,aAAa,EAAE,CAAC;gBACxB,OAAO,OAAO,CAAC,IAAI,CAAC;YAEtB,KAAK,iBAAiB;gBACpB,8DAA8D;gBAC9D,4DAA4D;gBAC5D,2DAA2D;gBAC3D,yDAAyD;gBACzD,6DAA6D;gBAC7D,OAAO,CAAC,aAAa,EAAE,CAAC;gBACxB,OAAO,OAAO,CAAC,IAAI,CAAC;YAEtB,KAAK,kBAAkB;gBACrB,8DAA8D;gBAC9D,+DAA+D;gBAC/D,yDAAyD;gBACzD,4DAA4D;gBAC5D,wDAAwD;gBACxD,OAAO,CAAC,aAAa,EAAE,CAAC;gBACxB,MAAM,OAAO,CAAC,GAAG,CAAC;YAEpB,KAAK,kBAAkB;gBACrB,IAAI,OAAO,GAAG,CAAC,IAAI,WAAW,CAAC,WAAW,EAAE,CAAC;oBAC3C,OAAO,CAAC,aAAa,EAAE,CAAC;oBACxB,MAAM,IAAI,4BAA4B,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACvD,CAAC;gBACD,MAAM,KAAK,CACT,gBAAgB,CACd,OAAO,EACP,WAAW,CAAC,WAAW,EACvB,WAAW,CAAC,UAAU,EACtB,OAAO,CAAC,OAAO,EACf,MAAM,CACP,CACF,CAAC;gBACF,MAAM;YAER,KAAK,mBAAmB;gBACtB,IAAI,OAAO,GAAG,CAAC,IAAI,WAAW,CAAC,WAAW,EAAE,CAAC;oBAC3C,OAAO,CAAC,aAAa,EAAE,CAAC;oBACxB,MAAM,OAAO,CAAC,GAAG,CAAC;gBACpB,CAAC;gBACD,MAAM,KAAK,CACT,gBAAgB,CACd,OAAO,EACP,WAAW,CAAC,WAAW,EACvB,WAAW,CAAC,UAAU,EACtB,SAAS,EACT,MAAM,CACP,CACF,CAAC;gBACF,MAAM;YAER,OAAO,CAAC,CAAC,CAAC;gBACR,8DAA8D;gBAC9D,6DAA6D;gBAC7D,yDAAyD;gBACzD,iEAAiE;gBACjE,MAAM,WAAW,GAAU,OAAO,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YACtF,CAAC;QACH,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,sEAAsE;IACtE,sEAAsE;IACtE,8EAA8E;IAC9E,sBAAsB;IACtB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;AACxE,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded retry helper with full-jitter exponential backoff.
|
|
3
|
+
*
|
|
4
|
+
* Runtime-agnostic: depends only on `setTimeout`, `Math.random`, and the
|
|
5
|
+
* Promise machinery — no Node-only imports. Safe to consume from CLI,
|
|
6
|
+
* server, or browser callers.
|
|
7
|
+
*
|
|
8
|
+
* Pattern reference: gitnexus/src/core/embeddings/http-client.ts. This
|
|
9
|
+
* helper is the upgraded form: classification is caller-supplied (so
|
|
10
|
+
* 4xx-vs-5xx-vs-timeout decisions live with the protocol that knows
|
|
11
|
+
* them), backoff is exponential with full jitter, and an optional
|
|
12
|
+
* `afterMs` lets callers honor `Retry-After` headers.
|
|
13
|
+
*/
|
|
14
|
+
export interface RetryOptions {
|
|
15
|
+
/** Initial delay before the first retry attempt, in milliseconds. */
|
|
16
|
+
baseDelayMs: number;
|
|
17
|
+
/** Upper bound on any single delay, in milliseconds. */
|
|
18
|
+
capDelayMs: number;
|
|
19
|
+
/** Total attempts including the first call. Must be >= 1. */
|
|
20
|
+
maxAttempts: number;
|
|
21
|
+
/**
|
|
22
|
+
* Decide whether to retry after a thrown error.
|
|
23
|
+
* Return `{retry:false}` to terminate immediately and rethrow.
|
|
24
|
+
* Return `{retry:true}` to retry with exponential-backoff jitter.
|
|
25
|
+
* Return `{retry:true, afterMs}` to wait at least `afterMs` (still
|
|
26
|
+
* subject to `capDelayMs`) — used by callers parsing `Retry-After`.
|
|
27
|
+
*/
|
|
28
|
+
isRetryable: (err: unknown, attempt: number) => RetryDecision;
|
|
29
|
+
/** Sleep override — defaults to `setTimeout`. Tests inject fake timers. */
|
|
30
|
+
sleep?: (ms: number) => Promise<void>;
|
|
31
|
+
/** Random override — defaults to `Math.random`. Tests inject seeded values. */
|
|
32
|
+
random?: () => number;
|
|
33
|
+
}
|
|
34
|
+
export type RetryDecision = {
|
|
35
|
+
retry: false;
|
|
36
|
+
} | {
|
|
37
|
+
retry: true;
|
|
38
|
+
afterMs?: number;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Compute the delay before the next retry attempt.
|
|
42
|
+
*
|
|
43
|
+
* - When the caller specifies `afterMs` (e.g., from `Retry-After`), use
|
|
44
|
+
* `min(afterMs, capDelayMs)` so a misbehaving server can't pin the
|
|
45
|
+
* client for an arbitrarily long wait.
|
|
46
|
+
* - Otherwise compute full-jitter exponential backoff:
|
|
47
|
+
* `random() * min(cap, base * 2^attempt)`. Full jitter (rather than
|
|
48
|
+
* "equal jitter") avoids retry-storm thundering herd, per AWS
|
|
49
|
+
* guidance on backoff strategies.
|
|
50
|
+
*/
|
|
51
|
+
export declare function computeBackoffMs(attempt: number, baseDelayMs: number, capDelayMs: number, afterMs: number | undefined, random: () => number): number;
|
|
52
|
+
/**
|
|
53
|
+
* Execute `fn` with bounded retries.
|
|
54
|
+
*
|
|
55
|
+
* The classification of "retryable" is the caller's responsibility — see
|
|
56
|
+
* `resilient-fetch.ts` for the GitHub-dispatch-specific rules. This
|
|
57
|
+
* helper is the mechanical retry loop only.
|
|
58
|
+
*/
|
|
59
|
+
export declare function withRetry<T>(fn: (attempt: number) => Promise<T>, opts: RetryOptions): Promise<T>;
|
|
60
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/integrations/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,aAAa,CAAC;IAC9D,2EAA2E;IAC3E,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAKjF;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,MAAM,EAAE,MAAM,MAAM,GACnB,MAAM,CAOR;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,CAAC,CAAC,CA4BZ"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded retry helper with full-jitter exponential backoff.
|
|
3
|
+
*
|
|
4
|
+
* Runtime-agnostic: depends only on `setTimeout`, `Math.random`, and the
|
|
5
|
+
* Promise machinery — no Node-only imports. Safe to consume from CLI,
|
|
6
|
+
* server, or browser callers.
|
|
7
|
+
*
|
|
8
|
+
* Pattern reference: gitnexus/src/core/embeddings/http-client.ts. This
|
|
9
|
+
* helper is the upgraded form: classification is caller-supplied (so
|
|
10
|
+
* 4xx-vs-5xx-vs-timeout decisions live with the protocol that knows
|
|
11
|
+
* them), backoff is exponential with full jitter, and an optional
|
|
12
|
+
* `afterMs` lets callers honor `Retry-After` headers.
|
|
13
|
+
*/
|
|
14
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
15
|
+
/**
|
|
16
|
+
* Compute the delay before the next retry attempt.
|
|
17
|
+
*
|
|
18
|
+
* - When the caller specifies `afterMs` (e.g., from `Retry-After`), use
|
|
19
|
+
* `min(afterMs, capDelayMs)` so a misbehaving server can't pin the
|
|
20
|
+
* client for an arbitrarily long wait.
|
|
21
|
+
* - Otherwise compute full-jitter exponential backoff:
|
|
22
|
+
* `random() * min(cap, base * 2^attempt)`. Full jitter (rather than
|
|
23
|
+
* "equal jitter") avoids retry-storm thundering herd, per AWS
|
|
24
|
+
* guidance on backoff strategies.
|
|
25
|
+
*/
|
|
26
|
+
export function computeBackoffMs(attempt, baseDelayMs, capDelayMs, afterMs, random) {
|
|
27
|
+
if (afterMs !== undefined) {
|
|
28
|
+
return Math.min(Math.max(0, afterMs), capDelayMs);
|
|
29
|
+
}
|
|
30
|
+
const exponential = baseDelayMs * Math.pow(2, attempt);
|
|
31
|
+
const upper = Math.min(capDelayMs, exponential);
|
|
32
|
+
return Math.floor(random() * upper);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Execute `fn` with bounded retries.
|
|
36
|
+
*
|
|
37
|
+
* The classification of "retryable" is the caller's responsibility — see
|
|
38
|
+
* `resilient-fetch.ts` for the GitHub-dispatch-specific rules. This
|
|
39
|
+
* helper is the mechanical retry loop only.
|
|
40
|
+
*/
|
|
41
|
+
export async function withRetry(fn, opts) {
|
|
42
|
+
if (opts.maxAttempts < 1) {
|
|
43
|
+
throw new Error(`withRetry: maxAttempts must be >= 1, got ${opts.maxAttempts}`);
|
|
44
|
+
}
|
|
45
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
46
|
+
const random = opts.random ?? Math.random;
|
|
47
|
+
let lastError;
|
|
48
|
+
for (let attempt = 0; attempt < opts.maxAttempts; attempt++) {
|
|
49
|
+
try {
|
|
50
|
+
return await fn(attempt);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
lastError = err;
|
|
54
|
+
const decision = opts.isRetryable(err, attempt);
|
|
55
|
+
if (!decision.retry)
|
|
56
|
+
throw err;
|
|
57
|
+
// Don't sleep after the final attempt.
|
|
58
|
+
if (attempt + 1 >= opts.maxAttempts)
|
|
59
|
+
break;
|
|
60
|
+
const delayMs = computeBackoffMs(attempt, opts.baseDelayMs, opts.capDelayMs, decision.afterMs, random);
|
|
61
|
+
if (delayMs > 0)
|
|
62
|
+
await sleep(delayMs);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw lastError;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/integrations/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAyBH,MAAM,YAAY,GAAG,CAAC,EAAU,EAAiB,EAAE,CACjD,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAEpD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,WAAmB,EACnB,UAAkB,EAClB,OAA2B,EAC3B,MAAoB;IAEpB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,WAAW,GAAG,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAChD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,EAAmC,EACnC,IAAkB;IAElB,IAAI,IAAI,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,4CAA4C,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,YAAY,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;IAE1C,IAAI,SAAkB,CAAC;IACvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QAC5D,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,GAAG,GAAG,CAAC;YAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAChD,IAAI,CAAC,QAAQ,CAAC,KAAK;gBAAE,MAAM,GAAG,CAAC;YAC/B,uCAAuC;YACvC,IAAI,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,WAAW;gBAAE,MAAM;YAC3C,MAAM,OAAO,GAAG,gBAAgB,CAC9B,OAAO,EACP,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,UAAU,EACf,QAAQ,CAAC,OAAO,EAChB,MAAM,CACP,CAAC;YACF,IAAI,OAAO,GAAG,CAAC;gBAAE,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,MAAM,SAAS,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Understand-Quickly registry integration helpers.
|
|
3
|
+
*
|
|
4
|
+
* Pure, runtime-agnostic logic for opting in to publishing a GitNexus
|
|
5
|
+
* index to the [`looptech-ai/understand-quickly`](https://github.com/looptech-ai/understand-quickly)
|
|
6
|
+
* registry. Lives in `gitnexus-shared` so both the Node CLI and any
|
|
7
|
+
* future browser-side surface can construct identical dispatch payloads.
|
|
8
|
+
*
|
|
9
|
+
* Network I/O lives in the CLI command (`gitnexus/src/cli/publish.ts`)
|
|
10
|
+
* to keep this module free of Node-only imports — see the comment at
|
|
11
|
+
* the top of `gitnexus-shared/src/graph/types.ts`.
|
|
12
|
+
*
|
|
13
|
+
* The protocol contract (single dispatch event, no graph upload) is
|
|
14
|
+
* documented at:
|
|
15
|
+
* https://github.com/looptech-ai/understand-quickly/blob/main/docs/integrations/protocol.md
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* URL of the registry repo's repository_dispatch endpoint. Hardcoded
|
|
19
|
+
* because the registry is the canonical home for this integration —
|
|
20
|
+
* users who want a private registry can fork and patch.
|
|
21
|
+
*/
|
|
22
|
+
export declare const UNDERSTAND_QUICKLY_DISPATCH_URL = "https://api.github.com/repos/looptech-ai/understand-quickly/dispatches";
|
|
23
|
+
/**
|
|
24
|
+
* Event type the registry's sync workflow listens for.
|
|
25
|
+
* See `looptech-ai/understand-quickly/.github/workflows/sync.yml`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const UNDERSTAND_QUICKLY_EVENT_TYPE = "sync-entry";
|
|
28
|
+
/** Environment variable that gates the dispatch. */
|
|
29
|
+
export declare const UNDERSTAND_QUICKLY_TOKEN_ENV = "UNDERSTAND_QUICKLY_TOKEN";
|
|
30
|
+
export interface UqDispatchPayload {
|
|
31
|
+
event_type: typeof UNDERSTAND_QUICKLY_EVENT_TYPE;
|
|
32
|
+
client_payload: {
|
|
33
|
+
/** `<owner>/<repo>` shape — must match the registered entry. */
|
|
34
|
+
id: string;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the JSON body for the `repository_dispatch` ping. Pure — no
|
|
39
|
+
* env reads, no network. Validates that `id` looks like `owner/repo`
|
|
40
|
+
* (one slash, no whitespace, both halves non-empty) so a misconfigured
|
|
41
|
+
* caller fails loudly before the round-trip.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildUqDispatchPayload(id: string): UqDispatchPayload;
|
|
44
|
+
/**
|
|
45
|
+
* `owner/repo` validation. Conservative on purpose: GitHub's actual
|
|
46
|
+
* naming rules are looser, but we want to catch local paths
|
|
47
|
+
* (`/Users/...`), bare slugs (`my-repo`), and accidental whitespace.
|
|
48
|
+
*
|
|
49
|
+
* Matches GitHub's published slug rules:
|
|
50
|
+
* owner: starts with alnum, then alnum/hyphen only, must end with
|
|
51
|
+
* alnum (no trailing hyphen — GitHub rejects this at account
|
|
52
|
+
* creation, so a `my-org-/repo` input would otherwise pass us
|
|
53
|
+
* and 422 from GitHub). No underscore, no dot. Length cap 39.
|
|
54
|
+
* repo: any of alnum/dot/hyphen/underscore. Length cap 100.
|
|
55
|
+
*/
|
|
56
|
+
export declare function isValidOwnerRepo(id: string): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Strip a single trailing `.git` (case-insensitive) and any trailing
|
|
59
|
+
* slashes from a URL-ish string. Bounded linear: each character is
|
|
60
|
+
* visited at most twice, no backtracking.
|
|
61
|
+
*
|
|
62
|
+
* Replaces `s.replace(/\.git\/*$/i, '').replace(/\/+$/, '')` which
|
|
63
|
+
* CodeQL's polynomial-regex check (codeql/js/polynomial-redos) flags as
|
|
64
|
+
* a worst-case O(n²) on adversarial input like "////.../x".
|
|
65
|
+
*/
|
|
66
|
+
export declare function stripGitSuffix(input: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* Parse `owner/repo` out of a git remote URL. Mirrors the heuristic in
|
|
69
|
+
* `gitnexus/src/storage/git.ts:parseRepoNameFromUrl` but keeps both
|
|
70
|
+
* halves so we can build a registry id. Returns `null` on shapes we
|
|
71
|
+
* don't recognise.
|
|
72
|
+
*
|
|
73
|
+
* Examples:
|
|
74
|
+
* git@github.com:looptech-ai/understand-quickly.git
|
|
75
|
+
* https://github.com/looptech-ai/understand-quickly
|
|
76
|
+
* ssh://git@github.com/looptech-ai/understand-quickly.git
|
|
77
|
+
*/
|
|
78
|
+
export declare function parseOwnerRepoFromRemote(url: string | null | undefined): string | null;
|
|
79
|
+
//# sourceMappingURL=understand-quickly.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"understand-quickly.d.ts","sourceRoot":"","sources":["../../src/integrations/understand-quickly.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,2EAC8B,CAAC;AAE3E;;;GAGG;AACH,eAAO,MAAM,6BAA6B,eAAe,CAAC;AAE1D,oDAAoD;AACpD,eAAO,MAAM,4BAA4B,6BAA6B,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,OAAO,6BAA6B,CAAC;IACjD,cAAc,EAAE;QACd,gEAAgE;QAChE,EAAE,EAAE,MAAM,CAAC;KACZ,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,CAYpE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAsCtF"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Understand-Quickly registry integration helpers.
|
|
3
|
+
*
|
|
4
|
+
* Pure, runtime-agnostic logic for opting in to publishing a GitNexus
|
|
5
|
+
* index to the [`looptech-ai/understand-quickly`](https://github.com/looptech-ai/understand-quickly)
|
|
6
|
+
* registry. Lives in `gitnexus-shared` so both the Node CLI and any
|
|
7
|
+
* future browser-side surface can construct identical dispatch payloads.
|
|
8
|
+
*
|
|
9
|
+
* Network I/O lives in the CLI command (`gitnexus/src/cli/publish.ts`)
|
|
10
|
+
* to keep this module free of Node-only imports — see the comment at
|
|
11
|
+
* the top of `gitnexus-shared/src/graph/types.ts`.
|
|
12
|
+
*
|
|
13
|
+
* The protocol contract (single dispatch event, no graph upload) is
|
|
14
|
+
* documented at:
|
|
15
|
+
* https://github.com/looptech-ai/understand-quickly/blob/main/docs/integrations/protocol.md
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* URL of the registry repo's repository_dispatch endpoint. Hardcoded
|
|
19
|
+
* because the registry is the canonical home for this integration —
|
|
20
|
+
* users who want a private registry can fork and patch.
|
|
21
|
+
*/
|
|
22
|
+
export const UNDERSTAND_QUICKLY_DISPATCH_URL = 'https://api.github.com/repos/looptech-ai/understand-quickly/dispatches';
|
|
23
|
+
/**
|
|
24
|
+
* Event type the registry's sync workflow listens for.
|
|
25
|
+
* See `looptech-ai/understand-quickly/.github/workflows/sync.yml`.
|
|
26
|
+
*/
|
|
27
|
+
export const UNDERSTAND_QUICKLY_EVENT_TYPE = 'sync-entry';
|
|
28
|
+
/** Environment variable that gates the dispatch. */
|
|
29
|
+
export const UNDERSTAND_QUICKLY_TOKEN_ENV = 'UNDERSTAND_QUICKLY_TOKEN';
|
|
30
|
+
/**
|
|
31
|
+
* Build the JSON body for the `repository_dispatch` ping. Pure — no
|
|
32
|
+
* env reads, no network. Validates that `id` looks like `owner/repo`
|
|
33
|
+
* (one slash, no whitespace, both halves non-empty) so a misconfigured
|
|
34
|
+
* caller fails loudly before the round-trip.
|
|
35
|
+
*/
|
|
36
|
+
export function buildUqDispatchPayload(id) {
|
|
37
|
+
if (!isValidOwnerRepo(id)) {
|
|
38
|
+
throw new Error(`[understand-quickly] expected id of the form "owner/repo", got "${id}". ` +
|
|
39
|
+
`The registry uses this string to look up your entry in registry.json — ` +
|
|
40
|
+
`it must match the GitHub owner/repo of the source code, not a local path.`);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
event_type: UNDERSTAND_QUICKLY_EVENT_TYPE,
|
|
44
|
+
client_payload: { id },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* `owner/repo` validation. Conservative on purpose: GitHub's actual
|
|
49
|
+
* naming rules are looser, but we want to catch local paths
|
|
50
|
+
* (`/Users/...`), bare slugs (`my-repo`), and accidental whitespace.
|
|
51
|
+
*
|
|
52
|
+
* Matches GitHub's published slug rules:
|
|
53
|
+
* owner: starts with alnum, then alnum/hyphen only, must end with
|
|
54
|
+
* alnum (no trailing hyphen — GitHub rejects this at account
|
|
55
|
+
* creation, so a `my-org-/repo` input would otherwise pass us
|
|
56
|
+
* and 422 from GitHub). No underscore, no dot. Length cap 39.
|
|
57
|
+
* repo: any of alnum/dot/hyphen/underscore. Length cap 100.
|
|
58
|
+
*/
|
|
59
|
+
export function isValidOwnerRepo(id) {
|
|
60
|
+
return /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/.test(id);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Strip a single trailing `.git` (case-insensitive) and any trailing
|
|
64
|
+
* slashes from a URL-ish string. Bounded linear: each character is
|
|
65
|
+
* visited at most twice, no backtracking.
|
|
66
|
+
*
|
|
67
|
+
* Replaces `s.replace(/\.git\/*$/i, '').replace(/\/+$/, '')` which
|
|
68
|
+
* CodeQL's polynomial-regex check (codeql/js/polynomial-redos) flags as
|
|
69
|
+
* a worst-case O(n²) on adversarial input like "////.../x".
|
|
70
|
+
*/
|
|
71
|
+
export function stripGitSuffix(input) {
|
|
72
|
+
let end = input.length;
|
|
73
|
+
// Trim trailing '/'.
|
|
74
|
+
while (end > 0 && input.charCodeAt(end - 1) === 0x2f)
|
|
75
|
+
end--;
|
|
76
|
+
// Drop one trailing '.git' (case-insensitive).
|
|
77
|
+
if (end >= 4) {
|
|
78
|
+
const tail = input.slice(end - 4, end).toLowerCase();
|
|
79
|
+
if (tail === '.git')
|
|
80
|
+
end -= 4;
|
|
81
|
+
}
|
|
82
|
+
// Trim trailing '/' that may have sat between '.git' and the rest.
|
|
83
|
+
while (end > 0 && input.charCodeAt(end - 1) === 0x2f)
|
|
84
|
+
end--;
|
|
85
|
+
return input.slice(0, end);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Parse `owner/repo` out of a git remote URL. Mirrors the heuristic in
|
|
89
|
+
* `gitnexus/src/storage/git.ts:parseRepoNameFromUrl` but keeps both
|
|
90
|
+
* halves so we can build a registry id. Returns `null` on shapes we
|
|
91
|
+
* don't recognise.
|
|
92
|
+
*
|
|
93
|
+
* Examples:
|
|
94
|
+
* git@github.com:looptech-ai/understand-quickly.git
|
|
95
|
+
* https://github.com/looptech-ai/understand-quickly
|
|
96
|
+
* ssh://git@github.com/looptech-ai/understand-quickly.git
|
|
97
|
+
*/
|
|
98
|
+
export function parseOwnerRepoFromRemote(url) {
|
|
99
|
+
if (!url)
|
|
100
|
+
return null;
|
|
101
|
+
const trimmed = url.trim();
|
|
102
|
+
if (!trimmed)
|
|
103
|
+
return null;
|
|
104
|
+
// Strip a trailing `.git` (case-insensitive) and any trailing slashes
|
|
105
|
+
// so https://h/o/r and https://h/o/r.git collapse to the same id.
|
|
106
|
+
// Bounded-linear helper avoids the polynomial-regex CodeQL alert.
|
|
107
|
+
const stripped = stripGitSuffix(trimmed);
|
|
108
|
+
// SCP-form SSH (`git@host:owner/repo`). Capture host so we can reject
|
|
109
|
+
// non-GitHub remotes — a GitLab origin like
|
|
110
|
+
// `https://gitlab.example.com/group/sub/project.git` would otherwise
|
|
111
|
+
// silently dispatch the wrong id (LOW 9).
|
|
112
|
+
const ssh = stripped.match(/^[^@]+@([^:]+):([^/]+)\/([^/]+)$/);
|
|
113
|
+
if (ssh) {
|
|
114
|
+
const host = ssh[1].toLowerCase();
|
|
115
|
+
if (host !== 'github.com' && host !== 'www.github.com')
|
|
116
|
+
return null;
|
|
117
|
+
return `${ssh[2]}/${ssh[3]}`;
|
|
118
|
+
}
|
|
119
|
+
// URL forms (https://, ssh://, git://, file://) — last two path segments.
|
|
120
|
+
const url2 = stripped.match(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/([^/]+)\/(.+)$/);
|
|
121
|
+
if (url2) {
|
|
122
|
+
// Strip optional `userinfo@` (e.g. `ssh://git@github.com/...`).
|
|
123
|
+
const authority = url2[1];
|
|
124
|
+
const atIdx = authority.lastIndexOf('@');
|
|
125
|
+
const hostAndPort = atIdx >= 0 ? authority.slice(atIdx + 1) : authority;
|
|
126
|
+
// Strip `:port` suffix if present.
|
|
127
|
+
const colonIdx = hostAndPort.indexOf(':');
|
|
128
|
+
const host = (colonIdx >= 0 ? hostAndPort.slice(0, colonIdx) : hostAndPort).toLowerCase();
|
|
129
|
+
if (host !== 'github.com' && host !== 'www.github.com')
|
|
130
|
+
return null;
|
|
131
|
+
const segments = url2[2].split('/').filter(Boolean);
|
|
132
|
+
if (segments.length >= 2) {
|
|
133
|
+
const [owner, repo] = segments.slice(-2);
|
|
134
|
+
return `${owner}/${repo}`;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=understand-quickly.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"understand-quickly.js","sourceRoot":"","sources":["../../src/integrations/understand-quickly.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAC1C,wEAAwE,CAAC;AAE3E;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,YAAY,CAAC;AAE1D,oDAAoD;AACpD,MAAM,CAAC,MAAM,4BAA4B,GAAG,0BAA0B,CAAC;AAUvE;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,EAAU;IAC/C,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,mEAAmE,EAAE,KAAK;YACxE,yEAAyE;YACzE,2EAA2E,CAC9E,CAAC;IACJ,CAAC;IACD,OAAO;QACL,UAAU,EAAE,6BAA6B;QACzC,cAAc,EAAE,EAAE,EAAE,EAAE;KACvB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAU;IACzC,OAAO,wEAAwE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC3F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,IAAI,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IACvB,qBAAqB;IACrB,OAAO,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI;QAAE,GAAG,EAAE,CAAC;IAC5D,+CAA+C;IAC/C,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QACrD,IAAI,IAAI,KAAK,MAAM;YAAE,GAAG,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,mEAAmE;IACnE,OAAO,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI;QAAE,GAAG,EAAE,CAAC;IAC5D,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAA8B;IACrE,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,sEAAsE;IACtE,kEAAkE;IAClE,kEAAkE;IAClE,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAEzC,sEAAsE;IACtE,4CAA4C;IAC5C,qEAAqE;IACrE,0CAA0C;IAC1C,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC/D,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAClC,IAAI,IAAI,KAAK,YAAY,IAAI,IAAI,KAAK,gBAAgB;YAAE,OAAO,IAAI,CAAC;QACpE,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/B,CAAC;IAED,0EAA0E;IAC1E,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAC3E,IAAI,IAAI,EAAE,CAAC;QACT,gEAAgE;QAChE,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACzC,MAAM,WAAW,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACxE,mCAAmC;QACnC,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1F,IAAI,IAAI,KAAK,YAAY,IAAI,IAAI,KAAK,gBAAgB;YAAE,OAAO,IAAI,CAAC;QACpE,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,OAAO,GAAG,KAAK,IAAI,IAAI,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test-only helpers.
|
|
3
|
+
*
|
|
4
|
+
* Symbols here are reachable from `gitnexus-shared/test-helpers` so test
|
|
5
|
+
* suites can reset shared registries or exercise internal classifiers,
|
|
6
|
+
* but they are deliberately NOT re-exported from the main `gitnexus-shared`
|
|
7
|
+
* barrel. Production consumers should never import this module — calling
|
|
8
|
+
* `__resetBreakerRegistry__()` from a tool implementation would silently
|
|
9
|
+
* nuke every circuit breaker process-wide.
|
|
10
|
+
*/
|
|
11
|
+
export { __resetBreakerRegistry__ } from './integrations/circuit-breaker.js';
|
|
12
|
+
export { classifyOutcome } from './integrations/resilient-fetch.js';
|
|
13
|
+
//# sourceMappingURL=test-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test-only helpers.
|
|
3
|
+
*
|
|
4
|
+
* Symbols here are reachable from `gitnexus-shared/test-helpers` so test
|
|
5
|
+
* suites can reset shared registries or exercise internal classifiers,
|
|
6
|
+
* but they are deliberately NOT re-exported from the main `gitnexus-shared`
|
|
7
|
+
* barrel. Production consumers should never import this module — calling
|
|
8
|
+
* `__resetBreakerRegistry__()` from a tool implementation would silently
|
|
9
|
+
* nuke every circuit breaker process-wide.
|
|
10
|
+
*/
|
|
11
|
+
export { __resetBreakerRegistry__ } from './integrations/circuit-breaker.js';
|
|
12
|
+
export { classifyOutcome } from './integrations/resilient-fetch.js';
|
|
13
|
+
//# sourceMappingURL=test-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-helpers.js","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -106,6 +106,15 @@ program
|
|
|
106
106
|
.command('augment <pattern>')
|
|
107
107
|
.description('Augment a search pattern with knowledge graph context (used by hooks)')
|
|
108
108
|
.action(createLazyAction(() => import('./augment.js'), 'augmentCommand'));
|
|
109
|
+
program
|
|
110
|
+
.command('publish [path]')
|
|
111
|
+
.description('Notify the understand-quickly registry that this repo has a fresh GitNexus index. ' +
|
|
112
|
+
'Opt-in: requires UNDERSTAND_QUICKLY_TOKEN (fine-grained PAT with ' +
|
|
113
|
+
'`Repository dispatches: write` on looptech-ai/understand-quickly). ' +
|
|
114
|
+
'No-op without the token. See https://github.com/looptech-ai/understand-quickly.')
|
|
115
|
+
.option('--id <owner/repo>', 'Override the registry id (defaults to the origin remote)')
|
|
116
|
+
.option('--skip-git', 'Treat cwd as the repo root and skip parent git-root discovery')
|
|
117
|
+
.action(createLazyAction(() => import('./publish.js'), 'publishCommand'));
|
|
109
118
|
// ─── Direct Tool Commands (no MCP overhead) ────────────────────────
|
|
110
119
|
// These invoke LocalBackend directly for use in eval, scripts, and CI.
|
|
111
120
|
program
|