@telora/daemon 0.20.28 → 0.20.53
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/build-info.json +2 -2
- package/dist/agent-state.d.ts +4 -6
- package/dist/agent-state.d.ts.map +1 -1
- package/dist/agent-state.js +4 -10
- package/dist/agent-state.js.map +1 -1
- package/dist/auto-update.d.ts +87 -0
- package/dist/auto-update.d.ts.map +1 -1
- package/dist/auto-update.js +142 -8
- package/dist/auto-update.js.map +1 -1
- package/dist/branch-status.d.ts +25 -3
- package/dist/branch-status.d.ts.map +1 -1
- package/dist/branch-status.js +130 -59
- package/dist/branch-status.js.map +1 -1
- package/dist/completion/completion-decision.d.ts +29 -3
- package/dist/completion/completion-decision.d.ts.map +1 -1
- package/dist/completion/completion-decision.js +15 -3
- package/dist/completion/completion-decision.js.map +1 -1
- package/dist/completion/event-entry-phases.d.ts +57 -0
- package/dist/completion/event-entry-phases.d.ts.map +1 -0
- package/dist/completion/event-entry-phases.js +205 -0
- package/dist/completion/event-entry-phases.js.map +1 -0
- package/dist/completion/event-phases.d.ts +14 -42
- package/dist/completion/event-phases.d.ts.map +1 -1
- package/dist/completion/event-phases.js +63 -190
- package/dist/completion/event-phases.js.map +1 -1
- package/dist/completion/event.d.ts +11 -1
- package/dist/completion/event.d.ts.map +1 -1
- package/dist/completion/event.js +5 -2
- package/dist/completion/event.js.map +1 -1
- package/dist/completion/index.d.ts +0 -3
- package/dist/completion/index.d.ts.map +1 -1
- package/dist/completion/index.js +0 -3
- package/dist/completion/index.js.map +1 -1
- package/dist/completion/review-exit-phase.d.ts +2 -1
- package/dist/completion/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit-phase.js +41 -1
- package/dist/completion/review-exit-phase.js.map +1 -1
- package/dist/completion/status-advance-phase.d.ts +2 -0
- package/dist/completion/status-advance-phase.d.ts.map +1 -1
- package/dist/completion/status-advance-phase.js +14 -28
- package/dist/completion/status-advance-phase.js.map +1 -1
- package/dist/completion/team-completion.js +1 -1
- package/dist/completion/team-completion.js.map +1 -1
- package/dist/completion/terminated-work-reconcile.d.ts.map +1 -1
- package/dist/completion/terminated-work-reconcile.js +10 -0
- package/dist/completion/terminated-work-reconcile.js.map +1 -1
- package/dist/constants.d.ts +0 -8
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +0 -8
- package/dist/constants.js.map +1 -1
- package/dist/delivery-close-actions.d.ts +39 -0
- package/dist/delivery-close-actions.d.ts.map +1 -0
- package/dist/delivery-close-actions.js +53 -0
- package/dist/delivery-close-actions.js.map +1 -0
- package/dist/delivery-lifecycle.d.ts +11 -0
- package/dist/delivery-lifecycle.d.ts.map +1 -1
- package/dist/delivery-lifecycle.js +13 -14
- package/dist/delivery-lifecycle.js.map +1 -1
- package/dist/delivery-reconciler.d.ts +18 -4
- package/dist/delivery-reconciler.d.ts.map +1 -1
- package/dist/delivery-reconciler.js +18 -4
- package/dist/delivery-reconciler.js.map +1 -1
- package/dist/directive-executor.d.ts +1 -1
- package/dist/directive-executor.js +1 -1
- package/dist/discovery-fetch.d.ts +76 -0
- package/dist/discovery-fetch.d.ts.map +1 -0
- package/dist/discovery-fetch.js +206 -0
- package/dist/discovery-fetch.js.map +1 -0
- package/dist/discovery-poll.d.ts +1 -51
- package/dist/discovery-poll.d.ts.map +1 -1
- package/dist/discovery-poll.js +10 -192
- package/dist/discovery-poll.js.map +1 -1
- package/dist/drain-teardown.d.ts +11 -6
- package/dist/drain-teardown.d.ts.map +1 -1
- package/dist/drain-teardown.js +3 -2
- package/dist/drain-teardown.js.map +1 -1
- package/dist/focus-branch-ahead.d.ts +42 -0
- package/dist/focus-branch-ahead.d.ts.map +1 -0
- package/dist/focus-branch-ahead.js +45 -0
- package/dist/focus-branch-ahead.js.map +1 -0
- package/dist/focus-engine.d.ts.map +1 -1
- package/dist/focus-engine.js +21 -43
- package/dist/focus-engine.js.map +1 -1
- package/dist/focus-executor.js +2 -2
- package/dist/focus-executor.js.map +1 -1
- package/dist/focus-lifecycle.d.ts.map +1 -1
- package/dist/focus-lifecycle.js +32 -3
- package/dist/focus-lifecycle.js.map +1 -1
- package/dist/focus-loop-registry.d.ts.map +1 -1
- package/dist/focus-loop-registry.js +27 -0
- package/dist/focus-loop-registry.js.map +1 -1
- package/dist/focus-loop.d.ts +34 -12
- package/dist/focus-loop.d.ts.map +1 -1
- package/dist/focus-loop.js +78 -15
- package/dist/focus-loop.js.map +1 -1
- package/dist/focus-merge.d.ts.map +1 -1
- package/dist/focus-merge.js +6 -1
- package/dist/focus-merge.js.map +1 -1
- package/dist/focus-phase.d.ts +18 -3
- package/dist/focus-phase.d.ts.map +1 -1
- package/dist/focus-phase.js +85 -31
- package/dist/focus-phase.js.map +1 -1
- package/dist/focus-prompt-builder.d.ts.map +1 -1
- package/dist/focus-prompt-builder.js +13 -1
- package/dist/focus-prompt-builder.js.map +1 -1
- package/dist/focus-provisioning.d.ts.map +1 -1
- package/dist/focus-provisioning.js +4 -0
- package/dist/focus-provisioning.js.map +1 -1
- package/dist/focus-stage-lifecycle/planning-release.d.ts +35 -17
- package/dist/focus-stage-lifecycle/planning-release.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/planning-release.js +53 -22
- package/dist/focus-stage-lifecycle/planning-release.js.map +1 -1
- package/dist/focus-worktree-state.d.ts +17 -6
- package/dist/focus-worktree-state.d.ts.map +1 -1
- package/dist/focus-worktree-state.js +21 -11
- package/dist/focus-worktree-state.js.map +1 -1
- package/dist/guard-engine.d.ts +1 -2
- package/dist/guard-engine.d.ts.map +1 -1
- package/dist/guard-engine.js +1 -2
- package/dist/guard-engine.js.map +1 -1
- package/dist/health-events.d.ts +10 -0
- package/dist/health-events.d.ts.map +1 -0
- package/dist/health-events.js +10 -0
- package/dist/health-events.js.map +1 -0
- package/dist/heartbeat.d.ts.map +1 -1
- package/dist/heartbeat.js +98 -21
- package/dist/heartbeat.js.map +1 -1
- package/dist/host-control/catalog.d.ts.map +1 -1
- package/dist/host-control/catalog.js +4 -1
- package/dist/host-control/catalog.js.map +1 -1
- package/dist/host-control/client.d.ts +3 -4
- package/dist/host-control/client.d.ts.map +1 -1
- package/dist/host-control/client.js +4 -5
- package/dist/host-control/client.js.map +1 -1
- package/dist/listener-auto-advance.d.ts +2 -1
- package/dist/listener-auto-advance.d.ts.map +1 -1
- package/dist/listener-auto-advance.js +36 -40
- package/dist/listener-auto-advance.js.map +1 -1
- package/dist/listener.d.ts +2 -7
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +11 -9
- package/dist/listener.js.map +1 -1
- package/dist/operator-hold.d.ts +66 -0
- package/dist/operator-hold.d.ts.map +1 -0
- package/dist/operator-hold.js +99 -0
- package/dist/operator-hold.js.map +1 -0
- package/dist/poll-trigger.d.ts +28 -0
- package/dist/poll-trigger.d.ts.map +1 -0
- package/dist/poll-trigger.js +31 -0
- package/dist/poll-trigger.js.map +1 -0
- package/dist/prompt-listing.d.ts +25 -0
- package/dist/prompt-listing.d.ts.map +1 -1
- package/dist/prompt-listing.js +67 -0
- package/dist/prompt-listing.js.map +1 -1
- package/dist/prompt-sections/execution-sections.d.ts.map +1 -1
- package/dist/prompt-sections/execution-sections.js +24 -0
- package/dist/prompt-sections/execution-sections.js.map +1 -1
- package/dist/queries/context-lock-queries.d.ts.map +1 -1
- package/dist/queries/context-lock-queries.js +4 -1
- package/dist/queries/context-lock-queries.js.map +1 -1
- package/dist/queries/deliveries.d.ts +17 -0
- package/dist/queries/deliveries.d.ts.map +1 -1
- package/dist/queries/deliveries.js.map +1 -1
- package/dist/queries/issues.d.ts +98 -0
- package/dist/queries/issues.d.ts.map +1 -1
- package/dist/queries/issues.js +135 -0
- package/dist/queries/issues.js.map +1 -1
- package/dist/queries/schemas.d.ts +3 -3
- package/dist/security-scan-engine.d.ts +9 -1
- package/dist/security-scan-engine.d.ts.map +1 -1
- package/dist/security-scan-engine.js +25 -2
- package/dist/security-scan-engine.js.map +1 -1
- package/dist/spawn/worktree-session.d.ts.map +1 -1
- package/dist/spawn/worktree-session.js +2 -0
- package/dist/spawn/worktree-session.js.map +1 -1
- package/dist/spawner/index.d.ts +7 -9
- package/dist/spawner/index.d.ts.map +1 -1
- package/dist/spawner/index.js +13 -11
- package/dist/spawner/index.js.map +1 -1
- package/dist/spawner/lifecycle.d.ts +1 -18
- package/dist/spawner/lifecycle.d.ts.map +1 -1
- package/dist/spawner/lifecycle.js +1 -54
- package/dist/spawner/lifecycle.js.map +1 -1
- package/dist/spawner/liveness.d.ts +5 -14
- package/dist/spawner/liveness.d.ts.map +1 -1
- package/dist/spawner/liveness.js +5 -67
- package/dist/spawner/liveness.js.map +1 -1
- package/dist/spawner/state.d.ts +8 -20
- package/dist/spawner/state.d.ts.map +1 -1
- package/dist/spawner/state.js +8 -34
- package/dist/spawner/state.js.map +1 -1
- package/dist/staleness-sensor.d.ts +153 -0
- package/dist/staleness-sensor.d.ts.map +1 -0
- package/dist/staleness-sensor.js +308 -0
- package/dist/staleness-sensor.js.map +1 -0
- package/dist/types/focus.d.ts +9 -0
- package/dist/types/focus.d.ts.map +1 -1
- package/dist/types/session.d.ts +0 -44
- package/dist/types/session.d.ts.map +1 -1
- package/dist/unified-shell.d.ts.map +1 -1
- package/dist/unified-shell.js +19 -0
- package/dist/unified-shell.js.map +1 -1
- package/dist/verification-engine.d.ts +6 -4
- package/dist/verification-engine.d.ts.map +1 -1
- package/dist/verification-engine.js +9 -43
- package/dist/verification-engine.js.map +1 -1
- package/dist/verification-feedback.d.ts +42 -0
- package/dist/verification-feedback.d.ts.map +1 -1
- package/dist/verification-feedback.js +93 -0
- package/dist/verification-feedback.js.map +1 -1
- package/dist/version-check.d.ts +6 -0
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +1 -1
- package/dist/version-check.js.map +1 -1
- package/package.json +2 -3
- package/dist/completion/agent-completion.d.ts +0 -48
- package/dist/completion/agent-completion.d.ts.map +0 -1
- package/dist/completion/agent-completion.js +0 -205
- package/dist/completion/agent-completion.js.map +0 -1
- package/dist/delivery-guards.d.ts +0 -41
- package/dist/delivery-guards.d.ts.map +0 -1
- package/dist/delivery-guards.js +0 -200
- package/dist/delivery-guards.js.map +0 -1
- package/dist/spawner/spawn-team.d.ts +0 -20
- package/dist/spawner/spawn-team.d.ts.map +0 -1
- package/dist/spawner/spawn-team.js +0 -19
- package/dist/spawner/spawn-team.js.map +0 -1
- package/dist/spawner/timeout.d.ts +0 -32
- package/dist/spawner/timeout.d.ts.map +0 -1
- package/dist/spawner/timeout.js +0 -126
- package/dist/spawner/timeout.js.map +0 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local model endpoint fetch primitives (the `/v1/models` floor).
|
|
3
|
+
*
|
|
4
|
+
* The shared low layer that BOTH the discovery poll (`discovery-poll.ts`,
|
|
5
|
+
* orchestration) and the host-control plane (`host-control/catalog.ts`, which
|
|
6
|
+
* degrades to this floor when a host is not controllable) sit on top of. It
|
|
7
|
+
* owns the SSRF guard (TEL-5), the response byte ceiling (TEL-6), the timeout,
|
|
8
|
+
* and the address-free error strings -- with NO dependency on either consumer,
|
|
9
|
+
* so neither `discovery-poll` nor `host-control` needs to import the other to
|
|
10
|
+
* reach the floor. `discovery-poll.ts` re-exports the public surface below for
|
|
11
|
+
* back-compat (existing importers and tests read these from `discovery-poll`).
|
|
12
|
+
*/
|
|
13
|
+
/** Time-box a single endpoint fetch so an unreachable host fails fast to error. */
|
|
14
|
+
export declare const DISCOVERY_FETCH_TIMEOUT_MS = 5000;
|
|
15
|
+
/**
|
|
16
|
+
* Hard ceiling on a discovery response body. A `/models` listing is tiny; this
|
|
17
|
+
* cap defends against a malicious endpoint streaming an unbounded body to OOM
|
|
18
|
+
* the daemon (TEL-6). Enforced both via the declared Content-Length and a
|
|
19
|
+
* streamed byte ceiling so a lying header can't slip past.
|
|
20
|
+
*/
|
|
21
|
+
export declare const MAX_DISCOVERY_BYTES = 1000000;
|
|
22
|
+
/** Generic, address-free error strings written back to the DB (TEL-5). The raw
|
|
23
|
+
* detail (URL, system error) is logged daemon-side only -- the DB-written value
|
|
24
|
+
* is readable by org members and must not leak a probe oracle. */
|
|
25
|
+
export declare const DISCOVERY_ERROR_STATUS = "Endpoint returned an error status";
|
|
26
|
+
export declare const DISCOVERY_ERROR_UNREACHABLE = "Endpoint unreachable or timed out";
|
|
27
|
+
export declare const DISCOVERY_ERROR_INVALID = "Endpoint response was not valid";
|
|
28
|
+
/** A 401/403 probe result. Distinct from the generic status message so an
|
|
29
|
+
* authenticated org admin operating their own endpoint sees the one actionable
|
|
30
|
+
* cause -- it's auth, fix the key -- instead of the address-free catch-all. Stays
|
|
31
|
+
* address-free itself (no URL/IP/system detail): the probe-oracle defense (TEL-5)
|
|
32
|
+
* is preserved; only the failure *category* is disclosed, never the endpoint. */
|
|
33
|
+
export declare const DISCOVERY_ERROR_AUTH = "Authentication failed -- check the API key";
|
|
34
|
+
/**
|
|
35
|
+
* True only for a base_url that parses as a URL whose protocol is exactly
|
|
36
|
+
* `http:` or `https:`. Rejects `file:`, `ftp:`, `gopher:`, non-URL strings, and
|
|
37
|
+
* empty -- the SSRF guard predicate (TEL-5). Pure + exported for unit testing.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isAllowedDiscoveryUrl(raw: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* A daemon-side error carrying a sanitized, address-free message for the DB.
|
|
42
|
+
* `fetchModels` throws these so the catch can write `dbMessage` verbatim while
|
|
43
|
+
* still logging the rich `message` daemon-side.
|
|
44
|
+
*/
|
|
45
|
+
export declare class DiscoveryError extends Error {
|
|
46
|
+
/** Generic string safe to write to the org-readable `error` column. */
|
|
47
|
+
readonly dbMessage: string;
|
|
48
|
+
constructor(message: string,
|
|
49
|
+
/** Generic string safe to write to the org-readable `error` column. */
|
|
50
|
+
dbMessage: string);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Extract served model id strings from an OpenAI-compatible `/models` payload
|
|
54
|
+
* (`{ object: 'list', data: [{ id }, ...] }`). Pure + defensive: tolerates a
|
|
55
|
+
* missing/!array `data`, non-object items, and non-string/blank ids, returning
|
|
56
|
+
* a de-duplicated, trimmed string[] (possibly empty). Exported for unit testing.
|
|
57
|
+
*/
|
|
58
|
+
export declare function normalizeModelList(payload: unknown): string[];
|
|
59
|
+
/**
|
|
60
|
+
* Build the request headers for a discovery fetch (D8). When a bearer key VALUE
|
|
61
|
+
* is provided (resolved server-side from the Telora-stored discovery secret), an
|
|
62
|
+
* `Authorization: Bearer <value>` header is added so an auth-protected endpoint
|
|
63
|
+
* (the common case) lists its models instead of 401-ing. A blank/absent value
|
|
64
|
+
* yields no auth header (unauthenticated probe). Pure + exported for unit
|
|
65
|
+
* testing; the value is never persisted or logged.
|
|
66
|
+
*/
|
|
67
|
+
export declare function buildDiscoveryHeaders(apiKey: string | null | undefined): Record<string, string>;
|
|
68
|
+
/**
|
|
69
|
+
* Fetch + normalize an endpoint's model list. Throws a `DiscoveryError` whose
|
|
70
|
+
* `dbMessage` is a generic, address-free string (TEL-5/TEL-6) -- the caller
|
|
71
|
+
* writes that to the DB and logs the rich `.message` daemon-side only. When a
|
|
72
|
+
* bearer key VALUE is provided (resolved from the Telora discovery secret), an
|
|
73
|
+
* Authorization bearer is sent (D8).
|
|
74
|
+
*/
|
|
75
|
+
export declare function fetchModels(baseUrl: string, apiKey?: string | null): Promise<string[]>;
|
|
76
|
+
//# sourceMappingURL=discovery-fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery-fetch.d.ts","sourceRoot":"","sources":["../src/discovery-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,mFAAmF;AACnF,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,UAAY,CAAC;AAE7C;;mEAEmE;AACnE,eAAO,MAAM,sBAAsB,sCAAsC,CAAC;AAC1E,eAAO,MAAM,2BAA2B,sCAAsC,CAAC;AAC/E,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AACzE;;;;kFAIkF;AAClF,eAAO,MAAM,oBAAoB,+CAA+C,CAAC;AAEjF;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ1D;AAED;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,KAAK;IAGrC,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,MAAM;gBAF1B,OAAO,EAAE,MAAM;IACf,uEAAuE;IAC9D,SAAS,EAAE,MAAM;CAK7B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAc7D;AA8CD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKxB;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAqD5F"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local model endpoint fetch primitives (the `/v1/models` floor).
|
|
3
|
+
*
|
|
4
|
+
* The shared low layer that BOTH the discovery poll (`discovery-poll.ts`,
|
|
5
|
+
* orchestration) and the host-control plane (`host-control/catalog.ts`, which
|
|
6
|
+
* degrades to this floor when a host is not controllable) sit on top of. It
|
|
7
|
+
* owns the SSRF guard (TEL-5), the response byte ceiling (TEL-6), the timeout,
|
|
8
|
+
* and the address-free error strings -- with NO dependency on either consumer,
|
|
9
|
+
* so neither `discovery-poll` nor `host-control` needs to import the other to
|
|
10
|
+
* reach the floor. `discovery-poll.ts` re-exports the public surface below for
|
|
11
|
+
* back-compat (existing importers and tests read these from `discovery-poll`).
|
|
12
|
+
*/
|
|
13
|
+
import { createLogger } from './log.js';
|
|
14
|
+
const log = createLogger({ module: 'discovery' });
|
|
15
|
+
/** Time-box a single endpoint fetch so an unreachable host fails fast to error. */
|
|
16
|
+
export const DISCOVERY_FETCH_TIMEOUT_MS = 5000;
|
|
17
|
+
/**
|
|
18
|
+
* Hard ceiling on a discovery response body. A `/models` listing is tiny; this
|
|
19
|
+
* cap defends against a malicious endpoint streaming an unbounded body to OOM
|
|
20
|
+
* the daemon (TEL-6). Enforced both via the declared Content-Length and a
|
|
21
|
+
* streamed byte ceiling so a lying header can't slip past.
|
|
22
|
+
*/
|
|
23
|
+
export const MAX_DISCOVERY_BYTES = 1_000_000;
|
|
24
|
+
/** Generic, address-free error strings written back to the DB (TEL-5). The raw
|
|
25
|
+
* detail (URL, system error) is logged daemon-side only -- the DB-written value
|
|
26
|
+
* is readable by org members and must not leak a probe oracle. */
|
|
27
|
+
export const DISCOVERY_ERROR_STATUS = 'Endpoint returned an error status';
|
|
28
|
+
export const DISCOVERY_ERROR_UNREACHABLE = 'Endpoint unreachable or timed out';
|
|
29
|
+
export const DISCOVERY_ERROR_INVALID = 'Endpoint response was not valid';
|
|
30
|
+
/** A 401/403 probe result. Distinct from the generic status message so an
|
|
31
|
+
* authenticated org admin operating their own endpoint sees the one actionable
|
|
32
|
+
* cause -- it's auth, fix the key -- instead of the address-free catch-all. Stays
|
|
33
|
+
* address-free itself (no URL/IP/system detail): the probe-oracle defense (TEL-5)
|
|
34
|
+
* is preserved; only the failure *category* is disclosed, never the endpoint. */
|
|
35
|
+
export const DISCOVERY_ERROR_AUTH = 'Authentication failed -- check the API key';
|
|
36
|
+
/**
|
|
37
|
+
* True only for a base_url that parses as a URL whose protocol is exactly
|
|
38
|
+
* `http:` or `https:`. Rejects `file:`, `ftp:`, `gopher:`, non-URL strings, and
|
|
39
|
+
* empty -- the SSRF guard predicate (TEL-5). Pure + exported for unit testing.
|
|
40
|
+
*/
|
|
41
|
+
export function isAllowedDiscoveryUrl(raw) {
|
|
42
|
+
let parsed;
|
|
43
|
+
try {
|
|
44
|
+
parsed = new URL(raw);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A daemon-side error carrying a sanitized, address-free message for the DB.
|
|
53
|
+
* `fetchModels` throws these so the catch can write `dbMessage` verbatim while
|
|
54
|
+
* still logging the rich `message` daemon-side.
|
|
55
|
+
*/
|
|
56
|
+
export class DiscoveryError extends Error {
|
|
57
|
+
dbMessage;
|
|
58
|
+
constructor(message,
|
|
59
|
+
/** Generic string safe to write to the org-readable `error` column. */
|
|
60
|
+
dbMessage) {
|
|
61
|
+
super(message);
|
|
62
|
+
this.dbMessage = dbMessage;
|
|
63
|
+
this.name = 'DiscoveryError';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Extract served model id strings from an OpenAI-compatible `/models` payload
|
|
68
|
+
* (`{ object: 'list', data: [{ id }, ...] }`). Pure + defensive: tolerates a
|
|
69
|
+
* missing/!array `data`, non-object items, and non-string/blank ids, returning
|
|
70
|
+
* a de-duplicated, trimmed string[] (possibly empty). Exported for unit testing.
|
|
71
|
+
*/
|
|
72
|
+
export function normalizeModelList(payload) {
|
|
73
|
+
if (!payload || typeof payload !== 'object')
|
|
74
|
+
return [];
|
|
75
|
+
const data = payload.data;
|
|
76
|
+
if (!Array.isArray(data))
|
|
77
|
+
return [];
|
|
78
|
+
const seen = new Set();
|
|
79
|
+
for (const item of data) {
|
|
80
|
+
if (!item || typeof item !== 'object')
|
|
81
|
+
continue;
|
|
82
|
+
const id = item.id;
|
|
83
|
+
if (typeof id === 'string') {
|
|
84
|
+
const trimmed = id.trim();
|
|
85
|
+
if (trimmed)
|
|
86
|
+
seen.add(trimmed);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return [...seen];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Read a fetch response body with a hard byte ceiling, aborting once the cap is
|
|
93
|
+
* exceeded so a lying Content-Length (or a chunked body with none) can't OOM the
|
|
94
|
+
* daemon (TEL-6). Returns the decoded text. Throws past the ceiling.
|
|
95
|
+
*/
|
|
96
|
+
async function readBodyCapped(res) {
|
|
97
|
+
const body = res.body;
|
|
98
|
+
if (!body) {
|
|
99
|
+
// No stream available (shouldn't happen on Node fetch) -- fall back but still
|
|
100
|
+
// cap by post-read byteLength.
|
|
101
|
+
const buf = await res.arrayBuffer();
|
|
102
|
+
if (buf.byteLength > MAX_DISCOVERY_BYTES) {
|
|
103
|
+
throw new DiscoveryError('Response body exceeded byte ceiling', DISCOVERY_ERROR_INVALID);
|
|
104
|
+
}
|
|
105
|
+
return new TextDecoder().decode(buf);
|
|
106
|
+
}
|
|
107
|
+
const reader = body.getReader();
|
|
108
|
+
const chunks = [];
|
|
109
|
+
let total = 0;
|
|
110
|
+
try {
|
|
111
|
+
for (;;) {
|
|
112
|
+
const { done, value } = await reader.read();
|
|
113
|
+
if (done)
|
|
114
|
+
break;
|
|
115
|
+
if (value) {
|
|
116
|
+
total += value.byteLength;
|
|
117
|
+
if (total > MAX_DISCOVERY_BYTES) {
|
|
118
|
+
throw new DiscoveryError('Response body exceeded byte ceiling', DISCOVERY_ERROR_INVALID);
|
|
119
|
+
}
|
|
120
|
+
chunks.push(value);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
// Stop the underlying transfer; ignore cancel errors.
|
|
126
|
+
await reader.cancel().catch((err) => { log.debug('SSE reader cancel failed', { error: String(err?.message ?? err) }); });
|
|
127
|
+
}
|
|
128
|
+
const merged = new Uint8Array(total);
|
|
129
|
+
let offset = 0;
|
|
130
|
+
for (const c of chunks) {
|
|
131
|
+
merged.set(c, offset);
|
|
132
|
+
offset += c.byteLength;
|
|
133
|
+
}
|
|
134
|
+
return new TextDecoder().decode(merged);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Build the request headers for a discovery fetch (D8). When a bearer key VALUE
|
|
138
|
+
* is provided (resolved server-side from the Telora-stored discovery secret), an
|
|
139
|
+
* `Authorization: Bearer <value>` header is added so an auth-protected endpoint
|
|
140
|
+
* (the common case) lists its models instead of 401-ing. A blank/absent value
|
|
141
|
+
* yields no auth header (unauthenticated probe). Pure + exported for unit
|
|
142
|
+
* testing; the value is never persisted or logged.
|
|
143
|
+
*/
|
|
144
|
+
export function buildDiscoveryHeaders(apiKey) {
|
|
145
|
+
const headers = { accept: 'application/json' };
|
|
146
|
+
const value = typeof apiKey === 'string' ? apiKey.trim() : '';
|
|
147
|
+
if (value)
|
|
148
|
+
headers.Authorization = `Bearer ${value}`;
|
|
149
|
+
return headers;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Fetch + normalize an endpoint's model list. Throws a `DiscoveryError` whose
|
|
153
|
+
* `dbMessage` is a generic, address-free string (TEL-5/TEL-6) -- the caller
|
|
154
|
+
* writes that to the DB and logs the rich `.message` daemon-side only. When a
|
|
155
|
+
* bearer key VALUE is provided (resolved from the Telora discovery secret), an
|
|
156
|
+
* Authorization bearer is sent (D8).
|
|
157
|
+
*/
|
|
158
|
+
export async function fetchModels(baseUrl, apiKey) {
|
|
159
|
+
// TEL-5: validate the operator-supplied base_url before any network call --
|
|
160
|
+
// reject anything that isn't a parseable http/https URL (no file:, ftp:, etc).
|
|
161
|
+
if (!isAllowedDiscoveryUrl(baseUrl)) {
|
|
162
|
+
throw new DiscoveryError(`Rejected non-http(s) discovery URL: ${baseUrl}`, DISCOVERY_ERROR_INVALID);
|
|
163
|
+
}
|
|
164
|
+
// base_url already includes /v1 (e.g. http://host:8888/v1); append /models.
|
|
165
|
+
const url = `${baseUrl.replace(/\/+$/, '')}/models`;
|
|
166
|
+
const controller = new AbortController();
|
|
167
|
+
const timer = setTimeout(() => controller.abort(), DISCOVERY_FETCH_TIMEOUT_MS);
|
|
168
|
+
try {
|
|
169
|
+
const res = await fetch(url, { signal: controller.signal, headers: buildDiscoveryHeaders(apiKey) });
|
|
170
|
+
if (!res.ok) {
|
|
171
|
+
// 401/403: disclose the failure CATEGORY (auth) so an authenticated operator
|
|
172
|
+
// can act, while keeping the message address-free (TEL-5). All other non-2xx
|
|
173
|
+
// codes stay the generic catch-all so we never leak which path/host exists.
|
|
174
|
+
const dbMessage = res.status === 401 || res.status === 403 ? DISCOVERY_ERROR_AUTH : DISCOVERY_ERROR_STATUS;
|
|
175
|
+
throw new DiscoveryError(`Endpoint returned HTTP ${res.status}`, dbMessage);
|
|
176
|
+
}
|
|
177
|
+
// TEL-6: reject a too-large declared body up front...
|
|
178
|
+
const declared = Number(res.headers.get('content-length'));
|
|
179
|
+
if (Number.isFinite(declared) && declared > MAX_DISCOVERY_BYTES) {
|
|
180
|
+
throw new DiscoveryError(`Declared Content-Length ${declared} exceeds cap`, DISCOVERY_ERROR_INVALID);
|
|
181
|
+
}
|
|
182
|
+
// ...and defensively cap the actual stream in case the header lied / is absent.
|
|
183
|
+
const text = await readBodyCapped(res);
|
|
184
|
+
let json;
|
|
185
|
+
try {
|
|
186
|
+
json = JSON.parse(text);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
throw new DiscoveryError('Response body was not valid JSON', DISCOVERY_ERROR_INVALID);
|
|
190
|
+
}
|
|
191
|
+
return normalizeModelList(json);
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
if (err instanceof DiscoveryError)
|
|
195
|
+
throw err;
|
|
196
|
+
if (err.name === 'AbortError') {
|
|
197
|
+
throw new DiscoveryError(`Timed out after ${DISCOVERY_FETCH_TIMEOUT_MS}ms reaching ${url}`, DISCOVERY_ERROR_UNREACHABLE);
|
|
198
|
+
}
|
|
199
|
+
// Network-level failure (DNS, connection refused, TLS): generic to the DB.
|
|
200
|
+
throw new DiscoveryError(err.message || `Failed to reach ${url}`, DISCOVERY_ERROR_UNREACHABLE);
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
clearTimeout(timer);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=discovery-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery-fetch.js","sourceRoot":"","sources":["../src/discovery-fetch.ts"],"names":[],"mappings":"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"}
|
package/dist/discovery-poll.d.ts
CHANGED
|
@@ -10,59 +10,9 @@
|
|
|
10
10
|
* Runs once per focus-poll cycle, org-scoped, best-effort: a failure here must
|
|
11
11
|
* never disturb the spawn/merge poll work it rides alongside.
|
|
12
12
|
*/
|
|
13
|
-
|
|
14
|
-
export declare const DISCOVERY_FETCH_TIMEOUT_MS = 5000;
|
|
15
|
-
/**
|
|
16
|
-
* Hard ceiling on a discovery response body. A `/models` listing is tiny; this
|
|
17
|
-
* cap defends against a malicious endpoint streaming an unbounded body to OOM
|
|
18
|
-
* the daemon (TEL-6). Enforced both via the declared Content-Length and a
|
|
19
|
-
* streamed byte ceiling so a lying header can't slip past.
|
|
20
|
-
*/
|
|
21
|
-
export declare const MAX_DISCOVERY_BYTES = 1000000;
|
|
13
|
+
export { isAllowedDiscoveryUrl, normalizeModelList, buildDiscoveryHeaders, fetchModels, DISCOVERY_FETCH_TIMEOUT_MS, MAX_DISCOVERY_BYTES, DISCOVERY_ERROR_STATUS, DISCOVERY_ERROR_UNREACHABLE, DISCOVERY_ERROR_INVALID, DISCOVERY_ERROR_AUTH, } from './discovery-fetch.js';
|
|
22
14
|
/** Number of claimed discovery requests fetched concurrently per cycle (TEL-7). */
|
|
23
15
|
export declare const DISCOVERY_FETCH_CONCURRENCY = 4;
|
|
24
|
-
/** Generic, address-free error strings written back to the DB (TEL-5). The raw
|
|
25
|
-
* detail (URL, system error) is logged daemon-side only -- the DB-written value
|
|
26
|
-
* is readable by org members and must not leak a probe oracle. */
|
|
27
|
-
export declare const DISCOVERY_ERROR_STATUS = "Endpoint returned an error status";
|
|
28
|
-
export declare const DISCOVERY_ERROR_UNREACHABLE = "Endpoint unreachable or timed out";
|
|
29
|
-
export declare const DISCOVERY_ERROR_INVALID = "Endpoint response was not valid";
|
|
30
|
-
/** A 401/403 probe result. Distinct from the generic status message so an
|
|
31
|
-
* authenticated org admin operating their own endpoint sees the one actionable
|
|
32
|
-
* cause -- it's auth, fix the key -- instead of the address-free catch-all. Stays
|
|
33
|
-
* address-free itself (no URL/IP/system detail): the probe-oracle defense (TEL-5)
|
|
34
|
-
* is preserved; only the failure *category* is disclosed, never the endpoint. */
|
|
35
|
-
export declare const DISCOVERY_ERROR_AUTH = "Authentication failed -- check the API key";
|
|
36
|
-
/**
|
|
37
|
-
* True only for a base_url that parses as a URL whose protocol is exactly
|
|
38
|
-
* `http:` or `https:`. Rejects `file:`, `ftp:`, `gopher:`, non-URL strings, and
|
|
39
|
-
* empty -- the SSRF guard predicate (TEL-5). Pure + exported for unit testing.
|
|
40
|
-
*/
|
|
41
|
-
export declare function isAllowedDiscoveryUrl(raw: string): boolean;
|
|
42
|
-
/**
|
|
43
|
-
* Extract served model id strings from an OpenAI-compatible `/models` payload
|
|
44
|
-
* (`{ object: 'list', data: [{ id }, ...] }`). Pure + defensive: tolerates a
|
|
45
|
-
* missing/!array `data`, non-object items, and non-string/blank ids, returning
|
|
46
|
-
* a de-duplicated, trimmed string[] (possibly empty). Exported for unit testing.
|
|
47
|
-
*/
|
|
48
|
-
export declare function normalizeModelList(payload: unknown): string[];
|
|
49
|
-
/**
|
|
50
|
-
* Build the request headers for a discovery fetch (D8). When a bearer key VALUE
|
|
51
|
-
* is provided (resolved server-side from the Telora-stored discovery secret), an
|
|
52
|
-
* `Authorization: Bearer <value>` header is added so an auth-protected endpoint
|
|
53
|
-
* (the common case) lists its models instead of 401-ing. A blank/absent value
|
|
54
|
-
* yields no auth header (unauthenticated probe). Pure + exported for unit
|
|
55
|
-
* testing; the value is never persisted or logged.
|
|
56
|
-
*/
|
|
57
|
-
export declare function buildDiscoveryHeaders(apiKey: string | null | undefined): Record<string, string>;
|
|
58
|
-
/**
|
|
59
|
-
* Fetch + normalize an endpoint's model list. Throws a `DiscoveryError` whose
|
|
60
|
-
* `dbMessage` is a generic, address-free string (TEL-5/TEL-6) -- the caller
|
|
61
|
-
* writes that to the DB and logs the rich `.message` daemon-side only. When a
|
|
62
|
-
* bearer key VALUE is provided (resolved from the Telora discovery secret), an
|
|
63
|
-
* Authorization bearer is sent (D8).
|
|
64
|
-
*/
|
|
65
|
-
export declare function fetchModels(baseUrl: string, apiKey?: string | null): Promise<string[]>;
|
|
66
16
|
/**
|
|
67
17
|
* Claim and resolve all of the org's pending discovery requests. Best-effort:
|
|
68
18
|
* a claim failure logs and returns; per-request fetches run with bounded
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery-poll.d.ts","sourceRoot":"","sources":["../src/discovery-poll.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"discovery-poll.d.ts","sourceRoot":"","sources":["../src/discovery-poll.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAiBH,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,WAAW,EACX,0BAA0B,EAC1B,mBAAmB,EACnB,sBAAsB,EACtB,2BAA2B,EAC3B,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAI9B,mFAAmF;AACnF,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAwD7C;;;;;GAKG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAoB5D"}
|
package/dist/discovery-poll.js
CHANGED
|
@@ -11,202 +11,20 @@
|
|
|
11
11
|
* never disturb the spawn/merge poll work it rides alongside.
|
|
12
12
|
*/
|
|
13
13
|
import { getPendingDiscoveryRequests, resolveDiscoveryRequest, pruneDiscoveryRequests, } from './queries/local-models.js';
|
|
14
|
-
|
|
14
|
+
// Import readLoadableCatalog from its defining module (host-control/catalog.ts),
|
|
15
|
+
// NOT the host-control/index.js barrel: the barrel re-exports members that
|
|
16
|
+
// import back from this file, so routing through it would close a static cycle.
|
|
17
|
+
import { readLoadableCatalog } from './host-control/catalog.js';
|
|
15
18
|
import { createLogger } from './log.js';
|
|
19
|
+
// The `/v1/models` fetch primitives live in discovery-fetch.ts (a leaf module
|
|
20
|
+
// shared with host-control's catalog floor). Import what this orchestration
|
|
21
|
+
// layer uses directly, and re-export the full public surface for back-compat:
|
|
22
|
+
// existing importers and tests read these symbols from './discovery-poll.js'.
|
|
23
|
+
import { DiscoveryError, DISCOVERY_ERROR_UNREACHABLE } from './discovery-fetch.js';
|
|
24
|
+
export { isAllowedDiscoveryUrl, normalizeModelList, buildDiscoveryHeaders, fetchModels, DISCOVERY_FETCH_TIMEOUT_MS, MAX_DISCOVERY_BYTES, DISCOVERY_ERROR_STATUS, DISCOVERY_ERROR_UNREACHABLE, DISCOVERY_ERROR_INVALID, DISCOVERY_ERROR_AUTH, } from './discovery-fetch.js';
|
|
16
25
|
const log = createLogger({ module: 'discovery' });
|
|
17
|
-
/** Time-box a single endpoint fetch so an unreachable host fails fast to error. */
|
|
18
|
-
export const DISCOVERY_FETCH_TIMEOUT_MS = 5000;
|
|
19
|
-
/**
|
|
20
|
-
* Hard ceiling on a discovery response body. A `/models` listing is tiny; this
|
|
21
|
-
* cap defends against a malicious endpoint streaming an unbounded body to OOM
|
|
22
|
-
* the daemon (TEL-6). Enforced both via the declared Content-Length and a
|
|
23
|
-
* streamed byte ceiling so a lying header can't slip past.
|
|
24
|
-
*/
|
|
25
|
-
export const MAX_DISCOVERY_BYTES = 1_000_000;
|
|
26
26
|
/** Number of claimed discovery requests fetched concurrently per cycle (TEL-7). */
|
|
27
27
|
export const DISCOVERY_FETCH_CONCURRENCY = 4;
|
|
28
|
-
/** Generic, address-free error strings written back to the DB (TEL-5). The raw
|
|
29
|
-
* detail (URL, system error) is logged daemon-side only -- the DB-written value
|
|
30
|
-
* is readable by org members and must not leak a probe oracle. */
|
|
31
|
-
export const DISCOVERY_ERROR_STATUS = 'Endpoint returned an error status';
|
|
32
|
-
export const DISCOVERY_ERROR_UNREACHABLE = 'Endpoint unreachable or timed out';
|
|
33
|
-
export const DISCOVERY_ERROR_INVALID = 'Endpoint response was not valid';
|
|
34
|
-
/** A 401/403 probe result. Distinct from the generic status message so an
|
|
35
|
-
* authenticated org admin operating their own endpoint sees the one actionable
|
|
36
|
-
* cause -- it's auth, fix the key -- instead of the address-free catch-all. Stays
|
|
37
|
-
* address-free itself (no URL/IP/system detail): the probe-oracle defense (TEL-5)
|
|
38
|
-
* is preserved; only the failure *category* is disclosed, never the endpoint. */
|
|
39
|
-
export const DISCOVERY_ERROR_AUTH = 'Authentication failed -- check the API key';
|
|
40
|
-
/**
|
|
41
|
-
* True only for a base_url that parses as a URL whose protocol is exactly
|
|
42
|
-
* `http:` or `https:`. Rejects `file:`, `ftp:`, `gopher:`, non-URL strings, and
|
|
43
|
-
* empty -- the SSRF guard predicate (TEL-5). Pure + exported for unit testing.
|
|
44
|
-
*/
|
|
45
|
-
export function isAllowedDiscoveryUrl(raw) {
|
|
46
|
-
let parsed;
|
|
47
|
-
try {
|
|
48
|
-
parsed = new URL(raw);
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* A daemon-side error carrying a sanitized, address-free message for the DB.
|
|
57
|
-
* `fetchModels` throws these so the catch can write `dbMessage` verbatim while
|
|
58
|
-
* still logging the rich `message` daemon-side.
|
|
59
|
-
*/
|
|
60
|
-
class DiscoveryError extends Error {
|
|
61
|
-
dbMessage;
|
|
62
|
-
constructor(message,
|
|
63
|
-
/** Generic string safe to write to the org-readable `error` column. */
|
|
64
|
-
dbMessage) {
|
|
65
|
-
super(message);
|
|
66
|
-
this.dbMessage = dbMessage;
|
|
67
|
-
this.name = 'DiscoveryError';
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Extract served model id strings from an OpenAI-compatible `/models` payload
|
|
72
|
-
* (`{ object: 'list', data: [{ id }, ...] }`). Pure + defensive: tolerates a
|
|
73
|
-
* missing/!array `data`, non-object items, and non-string/blank ids, returning
|
|
74
|
-
* a de-duplicated, trimmed string[] (possibly empty). Exported for unit testing.
|
|
75
|
-
*/
|
|
76
|
-
export function normalizeModelList(payload) {
|
|
77
|
-
if (!payload || typeof payload !== 'object')
|
|
78
|
-
return [];
|
|
79
|
-
const data = payload.data;
|
|
80
|
-
if (!Array.isArray(data))
|
|
81
|
-
return [];
|
|
82
|
-
const seen = new Set();
|
|
83
|
-
for (const item of data) {
|
|
84
|
-
if (!item || typeof item !== 'object')
|
|
85
|
-
continue;
|
|
86
|
-
const id = item.id;
|
|
87
|
-
if (typeof id === 'string') {
|
|
88
|
-
const trimmed = id.trim();
|
|
89
|
-
if (trimmed)
|
|
90
|
-
seen.add(trimmed);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return [...seen];
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Read a fetch response body with a hard byte ceiling, aborting once the cap is
|
|
97
|
-
* exceeded so a lying Content-Length (or a chunked body with none) can't OOM the
|
|
98
|
-
* daemon (TEL-6). Returns the decoded text. Throws past the ceiling.
|
|
99
|
-
*/
|
|
100
|
-
async function readBodyCapped(res) {
|
|
101
|
-
const body = res.body;
|
|
102
|
-
if (!body) {
|
|
103
|
-
// No stream available (shouldn't happen on Node fetch) -- fall back but still
|
|
104
|
-
// cap by post-read byteLength.
|
|
105
|
-
const buf = await res.arrayBuffer();
|
|
106
|
-
if (buf.byteLength > MAX_DISCOVERY_BYTES) {
|
|
107
|
-
throw new DiscoveryError('Response body exceeded byte ceiling', DISCOVERY_ERROR_INVALID);
|
|
108
|
-
}
|
|
109
|
-
return new TextDecoder().decode(buf);
|
|
110
|
-
}
|
|
111
|
-
const reader = body.getReader();
|
|
112
|
-
const chunks = [];
|
|
113
|
-
let total = 0;
|
|
114
|
-
try {
|
|
115
|
-
for (;;) {
|
|
116
|
-
const { done, value } = await reader.read();
|
|
117
|
-
if (done)
|
|
118
|
-
break;
|
|
119
|
-
if (value) {
|
|
120
|
-
total += value.byteLength;
|
|
121
|
-
if (total > MAX_DISCOVERY_BYTES) {
|
|
122
|
-
throw new DiscoveryError('Response body exceeded byte ceiling', DISCOVERY_ERROR_INVALID);
|
|
123
|
-
}
|
|
124
|
-
chunks.push(value);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
finally {
|
|
129
|
-
// Stop the underlying transfer; ignore cancel errors.
|
|
130
|
-
await reader.cancel().catch((err) => { log.debug('SSE reader cancel failed', { error: String(err?.message ?? err) }); });
|
|
131
|
-
}
|
|
132
|
-
const merged = new Uint8Array(total);
|
|
133
|
-
let offset = 0;
|
|
134
|
-
for (const c of chunks) {
|
|
135
|
-
merged.set(c, offset);
|
|
136
|
-
offset += c.byteLength;
|
|
137
|
-
}
|
|
138
|
-
return new TextDecoder().decode(merged);
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Build the request headers for a discovery fetch (D8). When a bearer key VALUE
|
|
142
|
-
* is provided (resolved server-side from the Telora-stored discovery secret), an
|
|
143
|
-
* `Authorization: Bearer <value>` header is added so an auth-protected endpoint
|
|
144
|
-
* (the common case) lists its models instead of 401-ing. A blank/absent value
|
|
145
|
-
* yields no auth header (unauthenticated probe). Pure + exported for unit
|
|
146
|
-
* testing; the value is never persisted or logged.
|
|
147
|
-
*/
|
|
148
|
-
export function buildDiscoveryHeaders(apiKey) {
|
|
149
|
-
const headers = { accept: 'application/json' };
|
|
150
|
-
const value = typeof apiKey === 'string' ? apiKey.trim() : '';
|
|
151
|
-
if (value)
|
|
152
|
-
headers.Authorization = `Bearer ${value}`;
|
|
153
|
-
return headers;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Fetch + normalize an endpoint's model list. Throws a `DiscoveryError` whose
|
|
157
|
-
* `dbMessage` is a generic, address-free string (TEL-5/TEL-6) -- the caller
|
|
158
|
-
* writes that to the DB and logs the rich `.message` daemon-side only. When a
|
|
159
|
-
* bearer key VALUE is provided (resolved from the Telora discovery secret), an
|
|
160
|
-
* Authorization bearer is sent (D8).
|
|
161
|
-
*/
|
|
162
|
-
export async function fetchModels(baseUrl, apiKey) {
|
|
163
|
-
// TEL-5: validate the operator-supplied base_url before any network call --
|
|
164
|
-
// reject anything that isn't a parseable http/https URL (no file:, ftp:, etc).
|
|
165
|
-
if (!isAllowedDiscoveryUrl(baseUrl)) {
|
|
166
|
-
throw new DiscoveryError(`Rejected non-http(s) discovery URL: ${baseUrl}`, DISCOVERY_ERROR_INVALID);
|
|
167
|
-
}
|
|
168
|
-
// base_url already includes /v1 (e.g. http://host:8888/v1); append /models.
|
|
169
|
-
const url = `${baseUrl.replace(/\/+$/, '')}/models`;
|
|
170
|
-
const controller = new AbortController();
|
|
171
|
-
const timer = setTimeout(() => controller.abort(), DISCOVERY_FETCH_TIMEOUT_MS);
|
|
172
|
-
try {
|
|
173
|
-
const res = await fetch(url, { signal: controller.signal, headers: buildDiscoveryHeaders(apiKey) });
|
|
174
|
-
if (!res.ok) {
|
|
175
|
-
// 401/403: disclose the failure CATEGORY (auth) so an authenticated operator
|
|
176
|
-
// can act, while keeping the message address-free (TEL-5). All other non-2xx
|
|
177
|
-
// codes stay the generic catch-all so we never leak which path/host exists.
|
|
178
|
-
const dbMessage = res.status === 401 || res.status === 403 ? DISCOVERY_ERROR_AUTH : DISCOVERY_ERROR_STATUS;
|
|
179
|
-
throw new DiscoveryError(`Endpoint returned HTTP ${res.status}`, dbMessage);
|
|
180
|
-
}
|
|
181
|
-
// TEL-6: reject a too-large declared body up front...
|
|
182
|
-
const declared = Number(res.headers.get('content-length'));
|
|
183
|
-
if (Number.isFinite(declared) && declared > MAX_DISCOVERY_BYTES) {
|
|
184
|
-
throw new DiscoveryError(`Declared Content-Length ${declared} exceeds cap`, DISCOVERY_ERROR_INVALID);
|
|
185
|
-
}
|
|
186
|
-
// ...and defensively cap the actual stream in case the header lied / is absent.
|
|
187
|
-
const text = await readBodyCapped(res);
|
|
188
|
-
let json;
|
|
189
|
-
try {
|
|
190
|
-
json = JSON.parse(text);
|
|
191
|
-
}
|
|
192
|
-
catch {
|
|
193
|
-
throw new DiscoveryError('Response body was not valid JSON', DISCOVERY_ERROR_INVALID);
|
|
194
|
-
}
|
|
195
|
-
return normalizeModelList(json);
|
|
196
|
-
}
|
|
197
|
-
catch (err) {
|
|
198
|
-
if (err instanceof DiscoveryError)
|
|
199
|
-
throw err;
|
|
200
|
-
if (err.name === 'AbortError') {
|
|
201
|
-
throw new DiscoveryError(`Timed out after ${DISCOVERY_FETCH_TIMEOUT_MS}ms reaching ${url}`, DISCOVERY_ERROR_UNREACHABLE);
|
|
202
|
-
}
|
|
203
|
-
// Network-level failure (DNS, connection refused, TLS): generic to the DB.
|
|
204
|
-
throw new DiscoveryError(err.message || `Failed to reach ${url}`, DISCOVERY_ERROR_UNREACHABLE);
|
|
205
|
-
}
|
|
206
|
-
finally {
|
|
207
|
-
clearTimeout(timer);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
28
|
/**
|
|
211
29
|
* Process one claimed discovery request: fetch + normalize, then write back its
|
|
212
30
|
* own loaded/error result. Never throws -- one request's failure must not abort
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery-poll.js","sourceRoot":"","sources":["../src/discovery-poll.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AACnC,
|
|
1
|
+
{"version":3,"file":"discovery-poll.js","sourceRoot":"","sources":["../src/discovery-poll.ts"],"names":[],"mappings":"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"}
|
package/dist/drain-teardown.d.ts
CHANGED
|
@@ -33,13 +33,18 @@ export interface DrainToIdleOptions {
|
|
|
33
33
|
/** Poll cadence for the idle gate (ms). Default 1000. */
|
|
34
34
|
pollIntervalMs?: number;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
36
|
+
* REQUIRED deadline for the drain wait -- an unbounded wait must be chosen
|
|
37
|
+
* explicitly, never defaulted into. Either a positive safety cap in ms (when
|
|
38
|
+
* the fleet has not idled within it the teardown is FORCED with a loud
|
|
39
|
+
* warning -- an operator-set bound that opts into a forced stop after
|
|
40
|
+
* waiting), or the literal `'unbounded'` to wait indefinitely for the fleet
|
|
41
|
+
* to finish work naturally. `'unbounded'` is legitimate ONLY where a separate
|
|
42
|
+
* escalation can still force teardown (the stop path upgrades a second signal
|
|
43
|
+
* to an immediate `shutdown()`); the daemon runs this in the background so an
|
|
44
|
+
* indefinite wait never blocks the CLI. Making this non-optional keeps an
|
|
45
|
+
* accidental silent-unbounded wait from being expressible.
|
|
41
46
|
*/
|
|
42
|
-
maxWaitMs
|
|
47
|
+
maxWaitMs: number | 'unbounded';
|
|
43
48
|
/** Logical clock (ms), injectable for tests. Default Date.now. */
|
|
44
49
|
now?: () => number;
|
|
45
50
|
/** Logger seam. Defaults to console.log. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drain-teardown.d.ts","sourceRoot":"","sources":["../src/drain-teardown.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,kEAAkE;AAClE,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,eAAe,EAAE,MAAM,OAAO,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,kFAAkF;IAClF,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,0CAA0C;IAC1C,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,yDAAyD;IACzD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB
|
|
1
|
+
{"version":3,"file":"drain-teardown.d.ts","sourceRoot":"","sources":["../src/drain-teardown.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,kEAAkE;AAClE,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,eAAe,EAAE,MAAM,OAAO,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,kFAAkF;IAClF,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,0CAA0C;IAC1C,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,yDAAyD;IACzD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;;OAWG;IACH,SAAS,EAAE,MAAM,GAAG,WAAW,CAAC;IAChC,kEAAkE;IAClE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,4CAA4C;IAC5C,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,gEAAgE;AAChE,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,aAAa,EAAE,OAAO,CAAC;IACvB,qDAAqD;IACrD,SAAS,EAAE,OAAO,CAAC;IACnB,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAyCjG"}
|