@telora/daemon 0.18.61 → 0.18.67

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.
Files changed (52) hide show
  1. package/build-info.json +2 -2
  2. package/dist/config.d.ts +16 -3
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +69 -13
  5. package/dist/config.js.map +1 -1
  6. package/dist/discovery-poll.d.ts.map +1 -1
  7. package/dist/discovery-poll.js +14 -2
  8. package/dist/discovery-poll.js.map +1 -1
  9. package/dist/focus-executor.d.ts +26 -3
  10. package/dist/focus-executor.d.ts.map +1 -1
  11. package/dist/focus-executor.js +79 -0
  12. package/dist/focus-executor.js.map +1 -1
  13. package/dist/git-fetch.d.ts +123 -0
  14. package/dist/git-fetch.d.ts.map +1 -0
  15. package/dist/git-fetch.js +164 -0
  16. package/dist/git-fetch.js.map +1 -0
  17. package/dist/git-state-detector.d.ts.map +1 -1
  18. package/dist/git-state-detector.js +63 -26
  19. package/dist/git-state-detector.js.map +1 -1
  20. package/dist/host-control/capability.d.ts +31 -0
  21. package/dist/host-control/capability.d.ts.map +1 -0
  22. package/dist/host-control/capability.js +50 -0
  23. package/dist/host-control/capability.js.map +1 -0
  24. package/dist/host-control/catalog.d.ts +48 -0
  25. package/dist/host-control/catalog.d.ts.map +1 -0
  26. package/dist/host-control/catalog.js +149 -0
  27. package/dist/host-control/catalog.js.map +1 -0
  28. package/dist/host-control/client.d.ts +76 -0
  29. package/dist/host-control/client.d.ts.map +1 -0
  30. package/dist/host-control/client.js +203 -0
  31. package/dist/host-control/client.js.map +1 -0
  32. package/dist/host-control/index.d.ts +18 -0
  33. package/dist/host-control/index.d.ts.map +1 -0
  34. package/dist/host-control/index.js +14 -0
  35. package/dist/host-control/index.js.map +1 -0
  36. package/dist/host-control/load.d.ts +136 -0
  37. package/dist/host-control/load.d.ts.map +1 -0
  38. package/dist/host-control/load.js +241 -0
  39. package/dist/host-control/load.js.map +1 -0
  40. package/dist/host-control/sizing.d.ts +207 -0
  41. package/dist/host-control/sizing.d.ts.map +1 -0
  42. package/dist/host-control/sizing.js +464 -0
  43. package/dist/host-control/sizing.js.map +1 -0
  44. package/dist/host-control/types.d.ts +174 -0
  45. package/dist/host-control/types.d.ts.map +1 -0
  46. package/dist/host-control/types.js +16 -0
  47. package/dist/host-control/types.js.map +1 -0
  48. package/dist/prd-controller.d.ts +10 -12
  49. package/dist/prd-controller.d.ts.map +1 -1
  50. package/dist/prd-controller.js +24 -54
  51. package/dist/prd-controller.js.map +1 -1
  52. package/package.json +3 -3
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Per-endpoint host-control capability detection (Local model host control, D1).
3
+ *
4
+ * The control plane is an OPTIONAL capability detected per-endpoint, never
5
+ * assumed. `detectHostCapability` runs a CHEAP, FAIL-SAFE probe against the host
6
+ * root's control surface: a 2xx with a well-formed (non-empty, usable) catalog
7
+ * means controllable; ANY other outcome -- error, non-2xx, parse failure,
8
+ * timeout, or an empty/unusable payload -- means NOT controllable, so the caller
9
+ * falls back to the `/v1/models` floor. This guarantees a plain
10
+ * OpenAI-compatible endpoint is never broken by the probe.
11
+ */
12
+ import type { HostCapability } from './types.js';
13
+ /**
14
+ * Pure decision: does a control-surface payload prove the host is controllable?
15
+ * True only when the payload normalizes to at least one usable loadable-model
16
+ * entry. A malformed, empty, or non-catalog payload yields false. Exported for
17
+ * unit testing the interpret-a-payload -> controllable? boundary in isolation.
18
+ */
19
+ export declare function isControllableCatalogPayload(payload: unknown): boolean;
20
+ /**
21
+ * Probe an endpoint's host root for the optional control capability. Cheap (a
22
+ * single GET, time-boxed by the client) and FAIL-SAFE: every failure mode --
23
+ * SSRF rejection, network error, non-2xx, invalid JSON, timeout, or an
24
+ * empty/unusable catalog -- resolves to `{ controllable: false }` rather than
25
+ * throwing, so the caller can unconditionally proceed to the floor.
26
+ *
27
+ * `apiKey` is the bearer VALUE (resolved from the Telora secret) authorizing the
28
+ * probe against an auth-protected host.
29
+ */
30
+ export declare function detectHostCapability(baseUrl: string, apiKey?: string | null): Promise<HostCapability>;
31
+ //# sourceMappingURL=capability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../../src/host-control/capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEtE;AAED;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GACrB,OAAO,CAAC,cAAc,CAAC,CAezB"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Per-endpoint host-control capability detection (Local model host control, D1).
3
+ *
4
+ * The control plane is an OPTIONAL capability detected per-endpoint, never
5
+ * assumed. `detectHostCapability` runs a CHEAP, FAIL-SAFE probe against the host
6
+ * root's control surface: a 2xx with a well-formed (non-empty, usable) catalog
7
+ * means controllable; ANY other outcome -- error, non-2xx, parse failure,
8
+ * timeout, or an empty/unusable payload -- means NOT controllable, so the caller
9
+ * falls back to the `/v1/models` floor. This guarantees a plain
10
+ * OpenAI-compatible endpoint is never broken by the probe.
11
+ */
12
+ import { hostGet, hostRootFromBaseUrl, HOST_CONTROL_FETCH_TIMEOUT_MS } from './client.js';
13
+ import { HOST_CONTROL_CATALOG_PATH, normalizeLocalCatalog } from './catalog.js';
14
+ /**
15
+ * Pure decision: does a control-surface payload prove the host is controllable?
16
+ * True only when the payload normalizes to at least one usable loadable-model
17
+ * entry. A malformed, empty, or non-catalog payload yields false. Exported for
18
+ * unit testing the interpret-a-payload -> controllable? boundary in isolation.
19
+ */
20
+ export function isControllableCatalogPayload(payload) {
21
+ return normalizeLocalCatalog(payload).length > 0;
22
+ }
23
+ /**
24
+ * Probe an endpoint's host root for the optional control capability. Cheap (a
25
+ * single GET, time-boxed by the client) and FAIL-SAFE: every failure mode --
26
+ * SSRF rejection, network error, non-2xx, invalid JSON, timeout, or an
27
+ * empty/unusable catalog -- resolves to `{ controllable: false }` rather than
28
+ * throwing, so the caller can unconditionally proceed to the floor.
29
+ *
30
+ * `apiKey` is the bearer VALUE (resolved from the Telora secret) authorizing the
31
+ * probe against an auth-protected host.
32
+ */
33
+ export async function detectHostCapability(baseUrl, apiKey) {
34
+ const root = hostRootFromBaseUrl(baseUrl);
35
+ const catalogUrl = `${root}${HOST_CONTROL_CATALOG_PATH}`;
36
+ try {
37
+ const payload = await hostGet(catalogUrl, {
38
+ apiKey,
39
+ timeoutMs: HOST_CONTROL_FETCH_TIMEOUT_MS,
40
+ });
41
+ return { controllable: isControllableCatalogPayload(payload) };
42
+ }
43
+ catch {
44
+ // Fail-safe: any error => not controllable. Detail is intentionally swallowed
45
+ // here (the catalog reader logs on its own fall-through); the probe must never
46
+ // surface a probe-oracle or break the floor path.
47
+ return { controllable: false };
48
+ }
49
+ }
50
+ //# sourceMappingURL=capability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.js","sourceRoot":"","sources":["../../src/host-control/capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC;AAC1F,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAGhF;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAAC,OAAgB;IAC3D,OAAO,qBAAqB,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAe,EACf,MAAsB;IAEtB,MAAM,IAAI,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,GAAG,IAAI,GAAG,yBAAyB,EAAE,CAAC;IACzD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAU,UAAU,EAAE;YACjD,MAAM;YACN,SAAS,EAAE,6BAA6B;SACzC,CAAC,CAAC;QACH,OAAO,EAAE,YAAY,EAAE,4BAA4B,CAAC,OAAO,CAAC,EAAE,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,8EAA8E;QAC9E,+EAA+E;QAC/E,kDAAkD;QAClD,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IACjC,CAAC;AACH,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Loadable-model catalog reader for the host-control plane (Local model host
3
+ * control, D1).
4
+ *
5
+ * A controllable host (e.g. Unsloth Studio @ :8888) exposes its FULL
6
+ * downloaded-but-unloaded catalog under the control surface
7
+ * `<root>/api/models/local`, returning every loadable model -- not just the one
8
+ * currently loaded that `/v1/models` reports. This module reads that catalog and
9
+ * degrades CLEANLY to the `/v1/models` floor (via discovery-poll's
10
+ * `fetchModels`) when the host is not controllable or the catalog read fails for
11
+ * any reason. The floor path is byte-identical to today's behavior, so a plain
12
+ * OpenAI-compatible endpoint is never broken.
13
+ *
14
+ * The live catalog JSON shape is host-specific (observed: 25 entries under
15
+ * `/api/models/local`, each carrying a load path). We do NOT have the exact live
16
+ * shape pinned, so `normalizeLocalCatalog` is DEFENSIVE: it tolerates a range of
17
+ * reasonable container and field names and is easy to extend per the runbook's
18
+ * live-shape reconciliation.
19
+ */
20
+ import type { LoadableCatalog, LoadableModel } from './types.js';
21
+ /** The control-surface path (under the host ROOT, not `/v1`) for the full catalog. */
22
+ export declare const HOST_CONTROL_CATALOG_PATH = "/api/models/local";
23
+ /**
24
+ * Normalize a control-surface catalog payload into clean `LoadableModel`
25
+ * entries. Pure + DEFENSIVE: tolerates a bare array or a wrapped container,
26
+ * non-object items, missing/blank ids (dropped), and a missing model_path
27
+ * (captured as null). De-duplicates by id (first entry with a model_path wins
28
+ * its path). Exported for unit testing.
29
+ */
30
+ export declare function normalizeLocalCatalog(payload: unknown): LoadableModel[];
31
+ /**
32
+ * Read an endpoint's loadable-model catalog, preferring the controllable host
33
+ * catalog and degrading cleanly to the `/v1/models` floor.
34
+ *
35
+ * Control path: GET `<root>/api/models/local`, normalize, and if it yields at
36
+ * least one usable entry return the full loadable list (`controllable: true`).
37
+ *
38
+ * Floor path: on ANY control-surface failure (unreachable, non-2xx, invalid
39
+ * JSON, timeout, or an empty/unusable catalog) fall back to `fetchModels`
40
+ * (`/v1/models`) so behavior is exactly today's load-then-discover. The floor
41
+ * fetch's own failure propagates to the caller (same contract as the existing
42
+ * discovery path).
43
+ *
44
+ * `apiKey` is the bearer VALUE (resolved from the Telora secret); it authorizes
45
+ * both the control probe and the floor fetch.
46
+ */
47
+ export declare function readLoadableCatalog(baseUrl: string, apiKey?: string | null): Promise<LoadableCatalog>;
48
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/host-control/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AASH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEjE,sFAAsF;AACtF,eAAO,MAAM,yBAAyB,sBAAsB,CAAC;AAgD7D;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,aAAa,EAAE,CAkBvE;AAYD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GACrB,OAAO,CAAC,eAAe,CAAC,CAwB1B"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Loadable-model catalog reader for the host-control plane (Local model host
3
+ * control, D1).
4
+ *
5
+ * A controllable host (e.g. Unsloth Studio @ :8888) exposes its FULL
6
+ * downloaded-but-unloaded catalog under the control surface
7
+ * `<root>/api/models/local`, returning every loadable model -- not just the one
8
+ * currently loaded that `/v1/models` reports. This module reads that catalog and
9
+ * degrades CLEANLY to the `/v1/models` floor (via discovery-poll's
10
+ * `fetchModels`) when the host is not controllable or the catalog read fails for
11
+ * any reason. The floor path is byte-identical to today's behavior, so a plain
12
+ * OpenAI-compatible endpoint is never broken.
13
+ *
14
+ * The live catalog JSON shape is host-specific (observed: 25 entries under
15
+ * `/api/models/local`, each carrying a load path). We do NOT have the exact live
16
+ * shape pinned, so `normalizeLocalCatalog` is DEFENSIVE: it tolerates a range of
17
+ * reasonable container and field names and is easy to extend per the runbook's
18
+ * live-shape reconciliation.
19
+ */
20
+ import { fetchModels } from '../discovery-poll.js';
21
+ import { hostGet, hostRootFromBaseUrl, HOST_CONTROL_FETCH_TIMEOUT_MS, HostControlError, } from './client.js';
22
+ /** The control-surface path (under the host ROOT, not `/v1`) for the full catalog. */
23
+ export const HOST_CONTROL_CATALOG_PATH = '/api/models/local';
24
+ /**
25
+ * Candidate keys that may carry the array of catalog entries. The live host
26
+ * (Unsloth Studio) returned a bare array under `/api/models/local`; other hosts
27
+ * may wrap it. Defensive: we accept a top-level array OR any of these container
28
+ * keys. Easy to extend per the runbook.
29
+ */
30
+ const CATALOG_ARRAY_KEYS = ['models', 'data', 'local', 'items'];
31
+ /**
32
+ * Candidate keys for a catalog entry's served model id. `id` is canonical;
33
+ * `model`/`name` are common alternates on local-model hosts.
34
+ */
35
+ const ID_KEYS = ['id', 'model', 'name'];
36
+ /**
37
+ * Candidate keys for a catalog entry's host load path. `model_path` is the
38
+ * observed convention; the rest are reasonable alternates. Captured verbatim so
39
+ * Delivery 2 can resolve id -> path to load the model.
40
+ */
41
+ const MODEL_PATH_KEYS = ['model_path', 'modelPath', 'path', 'file', 'gguf_path'];
42
+ /** Read the first string-valued key present on an object, trimmed; '' if none. */
43
+ function firstString(obj, keys) {
44
+ for (const key of keys) {
45
+ const v = obj[key];
46
+ if (typeof v === 'string') {
47
+ const trimmed = v.trim();
48
+ if (trimmed)
49
+ return trimmed;
50
+ }
51
+ }
52
+ return '';
53
+ }
54
+ /** Extract the catalog-entry array from a payload that is either a bare array or
55
+ * a single-keyed container. Returns [] when no array is found (defensive). */
56
+ function extractEntryArray(payload) {
57
+ if (Array.isArray(payload))
58
+ return payload;
59
+ if (!payload || typeof payload !== 'object')
60
+ return [];
61
+ const obj = payload;
62
+ for (const key of CATALOG_ARRAY_KEYS) {
63
+ const v = obj[key];
64
+ if (Array.isArray(v))
65
+ return v;
66
+ }
67
+ return [];
68
+ }
69
+ /**
70
+ * Normalize a control-surface catalog payload into clean `LoadableModel`
71
+ * entries. Pure + DEFENSIVE: tolerates a bare array or a wrapped container,
72
+ * non-object items, missing/blank ids (dropped), and a missing model_path
73
+ * (captured as null). De-duplicates by id (first entry with a model_path wins
74
+ * its path). Exported for unit testing.
75
+ */
76
+ export function normalizeLocalCatalog(payload) {
77
+ const entries = extractEntryArray(payload);
78
+ const byId = new Map();
79
+ for (const item of entries) {
80
+ if (!item || typeof item !== 'object')
81
+ continue;
82
+ const obj = item;
83
+ const id = firstString(obj, ID_KEYS);
84
+ if (!id)
85
+ continue; // an entry with no usable id is unselectable -- drop it.
86
+ const modelPath = firstString(obj, MODEL_PATH_KEYS) || null;
87
+ const existing = byId.get(id);
88
+ if (!existing) {
89
+ byId.set(id, { id, modelPath });
90
+ }
91
+ else if (existing.modelPath === null && modelPath !== null) {
92
+ // Prefer the duplicate that carries a load path over a path-less twin.
93
+ byId.set(id, { id, modelPath });
94
+ }
95
+ }
96
+ return [...byId.values()];
97
+ }
98
+ /** Build the floor-path catalog from a `/v1/models` id list. `modelPath` is null
99
+ * on the floor -- the floor knows only the loaded id, never a load path. */
100
+ function floorCatalog(ids) {
101
+ return {
102
+ controllable: false,
103
+ ids,
104
+ models: ids.map((id) => ({ id, modelPath: null })),
105
+ };
106
+ }
107
+ /**
108
+ * Read an endpoint's loadable-model catalog, preferring the controllable host
109
+ * catalog and degrading cleanly to the `/v1/models` floor.
110
+ *
111
+ * Control path: GET `<root>/api/models/local`, normalize, and if it yields at
112
+ * least one usable entry return the full loadable list (`controllable: true`).
113
+ *
114
+ * Floor path: on ANY control-surface failure (unreachable, non-2xx, invalid
115
+ * JSON, timeout, or an empty/unusable catalog) fall back to `fetchModels`
116
+ * (`/v1/models`) so behavior is exactly today's load-then-discover. The floor
117
+ * fetch's own failure propagates to the caller (same contract as the existing
118
+ * discovery path).
119
+ *
120
+ * `apiKey` is the bearer VALUE (resolved from the Telora secret); it authorizes
121
+ * both the control probe and the floor fetch.
122
+ */
123
+ export async function readLoadableCatalog(baseUrl, apiKey) {
124
+ const root = hostRootFromBaseUrl(baseUrl);
125
+ const catalogUrl = `${root}${HOST_CONTROL_CATALOG_PATH}`;
126
+ try {
127
+ const payload = await hostGet(catalogUrl, {
128
+ apiKey,
129
+ timeoutMs: HOST_CONTROL_FETCH_TIMEOUT_MS,
130
+ });
131
+ const models = normalizeLocalCatalog(payload);
132
+ if (models.length > 0) {
133
+ return { controllable: true, ids: models.map((m) => m.id), models };
134
+ }
135
+ // Well-formed but empty (or no usable ids): not a usable control catalog --
136
+ // fall through to the floor so the picklist still offers the loaded model.
137
+ }
138
+ catch (err) {
139
+ // Fail-safe: any control-surface error means "not controllable here". Log the
140
+ // rich detail daemon-side (address-free dbMessage available for categorizing)
141
+ // and fall through to the floor. Never break a plain endpoint.
142
+ const detail = err instanceof HostControlError ? err.message : err.message;
143
+ console.log(`[host-control] catalog read fell back to /v1/models floor: ${detail}`);
144
+ }
145
+ // Floor: the OpenAI-compatible `/v1/models` listing (today's behavior).
146
+ const ids = await fetchModels(baseUrl, apiKey);
147
+ return floorCatalog(ids);
148
+ }
149
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/host-control/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Typed JSON HTTP helpers for the host-control plane (Local model host control, D1).
3
+ *
4
+ * These mirror the security posture of `discovery-poll.ts` exactly: the host
5
+ * control surface is an operator-supplied LAN endpoint, so every request must
6
+ * (1) pass the SSRF guard before any network call (TEL-5), (2) time-box with an
7
+ * AbortController so an unreachable host fails fast (TEL-6), (3) cap the body
8
+ * read so a malicious/oversized response can't OOM the daemon (TEL-6), and
9
+ * (4) surface only address-free error categories -- the raw URL/system detail is
10
+ * logged daemon-side, never returned to a caller that may write it to an
11
+ * org-readable column (TEL-5, probe-oracle defense).
12
+ *
13
+ * `hostGet`/`hostPost` are deliberately generic over the decoded shape; the
14
+ * caller validates the `unknown` payload with a defensive normalizer (the
15
+ * control-surface JSON shape is host-specific and reconciled per the runbook).
16
+ */
17
+ /**
18
+ * Time-box a single host-control fetch. Mirrors `DISCOVERY_FETCH_TIMEOUT_MS`
19
+ * (5000ms) so the control probe fails fast on an unreachable host exactly like
20
+ * the `/v1/models` probe it rides alongside. Re-exported so callers and tests
21
+ * reference one source of truth.
22
+ */
23
+ export declare const HOST_CONTROL_FETCH_TIMEOUT_MS = 5000;
24
+ /**
25
+ * Hard ceiling on a host-control response body. A model catalog is small (~25
26
+ * entries observed live); this cap defends against an endpoint streaming an
27
+ * unbounded body to OOM the daemon (TEL-6). Mirrors discovery-poll's
28
+ * `MAX_DISCOVERY_BYTES` (1MB) -- declared as its own literal rather than
29
+ * re-exported to avoid an init-order coupling with the cyclic discovery-poll
30
+ * import (this module is imported by discovery-poll, which it also imports from).
31
+ */
32
+ export declare const HOST_CONTROL_MAX_BYTES = 1000000;
33
+ /**
34
+ * Address-free error categories for the host-control client. The control plane
35
+ * is fail-safe -- callers turn any of these into "not controllable, fall back to
36
+ * the floor" rather than propagating detail -- but they are kept distinct (and
37
+ * address-free) so daemon-side logs can categorize without leaking the endpoint.
38
+ */
39
+ export declare const HOST_CONTROL_ERROR_REJECTED_URL = "Rejected non-http(s) host URL";
40
+ export declare const HOST_CONTROL_ERROR_STATUS = "Host returned an error status";
41
+ export declare const HOST_CONTROL_ERROR_UNREACHABLE = "Host unreachable or timed out";
42
+ export declare const HOST_CONTROL_ERROR_INVALID = "Host response was not valid";
43
+ /**
44
+ * A host-control client error carrying a sanitized, address-free `dbMessage`
45
+ * alongside the rich `message` (URL/system detail) that is safe only for the
46
+ * daemon log. Same precedent as `discovery-poll`'s `DiscoveryError`.
47
+ */
48
+ export declare class HostControlError extends Error {
49
+ /** Address-free category string safe to surface anywhere (TEL-5). */
50
+ readonly dbMessage: string;
51
+ constructor(message: string,
52
+ /** Address-free category string safe to surface anywhere (TEL-5). */
53
+ dbMessage: string);
54
+ }
55
+ /** Options common to host-control requests. */
56
+ export interface HostRequestOptions {
57
+ /** Bearer key VALUE for an authenticated host (resolved from the Telora secret). */
58
+ apiKey?: string | null;
59
+ /** Per-request timeout override; defaults to `HOST_CONTROL_FETCH_TIMEOUT_MS`. */
60
+ timeoutMs?: number;
61
+ }
62
+ /** Typed JSON GET against a host-control URL. See `hostRequest` for the posture. */
63
+ export declare function hostGet<T>(url: string, opts?: HostRequestOptions): Promise<T>;
64
+ /** Typed JSON POST against a host-control URL. The body is JSON-encoded. */
65
+ export declare function hostPost<T>(url: string, body: unknown, opts?: HostRequestOptions): Promise<T>;
66
+ /**
67
+ * Derive the host ROOT from a stored `baseUrl`. The registry stores the
68
+ * OpenAI-compatible base (e.g. `http://host:8888/v1`), but the control surface
69
+ * lives under `/api` at the host root, NOT under `/v1`. Strip a single trailing
70
+ * `/v1` segment and any trailing slashes so callers can append `/api/...`. A
71
+ * `baseUrl` that is already a bare root passes through unchanged.
72
+ *
73
+ * Pure + exported for unit testing.
74
+ */
75
+ export declare function hostRootFromBaseUrl(baseUrl: string): string;
76
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/host-control/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,UAAY,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,kCAAkC,CAAC;AAC/E,eAAO,MAAM,yBAAyB,kCAAkC,CAAC;AACzE,eAAO,MAAM,8BAA8B,kCAAkC,CAAC;AAC9E,eAAO,MAAM,0BAA0B,gCAAgC,CAAC;AAExE;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IAGvC,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM;gBAF1B,OAAO,EAAE,MAAM;IACf,qEAAqE;IAC5D,SAAS,EAAE,MAAM;CAK7B;AAED,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA0ID,oFAAoF;AACpF,wBAAsB,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,CAAC,CAAC,CAEvF;AAED,4EAA4E;AAC5E,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,OAAO,EACb,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,CAAC,CAAC,CAMZ;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAO3D"}
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Typed JSON HTTP helpers for the host-control plane (Local model host control, D1).
3
+ *
4
+ * These mirror the security posture of `discovery-poll.ts` exactly: the host
5
+ * control surface is an operator-supplied LAN endpoint, so every request must
6
+ * (1) pass the SSRF guard before any network call (TEL-5), (2) time-box with an
7
+ * AbortController so an unreachable host fails fast (TEL-6), (3) cap the body
8
+ * read so a malicious/oversized response can't OOM the daemon (TEL-6), and
9
+ * (4) surface only address-free error categories -- the raw URL/system detail is
10
+ * logged daemon-side, never returned to a caller that may write it to an
11
+ * org-readable column (TEL-5, probe-oracle defense).
12
+ *
13
+ * `hostGet`/`hostPost` are deliberately generic over the decoded shape; the
14
+ * caller validates the `unknown` payload with a defensive normalizer (the
15
+ * control-surface JSON shape is host-specific and reconciled per the runbook).
16
+ */
17
+ import { isAllowedDiscoveryUrl } from '../discovery-poll.js';
18
+ /**
19
+ * Time-box a single host-control fetch. Mirrors `DISCOVERY_FETCH_TIMEOUT_MS`
20
+ * (5000ms) so the control probe fails fast on an unreachable host exactly like
21
+ * the `/v1/models` probe it rides alongside. Re-exported so callers and tests
22
+ * reference one source of truth.
23
+ */
24
+ export const HOST_CONTROL_FETCH_TIMEOUT_MS = 5000;
25
+ /**
26
+ * Hard ceiling on a host-control response body. A model catalog is small (~25
27
+ * entries observed live); this cap defends against an endpoint streaming an
28
+ * unbounded body to OOM the daemon (TEL-6). Mirrors discovery-poll's
29
+ * `MAX_DISCOVERY_BYTES` (1MB) -- declared as its own literal rather than
30
+ * re-exported to avoid an init-order coupling with the cyclic discovery-poll
31
+ * import (this module is imported by discovery-poll, which it also imports from).
32
+ */
33
+ export const HOST_CONTROL_MAX_BYTES = 1_000_000;
34
+ /**
35
+ * Address-free error categories for the host-control client. The control plane
36
+ * is fail-safe -- callers turn any of these into "not controllable, fall back to
37
+ * the floor" rather than propagating detail -- but they are kept distinct (and
38
+ * address-free) so daemon-side logs can categorize without leaking the endpoint.
39
+ */
40
+ export const HOST_CONTROL_ERROR_REJECTED_URL = 'Rejected non-http(s) host URL';
41
+ export const HOST_CONTROL_ERROR_STATUS = 'Host returned an error status';
42
+ export const HOST_CONTROL_ERROR_UNREACHABLE = 'Host unreachable or timed out';
43
+ export const HOST_CONTROL_ERROR_INVALID = 'Host response was not valid';
44
+ /**
45
+ * A host-control client error carrying a sanitized, address-free `dbMessage`
46
+ * alongside the rich `message` (URL/system detail) that is safe only for the
47
+ * daemon log. Same precedent as `discovery-poll`'s `DiscoveryError`.
48
+ */
49
+ export class HostControlError extends Error {
50
+ dbMessage;
51
+ constructor(message,
52
+ /** Address-free category string safe to surface anywhere (TEL-5). */
53
+ dbMessage) {
54
+ super(message);
55
+ this.dbMessage = dbMessage;
56
+ this.name = 'HostControlError';
57
+ }
58
+ }
59
+ /**
60
+ * Build request headers for a host-control call. When a bearer key VALUE is
61
+ * provided an `Authorization: Bearer <value>` header is added; a blank/absent
62
+ * value yields no auth header (unauthenticated probe). The value is never
63
+ * logged or persisted by this module. Mirrors `buildDiscoveryHeaders`.
64
+ */
65
+ function buildHostHeaders(apiKey, extra) {
66
+ const headers = { accept: 'application/json', ...extra };
67
+ const value = typeof apiKey === 'string' ? apiKey.trim() : '';
68
+ if (value)
69
+ headers.Authorization = `Bearer ${value}`;
70
+ return headers;
71
+ }
72
+ /**
73
+ * Read a fetch response body with a hard byte ceiling, aborting once the cap is
74
+ * exceeded so a lying Content-Length (or a chunked body with none) can't OOM the
75
+ * daemon (TEL-6). Returns the decoded text. Throws a `HostControlError` past the
76
+ * ceiling. Mirrors `discovery-poll`'s `readBodyCapped`.
77
+ */
78
+ async function readBodyCapped(res) {
79
+ const body = res.body;
80
+ if (!body) {
81
+ // No stream available -- fall back to arrayBuffer but still cap by byteLength.
82
+ const buf = await res.arrayBuffer();
83
+ if (buf.byteLength > HOST_CONTROL_MAX_BYTES) {
84
+ throw new HostControlError('Response body exceeded byte ceiling', HOST_CONTROL_ERROR_INVALID);
85
+ }
86
+ return new TextDecoder().decode(buf);
87
+ }
88
+ const reader = body.getReader();
89
+ const chunks = [];
90
+ let total = 0;
91
+ try {
92
+ for (;;) {
93
+ const { done, value } = await reader.read();
94
+ if (done)
95
+ break;
96
+ if (value) {
97
+ total += value.byteLength;
98
+ if (total > HOST_CONTROL_MAX_BYTES) {
99
+ throw new HostControlError('Response body exceeded byte ceiling', HOST_CONTROL_ERROR_INVALID);
100
+ }
101
+ chunks.push(value);
102
+ }
103
+ }
104
+ }
105
+ finally {
106
+ // Stop the underlying transfer; ignore cancel errors.
107
+ await reader.cancel().catch(() => { });
108
+ }
109
+ const merged = new Uint8Array(total);
110
+ let offset = 0;
111
+ for (const c of chunks) {
112
+ merged.set(c, offset);
113
+ offset += c.byteLength;
114
+ }
115
+ return new TextDecoder().decode(merged);
116
+ }
117
+ /**
118
+ * Map a non-2xx status to an address-free category. 401/403 surface as a status
119
+ * error here (the host-control plane is optional, so an auth wall just means we
120
+ * fall back to the floor) -- unlike discovery, we don't disclose an auth-specific
121
+ * message, because a control-surface 401 is not independently actionable.
122
+ */
123
+ function statusDbMessage(_status) {
124
+ return HOST_CONTROL_ERROR_STATUS;
125
+ }
126
+ /**
127
+ * Issue a host-control request and decode the JSON body, enforcing the full
128
+ * security posture (SSRF guard, timeout, byte cap, address-free errors). Shared
129
+ * core of `hostGet`/`hostPost`. Throws a `HostControlError` on any failure.
130
+ */
131
+ async function hostRequest(url, init, opts) {
132
+ // TEL-5: validate the operator-supplied URL before any network call -- reject
133
+ // anything that isn't a parseable http/https URL (no file:, ftp:, etc).
134
+ if (!isAllowedDiscoveryUrl(url)) {
135
+ throw new HostControlError(`Rejected non-http(s) host-control URL: ${url}`, HOST_CONTROL_ERROR_REJECTED_URL);
136
+ }
137
+ const timeoutMs = opts.timeoutMs ?? HOST_CONTROL_FETCH_TIMEOUT_MS;
138
+ const controller = new AbortController();
139
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
140
+ try {
141
+ const extra = init.method === 'POST' && init.contentType ? { 'content-type': init.contentType } : undefined;
142
+ const res = await fetch(url, {
143
+ method: init.method,
144
+ signal: controller.signal,
145
+ headers: buildHostHeaders(opts.apiKey, extra),
146
+ ...(init.body !== undefined ? { body: init.body } : {}),
147
+ });
148
+ if (!res.ok) {
149
+ throw new HostControlError(`Host returned HTTP ${res.status}`, statusDbMessage(res.status));
150
+ }
151
+ // TEL-6: reject a too-large declared body up front, then defensively cap the
152
+ // actual stream in case the header lied / is absent.
153
+ const declared = Number(res.headers.get('content-length'));
154
+ if (Number.isFinite(declared) && declared > HOST_CONTROL_MAX_BYTES) {
155
+ throw new HostControlError(`Declared Content-Length ${declared} exceeds cap`, HOST_CONTROL_ERROR_INVALID);
156
+ }
157
+ const text = await readBodyCapped(res);
158
+ try {
159
+ return JSON.parse(text);
160
+ }
161
+ catch {
162
+ throw new HostControlError('Response body was not valid JSON', HOST_CONTROL_ERROR_INVALID);
163
+ }
164
+ }
165
+ catch (err) {
166
+ if (err instanceof HostControlError)
167
+ throw err;
168
+ if (err.name === 'AbortError') {
169
+ throw new HostControlError(`Timed out after ${timeoutMs}ms reaching ${url}`, HOST_CONTROL_ERROR_UNREACHABLE);
170
+ }
171
+ // Network-level failure (DNS, connection refused, TLS): generic to callers.
172
+ throw new HostControlError(err.message || `Failed to reach ${url}`, HOST_CONTROL_ERROR_UNREACHABLE);
173
+ }
174
+ finally {
175
+ clearTimeout(timer);
176
+ }
177
+ }
178
+ /** Typed JSON GET against a host-control URL. See `hostRequest` for the posture. */
179
+ export async function hostGet(url, opts = {}) {
180
+ return hostRequest(url, { method: 'GET' }, opts);
181
+ }
182
+ /** Typed JSON POST against a host-control URL. The body is JSON-encoded. */
183
+ export async function hostPost(url, body, opts = {}) {
184
+ return hostRequest(url, { method: 'POST', body: JSON.stringify(body ?? {}), contentType: 'application/json' }, opts);
185
+ }
186
+ /**
187
+ * Derive the host ROOT from a stored `baseUrl`. The registry stores the
188
+ * OpenAI-compatible base (e.g. `http://host:8888/v1`), but the control surface
189
+ * lives under `/api` at the host root, NOT under `/v1`. Strip a single trailing
190
+ * `/v1` segment and any trailing slashes so callers can append `/api/...`. A
191
+ * `baseUrl` that is already a bare root passes through unchanged.
192
+ *
193
+ * Pure + exported for unit testing.
194
+ */
195
+ export function hostRootFromBaseUrl(baseUrl) {
196
+ // Trim trailing slashes first so a trailing-slash `/v1/` still matches.
197
+ const trimmed = baseUrl.replace(/\/+$/, '');
198
+ // Strip exactly one trailing `/v1` segment (case-sensitive, the API convention).
199
+ const root = trimmed.replace(/\/v1$/, '');
200
+ // A `/v1` with its own trailing slash (`/v1/`) leaves a dangling slash; clean it.
201
+ return root.replace(/\/+$/, '');
202
+ }
203
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/host-control/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Host-control plane barrel (Local model host control, D1).
3
+ *
4
+ * The spine for detecting and reading a controllable model host ABOVE the
5
+ * OpenAI-compatible `/v1/models` floor. Delivery 1 ships the client, capability
6
+ * detection, and catalog read; later deliveries add `load.ts` (id -> model_path
7
+ * load/unload) and `sizing.ts` -- export those from here when they land.
8
+ */
9
+ export type { HostCapability, LoadableCatalog, LoadableModel, HostStatus, LoadModelParams, SwapAction, SwapDecision, HostHardware, KvCacheEstimate, KvCacheEstimateParams, SafeMaxSeqInput, SafeMaxSeqResult, } from './types.js';
10
+ export { hostGet, hostPost, hostRootFromBaseUrl, HostControlError, HOST_CONTROL_FETCH_TIMEOUT_MS, HOST_CONTROL_MAX_BYTES, HOST_CONTROL_ERROR_REJECTED_URL, HOST_CONTROL_ERROR_STATUS, HOST_CONTROL_ERROR_UNREACHABLE, HOST_CONTROL_ERROR_INVALID, } from './client.js';
11
+ export type { HostRequestOptions } from './client.js';
12
+ export { detectHostCapability, isControllableCatalogPayload } from './capability.js';
13
+ export { readLoadableCatalog, normalizeLocalCatalog, HOST_CONTROL_CATALOG_PATH, } from './catalog.js';
14
+ export { loadModel, unloadModel, getStatus, resolveModelPath, decideSwap, ensureLoaded, normalizeStatus, DEFAULT_MAX_SEQ_LENGTH, HOST_CONTROL_LOAD_PATH, HOST_CONTROL_UNLOAD_PATH, HOST_CONTROL_STATUS_PATH, HOST_CONTROL_LOAD_READY_TIMEOUT_MS, HOST_CONTROL_LOAD_POLL_INTERVAL_MS, HOST_CONTROL_ERROR_LOAD_TIMEOUT, } from './load.js';
15
+ export type { EnsureLoadedOptions } from './load.js';
16
+ export { getHardware, getKvCacheEstimate, normalizeHardware, normalizeKvCacheEstimate, computeSafeMaxSeqLength, resolveSafeLoadOptions, CODEX_TURN1_FLOOR, VRAM_SAFETY_MARGIN_MB, KV_CACHE_MB_PER_TOKEN, KV_QUANT_FOOTPRINT_FACTOR, KV_QUANT_CACHE_TYPE, MAX_SEQ_LENGTH_CEILING, HOST_CONTROL_HARDWARE_PATH, HOST_CONTROL_KV_CACHE_PATH, } from './sizing.js';
17
+ export type { SafeLoadSizingHints } from './sizing.js';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/host-control/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,YAAY,EACV,cAAc,EACd,eAAe,EACf,aAAa,EACb,UAAU,EACV,eAAe,EACf,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,OAAO,EACP,QAAQ,EACR,mBAAmB,EACnB,gBAAgB,EAChB,6BAA6B,EAC7B,sBAAsB,EACtB,+BAA+B,EAC/B,yBAAyB,EACzB,8BAA8B,EAC9B,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAErF,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,SAAS,EACT,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,kCAAkC,EAClC,kCAAkC,EAClC,+BAA+B,GAChC,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAErD,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Host-control plane barrel (Local model host control, D1).
3
+ *
4
+ * The spine for detecting and reading a controllable model host ABOVE the
5
+ * OpenAI-compatible `/v1/models` floor. Delivery 1 ships the client, capability
6
+ * detection, and catalog read; later deliveries add `load.ts` (id -> model_path
7
+ * load/unload) and `sizing.ts` -- export those from here when they land.
8
+ */
9
+ export { hostGet, hostPost, hostRootFromBaseUrl, HostControlError, HOST_CONTROL_FETCH_TIMEOUT_MS, HOST_CONTROL_MAX_BYTES, HOST_CONTROL_ERROR_REJECTED_URL, HOST_CONTROL_ERROR_STATUS, HOST_CONTROL_ERROR_UNREACHABLE, HOST_CONTROL_ERROR_INVALID, } from './client.js';
10
+ export { detectHostCapability, isControllableCatalogPayload } from './capability.js';
11
+ export { readLoadableCatalog, normalizeLocalCatalog, HOST_CONTROL_CATALOG_PATH, } from './catalog.js';
12
+ export { loadModel, unloadModel, getStatus, resolveModelPath, decideSwap, ensureLoaded, normalizeStatus, DEFAULT_MAX_SEQ_LENGTH, HOST_CONTROL_LOAD_PATH, HOST_CONTROL_UNLOAD_PATH, HOST_CONTROL_STATUS_PATH, HOST_CONTROL_LOAD_READY_TIMEOUT_MS, HOST_CONTROL_LOAD_POLL_INTERVAL_MS, HOST_CONTROL_ERROR_LOAD_TIMEOUT, } from './load.js';
13
+ export { getHardware, getKvCacheEstimate, normalizeHardware, normalizeKvCacheEstimate, computeSafeMaxSeqLength, resolveSafeLoadOptions, CODEX_TURN1_FLOOR, VRAM_SAFETY_MARGIN_MB, KV_CACHE_MB_PER_TOKEN, KV_QUANT_FOOTPRINT_FACTOR, KV_QUANT_CACHE_TYPE, MAX_SEQ_LENGTH_CEILING, HOST_CONTROL_HARDWARE_PATH, HOST_CONTROL_KV_CACHE_PATH, } from './sizing.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/host-control/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiBH,OAAO,EACL,OAAO,EACP,QAAQ,EACR,mBAAmB,EACnB,gBAAgB,EAChB,6BAA6B,EAC7B,sBAAsB,EACtB,+BAA+B,EAC/B,yBAAyB,EACzB,8BAA8B,EAC9B,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAErF,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,SAAS,EACT,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,kCAAkC,EAClC,kCAAkC,EAClC,+BAA+B,GAChC,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,aAAa,CAAC"}