zcode-acp-server 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -9
- package/README.zh-CN.md +143 -8
- package/dist/backend/client.d.ts +12 -4
- package/dist/backend/client.d.ts.map +1 -1
- package/dist/backend/client.js +72 -7
- package/dist/backend/client.js.map +1 -1
- package/dist/backend/credentials.d.ts.map +1 -1
- package/dist/backend/credentials.js +2 -1
- package/dist/backend/credentials.js.map +1 -1
- package/dist/backend/listener.d.ts +12 -4
- package/dist/backend/listener.d.ts.map +1 -1
- package/dist/backend/listener.js +68 -12
- package/dist/backend/listener.js.map +1 -1
- package/dist/backend/types.d.ts +1 -1
- package/dist/backend/types.d.ts.map +1 -1
- package/dist/bin/hub.d.ts +16 -0
- package/dist/bin/hub.d.ts.map +1 -0
- package/dist/bin/hub.js +41 -0
- package/dist/bin/hub.js.map +1 -0
- package/dist/bin/quota.d.ts +54 -0
- package/dist/bin/quota.d.ts.map +1 -0
- package/dist/bin/quota.js +333 -0
- package/dist/bin/quota.js.map +1 -0
- package/dist/config/auto-compact.d.ts +23 -0
- package/dist/config/auto-compact.d.ts.map +1 -0
- package/dist/config/auto-compact.js +67 -0
- package/dist/config/auto-compact.js.map +1 -0
- package/dist/config/mcp-discovery.d.ts +34 -0
- package/dist/config/mcp-discovery.d.ts.map +1 -0
- package/dist/config/mcp-discovery.js +153 -0
- package/dist/config/mcp-discovery.js.map +1 -0
- package/dist/config/model-cache.d.ts +12 -1
- package/dist/config/model-cache.d.ts.map +1 -1
- package/dist/config/model-cache.js +30 -8
- package/dist/config/model-cache.js.map +1 -1
- package/dist/config/options.d.ts +87 -9
- package/dist/config/options.d.ts.map +1 -1
- package/dist/config/options.js +248 -45
- package/dist/config/options.js.map +1 -1
- package/dist/config/plugin-commands.d.ts +24 -0
- package/dist/config/plugin-commands.d.ts.map +1 -0
- package/dist/config/plugin-commands.js +107 -0
- package/dist/config/plugin-commands.js.map +1 -0
- package/dist/config/provider-registry.d.ts +60 -0
- package/dist/config/provider-registry.d.ts.map +1 -0
- package/dist/config/provider-registry.js +128 -0
- package/dist/config/provider-registry.js.map +1 -0
- package/dist/config/runtime-model.d.ts +54 -18
- package/dist/config/runtime-model.d.ts.map +1 -1
- package/dist/config/runtime-model.js +105 -61
- package/dist/config/runtime-model.js.map +1 -1
- package/dist/config/skill-discovery.d.ts +35 -0
- package/dist/config/skill-discovery.d.ts.map +1 -0
- package/dist/config/skill-discovery.js +188 -0
- package/dist/config/skill-discovery.js.map +1 -0
- package/dist/handlers/account.d.ts +43 -0
- package/dist/handlers/account.d.ts.map +1 -0
- package/dist/handlers/account.js +59 -0
- package/dist/handlers/account.js.map +1 -0
- package/dist/handlers/background-tasks.d.ts +72 -0
- package/dist/handlers/background-tasks.d.ts.map +1 -0
- package/dist/handlers/background-tasks.js +330 -0
- package/dist/handlers/background-tasks.js.map +1 -0
- package/dist/handlers/dispatch.d.ts.map +1 -1
- package/dist/handlers/dispatch.js +134 -22
- package/dist/handlers/dispatch.js.map +1 -1
- package/dist/handlers/extensions.d.ts +15 -0
- package/dist/handlers/extensions.d.ts.map +1 -1
- package/dist/handlers/extensions.js +43 -27
- package/dist/handlers/extensions.js.map +1 -1
- package/dist/handlers/io.d.ts +31 -3
- package/dist/handlers/io.d.ts.map +1 -1
- package/dist/handlers/io.js +108 -9
- package/dist/handlers/io.js.map +1 -1
- package/dist/handlers/replay.d.ts +79 -0
- package/dist/handlers/replay.d.ts.map +1 -0
- package/dist/handlers/replay.js +252 -0
- package/dist/handlers/replay.js.map +1 -0
- package/dist/handlers/server-requests.d.ts +20 -4
- package/dist/handlers/server-requests.d.ts.map +1 -1
- package/dist/handlers/server-requests.js +293 -63
- package/dist/handlers/server-requests.js.map +1 -1
- package/dist/handlers/session.d.ts +113 -10
- package/dist/handlers/session.d.ts.map +1 -1
- package/dist/handlers/session.js +932 -277
- package/dist/handlers/session.js.map +1 -1
- package/dist/handlers/slash.d.ts +37 -1
- package/dist/handlers/slash.d.ts.map +1 -1
- package/dist/handlers/slash.js +143 -8
- package/dist/handlers/slash.js.map +1 -1
- package/dist/index.js +76 -12
- package/dist/index.js.map +1 -1
- package/dist/interaction/adapter.d.ts +19 -33
- package/dist/interaction/adapter.d.ts.map +1 -1
- package/dist/interaction/adapter.js +80 -79
- package/dist/interaction/adapter.js.map +1 -1
- package/dist/lazy-sessions.d.ts +35 -0
- package/dist/lazy-sessions.d.ts.map +1 -0
- package/dist/lazy-sessions.js +98 -0
- package/dist/lazy-sessions.js.map +1 -0
- package/dist/quota/cache.d.ts +18 -0
- package/dist/quota/cache.d.ts.map +1 -0
- package/dist/quota/cache.js +32 -0
- package/dist/quota/cache.js.map +1 -0
- package/dist/quota/client.d.ts +30 -0
- package/dist/quota/client.d.ts.map +1 -0
- package/dist/quota/client.js +57 -0
- package/dist/quota/client.js.map +1 -0
- package/dist/quota/color.d.ts +58 -0
- package/dist/quota/color.d.ts.map +1 -0
- package/dist/quota/color.js +95 -0
- package/dist/quota/color.js.map +1 -0
- package/dist/quota/combined.d.ts +66 -0
- package/dist/quota/combined.d.ts.map +1 -0
- package/dist/quota/combined.js +179 -0
- package/dist/quota/combined.js.map +1 -0
- package/dist/quota/format.d.ts +105 -0
- package/dist/quota/format.d.ts.map +1 -0
- package/dist/quota/format.js +219 -0
- package/dist/quota/format.js.map +1 -0
- package/dist/quota/index.d.ts +23 -0
- package/dist/quota/index.d.ts.map +1 -0
- package/dist/quota/index.js +42 -0
- package/dist/quota/index.js.map +1 -0
- package/dist/quota/opencode-go/cache.d.ts +17 -0
- package/dist/quota/opencode-go/cache.d.ts.map +1 -0
- package/dist/quota/opencode-go/cache.js +31 -0
- package/dist/quota/opencode-go/cache.js.map +1 -0
- package/dist/quota/opencode-go/client.d.ts +28 -0
- package/dist/quota/opencode-go/client.d.ts.map +1 -0
- package/dist/quota/opencode-go/client.js +48 -0
- package/dist/quota/opencode-go/client.js.map +1 -0
- package/dist/quota/opencode-go/config.d.ts +37 -0
- package/dist/quota/opencode-go/config.d.ts.map +1 -0
- package/dist/quota/opencode-go/config.js +58 -0
- package/dist/quota/opencode-go/config.js.map +1 -0
- package/dist/quota/opencode-go/format.d.ts +36 -0
- package/dist/quota/opencode-go/format.d.ts.map +1 -0
- package/dist/quota/opencode-go/format.js +87 -0
- package/dist/quota/opencode-go/format.js.map +1 -0
- package/dist/quota/opencode-go/index.d.ts +30 -0
- package/dist/quota/opencode-go/index.d.ts.map +1 -0
- package/dist/quota/opencode-go/index.js +108 -0
- package/dist/quota/opencode-go/index.js.map +1 -0
- package/dist/quota/opencode-go/parse.d.ts +41 -0
- package/dist/quota/opencode-go/parse.d.ts.map +1 -0
- package/dist/quota/opencode-go/parse.js +75 -0
- package/dist/quota/opencode-go/parse.js.map +1 -0
- package/dist/quota/opencode-go/types.d.ts +48 -0
- package/dist/quota/opencode-go/types.d.ts.map +1 -0
- package/dist/quota/opencode-go/types.js +11 -0
- package/dist/quota/opencode-go/types.js.map +1 -0
- package/dist/quota/parse.d.ts +33 -0
- package/dist/quota/parse.d.ts.map +1 -0
- package/dist/quota/parse.js +200 -0
- package/dist/quota/parse.js.map +1 -0
- package/dist/quota/types.d.ts +72 -0
- package/dist/quota/types.d.ts.map +1 -0
- package/dist/quota/types.js +10 -0
- package/dist/quota/types.js.map +1 -0
- package/dist/remote/broadcast.d.ts +47 -0
- package/dist/remote/broadcast.d.ts.map +1 -0
- package/dist/remote/broadcast.js +121 -0
- package/dist/remote/broadcast.js.map +1 -0
- package/dist/remote/config.d.ts +32 -0
- package/dist/remote/config.d.ts.map +1 -0
- package/dist/remote/config.js +65 -0
- package/dist/remote/config.js.map +1 -0
- package/dist/remote/endpoint.d.ts +30 -0
- package/dist/remote/endpoint.d.ts.map +1 -0
- package/dist/remote/endpoint.js +213 -0
- package/dist/remote/endpoint.js.map +1 -0
- package/dist/remote/hub-server.d.ts +41 -0
- package/dist/remote/hub-server.d.ts.map +1 -0
- package/dist/remote/hub-server.js +346 -0
- package/dist/remote/hub-server.js.map +1 -0
- package/dist/server.d.ts +139 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +179 -4
- package/dist/server.js.map +1 -1
- package/dist/tasks-index.d.ts +14 -4
- package/dist/tasks-index.d.ts.map +1 -1
- package/dist/tasks-index.js +145 -46
- package/dist/tasks-index.js.map +1 -1
- package/dist/translators/event-translator.d.ts +33 -0
- package/dist/translators/event-translator.d.ts.map +1 -1
- package/dist/translators/event-translator.js +101 -0
- package/dist/translators/event-translator.js.map +1 -1
- package/dist/translators/index.d.ts +1 -1
- package/dist/translators/index.d.ts.map +1 -1
- package/dist/translators/index.js +1 -1
- package/dist/translators/index.js.map +1 -1
- package/dist/translators/projection-differ.d.ts +8 -0
- package/dist/translators/projection-differ.d.ts.map +1 -1
- package/dist/translators/projection-differ.js +19 -9
- package/dist/translators/projection-differ.js.map +1 -1
- package/dist/translators/tool-helpers.d.ts +28 -0
- package/dist/translators/tool-helpers.d.ts.map +1 -1
- package/dist/translators/tool-helpers.js +104 -0
- package/dist/translators/tool-helpers.js.map +1 -1
- package/dist/translators/types.d.ts +38 -1
- package/dist/translators/types.d.ts.map +1 -1
- package/dist/translators/types.js.map +1 -1
- package/dist/utils.d.ts +36 -7
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +69 -5
- package/dist/utils.js.map +1 -1
- package/docs/ARCHITECTURE.md +130 -40
- package/docs/BACKLOG.md +59 -0
- package/docs/DEVELOPMENT.md +31 -0
- package/docs/PROTOCOL.md +328 -23
- package/docs/REMOTE-CLIENTS.md +260 -0
- package/docs/REPLAY-GUIDE.md +131 -0
- package/docs/TROUBLESHOOTING.md +131 -14
- package/docs/adr/0001-bridge-lifetime-follows-primary-client.md +14 -0
- package/docs/adr/0002-stateless-hub-over-per-bridge-acp-endpoints.md +23 -0
- package/docs/adr/0003-tail-replay-meta-and-cursor-pagination.md +40 -0
- package/docs/agents/domain.md +51 -0
- package/docs/agents/issue-tracker.md +22 -0
- package/docs/agents/triage-labels.md +15 -0
- package/docs/proposals/0001-tail-session-replay.md +136 -0
- package/docs/proposals/0002-plan-quota-usage.md +81 -0
- package/package.json +10 -6
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quota response parsing — turns the raw GLM `limits[]` array into a flat list
|
|
3
|
+
* of normalised {@link QuotaItem}s.
|
|
4
|
+
*
|
|
5
|
+
* Design goals (vs. the reference project):
|
|
6
|
+
* - **No hardcoded type whitelist.** Unknown `type` values still render,
|
|
7
|
+
* labelled by their raw type string, so future windows surface
|
|
8
|
+
* automatically.
|
|
9
|
+
* - **Percentage fallback chain** that tolerates inconsistent field sets
|
|
10
|
+
* across limit kinds (some carry absolute counters, some only a legacy
|
|
11
|
+
* `percentage`).
|
|
12
|
+
* - **Labels derived from `type` + `number`**, without magic-number coupling.
|
|
13
|
+
*/
|
|
14
|
+
/** Coerce an unknown value to a finite number, or `null`. */
|
|
15
|
+
function asFiniteNumber(value) {
|
|
16
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
17
|
+
return Number.isFinite(n) ? n : null;
|
|
18
|
+
}
|
|
19
|
+
/** Clamp a percentage to the valid [0, 100] range, or `null` if not finite. */
|
|
20
|
+
function clampPercent(value) {
|
|
21
|
+
const n = asFiniteNumber(value);
|
|
22
|
+
if (n === null)
|
|
23
|
+
return null;
|
|
24
|
+
return Math.max(0, Math.min(100, Math.round(n)));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Compute `{ leftPercent, usedPercent }` from a single raw limit, tolerating
|
|
28
|
+
* several field combinations (preferred → fallback):
|
|
29
|
+
* 1. `remaining` + `currentValue` (absolute counters) — most precise;
|
|
30
|
+
* 2. `currentValue` / `usage` — used over total;
|
|
31
|
+
* 3. `percentage` (legacy, treated as *used* percent) — last resort.
|
|
32
|
+
* Returns `null` when no percentage can be derived.
|
|
33
|
+
*/
|
|
34
|
+
function computePercentages(limit) {
|
|
35
|
+
const usage = asFiniteNumber(limit.usage);
|
|
36
|
+
const remaining = asFiniteNumber(limit.remaining);
|
|
37
|
+
const currentValue = asFiniteNumber(limit.currentValue);
|
|
38
|
+
// Path 1: absolute remaining/used counters → derive total from parts.
|
|
39
|
+
const totalFromParts = remaining !== null && currentValue !== null ? remaining + currentValue : null;
|
|
40
|
+
const total = totalFromParts !== null && totalFromParts > 0 ? totalFromParts : usage;
|
|
41
|
+
if (total !== null && total > 0) {
|
|
42
|
+
if (remaining !== null && remaining >= 0 && remaining <= total) {
|
|
43
|
+
const leftPercent = clampPercent((remaining / total) * 100);
|
|
44
|
+
if (leftPercent !== null) {
|
|
45
|
+
return { leftPercent, usedPercent: 100 - leftPercent };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (currentValue !== null && currentValue >= 0 && currentValue <= total) {
|
|
49
|
+
const usedPercent = clampPercent((currentValue / total) * 100);
|
|
50
|
+
if (usedPercent !== null) {
|
|
51
|
+
return { leftPercent: 100 - usedPercent, usedPercent };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Path 3: legacy `percentage` (treated as used percent — GLM convention).
|
|
56
|
+
const usedPercent = clampPercent(limit.percentage);
|
|
57
|
+
if (usedPercent === null)
|
|
58
|
+
return null;
|
|
59
|
+
return { leftPercent: 100 - usedPercent, usedPercent };
|
|
60
|
+
}
|
|
61
|
+
/** Map a `type` to a coarse category used for label/key derivation. */
|
|
62
|
+
function categoryOf(type) {
|
|
63
|
+
switch (type) {
|
|
64
|
+
case "TOKENS_LIMIT":
|
|
65
|
+
return "token";
|
|
66
|
+
case "MCP_LIMIT":
|
|
67
|
+
case "TIME_LIMIT":
|
|
68
|
+
return "mcp";
|
|
69
|
+
default:
|
|
70
|
+
return "other";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Derive a stable key and human-readable label for a limit.
|
|
75
|
+
*
|
|
76
|
+
* Known windows get friendly labels; anything else falls back to the raw
|
|
77
|
+
* `type` (or `"Quota"`) so it still renders.
|
|
78
|
+
*/
|
|
79
|
+
function deriveLabel(limit, category) {
|
|
80
|
+
const number = limit.number;
|
|
81
|
+
if (category === "token") {
|
|
82
|
+
// GLM convention: number===5 → 5-hour window; number===7 → weekly.
|
|
83
|
+
if (number === 5)
|
|
84
|
+
return { key: "token_5h", label: "5h" };
|
|
85
|
+
if (number === 7)
|
|
86
|
+
return { key: "token_week", label: "Week" };
|
|
87
|
+
if (number !== undefined)
|
|
88
|
+
return { key: `token_${number}`, label: `${number}` };
|
|
89
|
+
return { key: "token", label: "Token" };
|
|
90
|
+
}
|
|
91
|
+
if (category === "mcp") {
|
|
92
|
+
return { key: "mcp", label: "MCP" };
|
|
93
|
+
}
|
|
94
|
+
// Unknown type — surface it verbatim rather than hide it.
|
|
95
|
+
const fallback = limit.type || "Quota";
|
|
96
|
+
return { key: fallback.toLowerCase(), label: fallback };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Parse one raw limit into a {@link QuotaItem}, or `null` if no percentage
|
|
100
|
+
* can be computed (the limit is then dropped from display).
|
|
101
|
+
*/
|
|
102
|
+
export function parseLimit(limit) {
|
|
103
|
+
const percent = computePercentages(limit);
|
|
104
|
+
if (!percent)
|
|
105
|
+
return null;
|
|
106
|
+
const category = categoryOf(limit.type);
|
|
107
|
+
const { key, label } = deriveLabel(limit, category);
|
|
108
|
+
const item = {
|
|
109
|
+
key,
|
|
110
|
+
label,
|
|
111
|
+
usedPercent: percent.usedPercent,
|
|
112
|
+
leftPercent: percent.leftPercent,
|
|
113
|
+
};
|
|
114
|
+
// Absolute counters — only some limit kinds carry them (MCP/TIME_LIMIT do,
|
|
115
|
+
// legacy TOKENS_LIMIT often does not). Surface them when present so the
|
|
116
|
+
// formatter can show "(used / total)".
|
|
117
|
+
const usedCount = asFiniteNumber(limit.currentValue);
|
|
118
|
+
if (usedCount !== null)
|
|
119
|
+
item.usedCount = usedCount;
|
|
120
|
+
const totalCount = asFiniteNumber(limit.usage);
|
|
121
|
+
if (totalCount !== null)
|
|
122
|
+
item.totalCount = totalCount;
|
|
123
|
+
const nextResetTime = asFiniteNumber(limit.nextResetTime);
|
|
124
|
+
if (nextResetTime !== null)
|
|
125
|
+
item.nextResetTime = nextResetTime;
|
|
126
|
+
if (Array.isArray(limit.usageDetails) && limit.usageDetails.length > 0) {
|
|
127
|
+
item.detail = limit.usageDetails;
|
|
128
|
+
}
|
|
129
|
+
return item;
|
|
130
|
+
}
|
|
131
|
+
/** Auth-failure detection from the business-layer `msg` (zh/en keywords). */
|
|
132
|
+
function isAuthFailureMessage(msg) {
|
|
133
|
+
return typeof msg === "string" && /authorization|auth|token|鉴权|授权|未登录/i.test(msg);
|
|
134
|
+
}
|
|
135
|
+
/** Rate-limit detection from the business-layer `msg` (zh/en keywords). */
|
|
136
|
+
function isRateLimitedMessage(msg) {
|
|
137
|
+
return (typeof msg === "string" &&
|
|
138
|
+
/rate\s*limit|too many requests|too frequent|frequency|限流|频率|过于频繁|稍后再试/i.test(msg));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Parse the full API response (already classified by the fetch layer as a
|
|
142
|
+
* `response` with a status + body) into a {@link QuotaResult}.
|
|
143
|
+
*
|
|
144
|
+
* State machine: 429/rate-limit text → `rate_limited`; auth codes/text →
|
|
145
|
+
* `auth_error`; success with zero parseable items → `unavailable`; otherwise
|
|
146
|
+
* the items are returned in original order.
|
|
147
|
+
*/
|
|
148
|
+
export function parseQuotaEnvelope(envelope) {
|
|
149
|
+
// HTTP-level rate limit.
|
|
150
|
+
if (envelope.status === 429 || isRateLimitedMessage(envelope.text)) {
|
|
151
|
+
return { kind: "rate_limited" };
|
|
152
|
+
}
|
|
153
|
+
const payload = envelope.json;
|
|
154
|
+
if (!payload || typeof payload !== "object") {
|
|
155
|
+
return { kind: "unavailable" };
|
|
156
|
+
}
|
|
157
|
+
if (payload.success !== true) {
|
|
158
|
+
if (payload.code === 1001 || payload.code === 401 || isAuthFailureMessage(payload.msg)) {
|
|
159
|
+
return { kind: "auth_error" };
|
|
160
|
+
}
|
|
161
|
+
if (isRateLimitedMessage(payload.msg)) {
|
|
162
|
+
return { kind: "rate_limited" };
|
|
163
|
+
}
|
|
164
|
+
return { kind: "unavailable" };
|
|
165
|
+
}
|
|
166
|
+
const limits = Array.isArray(payload.data?.limits) ? payload.data.limits : [];
|
|
167
|
+
const items = limits
|
|
168
|
+
.map(parseLimit)
|
|
169
|
+
.filter((x) => x !== null)
|
|
170
|
+
.sort(byDisplayOrder);
|
|
171
|
+
if (items.length === 0) {
|
|
172
|
+
return { kind: "unavailable" };
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
kind: "success",
|
|
176
|
+
level: typeof payload.data?.level === "string" ? payload.data.level : "",
|
|
177
|
+
items,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Display ordering: token windows first (5h before Week before other token
|
|
182
|
+
* windows), then MCP, then anything else — by stable key. Keeps the card
|
|
183
|
+
* layout predictable regardless of the API's array order.
|
|
184
|
+
*/
|
|
185
|
+
function byDisplayOrder(a, b) {
|
|
186
|
+
return rankOf(a.key) - rankOf(b.key) || a.key.localeCompare(b.key);
|
|
187
|
+
}
|
|
188
|
+
/** Stable rank for a quota key — lower renders first. */
|
|
189
|
+
function rankOf(key) {
|
|
190
|
+
if (key === "token_5h")
|
|
191
|
+
return 0;
|
|
192
|
+
if (key === "token_week")
|
|
193
|
+
return 1;
|
|
194
|
+
if (key.startsWith("token_"))
|
|
195
|
+
return 2; // other token windows
|
|
196
|
+
if (key === "mcp")
|
|
197
|
+
return 3;
|
|
198
|
+
return 4; // unknown types last
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/quota/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,6DAA6D;AAC7D,SAAS,cAAc,CAAC,KAAc;IACpC,MAAM,CAAC,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACvC,CAAC;AAED,+EAA+E;AAC/E,SAAS,YAAY,CAAC,KAAc;IAClC,MAAM,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,kBAAkB,CAAC,KAAe;IAIzC,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,cAAc,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAExD,sEAAsE;IACtE,MAAM,cAAc,GAClB,SAAS,KAAK,IAAI,IAAI,YAAY,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC;IAChF,MAAM,KAAK,GAAG,cAAc,KAAK,IAAI,IAAI,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC;IAErF,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAChC,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,IAAI,CAAC,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;YAC/D,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,SAAS,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;YAC5D,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;gBACzB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,GAAG,WAAW,EAAE,CAAC;YACzD,CAAC;QACH,CAAC;QACD,IAAI,YAAY,KAAK,IAAI,IAAI,YAAY,IAAI,CAAC,IAAI,YAAY,IAAI,KAAK,EAAE,CAAC;YACxE,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,YAAY,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;YAC/D,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;gBACzB,OAAO,EAAE,WAAW,EAAE,GAAG,GAAG,WAAW,EAAE,WAAW,EAAE,CAAC;YACzD,CAAC;QACH,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACnD,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,EAAE,WAAW,EAAE,GAAG,GAAG,WAAW,EAAE,WAAW,EAAE,CAAC;AACzD,CAAC;AAED,uEAAuE;AACvE,SAAS,UAAU,CAAC,IAAY;IAC9B,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,cAAc;YACjB,OAAO,OAAO,CAAC;QACjB,KAAK,WAAW,CAAC;QACjB,KAAK,YAAY;YACf,OAAO,KAAK,CAAC;QACf;YACE,OAAO,OAAO,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,WAAW,CAClB,KAAe,EACf,QAAmC;IAKnC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5B,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,mEAAmE;QACnE,IAAI,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC1D,IAAI,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAC9D,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,EAAE,GAAG,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,EAAE,CAAC;QAChF,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC1C,CAAC;IACD,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;QACvB,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACtC,CAAC;IACD,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC;IACvC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAAe;IACxC,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAEpD,MAAM,IAAI,GAAc;QACtB,GAAG;QACH,KAAK;QACL,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,WAAW,EAAE,OAAO,CAAC,WAAW;KACjC,CAAC;IAEF,2EAA2E;IAC3E,wEAAwE;IACxE,uCAAuC;IACvC,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACrD,IAAI,SAAS,KAAK,IAAI;QAAE,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IACnD,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,UAAU,KAAK,IAAI;QAAE,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAEtD,MAAM,aAAa,GAAG,cAAc,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC1D,IAAI,aAAa,KAAK,IAAI;QAAE,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IAE/D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC;IACnC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6EAA6E;AAC7E,SAAS,oBAAoB,CAAC,GAAY;IACxC,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,qCAAqC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpF,CAAC;AAED,2EAA2E;AAC3E,SAAS,oBAAoB,CAAC,GAAY;IACxC,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,wEAAwE,CAAC,IAAI,CAAC,GAAG,CAAC,CACnF,CAAC;AACJ,CAAC;AAUD;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAIlC;IACC,yBAAyB;IACzB,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAClC,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,IAA4B,CAAC;IACtD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IACjC,CAAC;IAED,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC7B,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,KAAK,GAAG,IAAI,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACvF,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;QAChC,CAAC;QACD,IAAI,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;QAClC,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IACjC,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAK,CAAC,MAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAChF,MAAM,KAAK,GAAG,MAAM;SACjB,GAAG,CAAC,UAAU,CAAC;SACf,MAAM,CAAC,CAAC,CAAC,EAAkB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;SACzC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IACjC,CAAC;IAED,OAAO;QACL,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,OAAO,OAAO,CAAC,IAAI,EAAE,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QACxE,KAAK;KACN,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,CAAY,EAAE,CAAY;IAChD,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AACrE,CAAC;AAED,yDAAyD;AACzD,SAAS,MAAM,CAAC,GAAW;IACzB,IAAI,GAAG,KAAK,UAAU;QAAE,OAAO,CAAC,CAAC;IACjC,IAAI,GAAG,KAAK,YAAY;QAAE,OAAO,CAAC,CAAC;IACnC,IAAI,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC,sBAAsB;IAC9D,IAAI,GAAG,KAAK,KAAK;QAAE,OAAO,CAAC,CAAC;IAC5B,OAAO,CAAC,CAAC,CAAC,qBAAqB;AACjC,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the GLM Coding Plan quota feature.
|
|
3
|
+
*
|
|
4
|
+
* The `/quota` slash command queries `bigmodel.cn` / `api.z.ai` for the
|
|
5
|
+
* current account's usage limits and renders them as a multi-line card.
|
|
6
|
+
* These types model the raw API response and the normalised shape consumed
|
|
7
|
+
* by the formatter.
|
|
8
|
+
*/
|
|
9
|
+
/** A single raw limit entry from the GLM quota API (`data.limits[]`). */
|
|
10
|
+
export interface RawLimit {
|
|
11
|
+
/** Limit family — "TOKENS_LIMIT" | "TIME_LIMIT" | "MCP_LIMIT" | <unknown>. */
|
|
12
|
+
type: string;
|
|
13
|
+
/** Window-count sub-identifier; meaning varies by backend (unused for classification). */
|
|
14
|
+
unit?: number;
|
|
15
|
+
/** Window span; GLM uses 5 for the 5-hour window, 7 for the weekly window. */
|
|
16
|
+
number?: number;
|
|
17
|
+
/** Total allowance for the window (absolute counter). */
|
|
18
|
+
usage?: number;
|
|
19
|
+
/** Absolute remaining counter (preferred for percentage). */
|
|
20
|
+
remaining?: number;
|
|
21
|
+
/** Absolute used counter (preferred for percentage alongside `usage`). */
|
|
22
|
+
currentValue?: number;
|
|
23
|
+
/** Legacy percentage field — historically represents *used* percent. */
|
|
24
|
+
percentage?: number;
|
|
25
|
+
/** Reset timestamp in epoch milliseconds. */
|
|
26
|
+
nextResetTime?: number;
|
|
27
|
+
/** Per-model usage breakdown (present on MCP/TIME limits). */
|
|
28
|
+
usageDetails?: {
|
|
29
|
+
modelCode: string;
|
|
30
|
+
usage: number;
|
|
31
|
+
}[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Normalised quota item — one rendered line.
|
|
35
|
+
*
|
|
36
|
+
* Every raw limit that yields a valid percentage becomes a `QuotaItem`;
|
|
37
|
+
* limits with no computable percentage are silently dropped.
|
|
38
|
+
*/
|
|
39
|
+
export interface QuotaItem {
|
|
40
|
+
/** Stable key derived from type+number, e.g. "token_5h" / "mcp". */
|
|
41
|
+
key: string;
|
|
42
|
+
/** Human-readable label for the line, e.g. "5h" / "MCP" / "Week". */
|
|
43
|
+
label: string;
|
|
44
|
+
/** Used percentage, clamped to [0, 100]. */
|
|
45
|
+
usedPercent: number;
|
|
46
|
+
/** Remaining percentage = 100 - usedPercent. */
|
|
47
|
+
leftPercent: number;
|
|
48
|
+
/** Absolute used count (only some limits carry this, e.g. MCP/TIME). */
|
|
49
|
+
usedCount?: number;
|
|
50
|
+
/** Absolute total allowance (only some limits carry this). */
|
|
51
|
+
totalCount?: number;
|
|
52
|
+
/** Reset timestamp in epoch milliseconds, if present. */
|
|
53
|
+
nextResetTime?: number;
|
|
54
|
+
/** Per-model usage breakdown, if present (rendered as indented sub-lines). */
|
|
55
|
+
detail?: {
|
|
56
|
+
modelCode: string;
|
|
57
|
+
usage: number;
|
|
58
|
+
}[];
|
|
59
|
+
}
|
|
60
|
+
/** Top-level parsed result — a 4-state sum type. */
|
|
61
|
+
export type QuotaResult = {
|
|
62
|
+
kind: "success";
|
|
63
|
+
level: string;
|
|
64
|
+
items: QuotaItem[];
|
|
65
|
+
} | {
|
|
66
|
+
kind: "auth_error";
|
|
67
|
+
} | {
|
|
68
|
+
kind: "rate_limited";
|
|
69
|
+
} | {
|
|
70
|
+
kind: "unavailable";
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/quota/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,yEAAyE;AACzE,MAAM,WAAW,QAAQ;IACvB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,YAAY,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvD;AAED;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8EAA8E;IAC9E,MAAM,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjD;AAED,oDAAoD;AACpD,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the GLM Coding Plan quota feature.
|
|
3
|
+
*
|
|
4
|
+
* The `/quota` slash command queries `bigmodel.cn` / `api.z.ai` for the
|
|
5
|
+
* current account's usage limits and renders them as a multi-line card.
|
|
6
|
+
* These types model the raw API response and the normalised shape consumed
|
|
7
|
+
* by the formatter.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/quota/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-client broadcast layer for remote access.
|
|
3
|
+
*
|
|
4
|
+
* The bridge historically served ONE ACP client (the editor over stdio). With
|
|
5
|
+
* remote access enabled, additional clients attach over WebSocket; every
|
|
6
|
+
* agent-originated message must reach all of them. This module owns the client
|
|
7
|
+
* registry and a stable proxy that quacks like an `AgentContext`:
|
|
8
|
+
*
|
|
9
|
+
* - `notify` fans out to every client; a single dead/slow client is warned
|
|
10
|
+
* about and never fails the others.
|
|
11
|
+
* - `request` (permission / elicitation) is sent to every client and the FIRST
|
|
12
|
+
* response wins. Losers are aborted via `cancellationSignal`, which makes
|
|
13
|
+
* the SDK emit `$/cancel_request` so the losing editor dismisses its dialog
|
|
14
|
+
* (verified against Zed's ACP client).
|
|
15
|
+
*
|
|
16
|
+
* Loser promises settle late (the peer answers the cancellation eventually) —
|
|
17
|
+
* every raced promise carries a no-op catch so late settlements can't surface
|
|
18
|
+
* as unhandledRejection (Node ≥15 crashes on those by default).
|
|
19
|
+
*/
|
|
20
|
+
import type * as acp from "@agentclientprotocol/sdk";
|
|
21
|
+
/** The AgentContext surface the bridge actually calls. */
|
|
22
|
+
export interface ClientLike {
|
|
23
|
+
notify(method: string, params?: unknown): Promise<void>;
|
|
24
|
+
request(method: string, params?: unknown, options?: acp.SendRequestOptions): Promise<unknown>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Track every connection opened on the app (stdio editor + remote WebSocket)
|
|
28
|
+
* in the registry, removing each on close. Wired once by the entry point
|
|
29
|
+
* BEFORE `connect()` so the stdio connection is captured too.
|
|
30
|
+
*/
|
|
31
|
+
export declare function trackConnections(app: acp.AgentApp, clients: ClientRegistry): void;
|
|
32
|
+
/**
|
|
33
|
+
* Registry of connected ACP clients (stdio editor + remote WebSocket clients).
|
|
34
|
+
* Membership is managed by the entry point via the SDK's per-connection
|
|
35
|
+
* lifecycle; the broadcast proxy reads membership live on every call.
|
|
36
|
+
*/
|
|
37
|
+
export declare class ClientRegistry {
|
|
38
|
+
private readonly clients;
|
|
39
|
+
private proxy;
|
|
40
|
+
add(cx: ClientLike): void;
|
|
41
|
+
remove(cx: ClientLike): void;
|
|
42
|
+
get size(): number;
|
|
43
|
+
/** Stable broadcast proxy satisfying the `AgentContext` call surface. */
|
|
44
|
+
broadcast(): acp.AgentContext;
|
|
45
|
+
snapshot(): ClientLike[];
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=broadcast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../../src/remote/broadcast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,KAAK,GAAG,MAAM,0BAA0B,CAAC;AAIrD,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAKjF;AAQD;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,KAAK,CAAiC;IAE9C,GAAG,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI;IAIzB,MAAM,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI;IAI5B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,yEAAyE;IACzE,SAAS,IAAI,GAAG,CAAC,YAAY;IAK7B,QAAQ,IAAI,UAAU,EAAE;CAGzB"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-client broadcast layer for remote access.
|
|
3
|
+
*
|
|
4
|
+
* The bridge historically served ONE ACP client (the editor over stdio). With
|
|
5
|
+
* remote access enabled, additional clients attach over WebSocket; every
|
|
6
|
+
* agent-originated message must reach all of them. This module owns the client
|
|
7
|
+
* registry and a stable proxy that quacks like an `AgentContext`:
|
|
8
|
+
*
|
|
9
|
+
* - `notify` fans out to every client; a single dead/slow client is warned
|
|
10
|
+
* about and never fails the others.
|
|
11
|
+
* - `request` (permission / elicitation) is sent to every client and the FIRST
|
|
12
|
+
* response wins. Losers are aborted via `cancellationSignal`, which makes
|
|
13
|
+
* the SDK emit `$/cancel_request` so the losing editor dismisses its dialog
|
|
14
|
+
* (verified against Zed's ACP client).
|
|
15
|
+
*
|
|
16
|
+
* Loser promises settle late (the peer answers the cancellation eventually) —
|
|
17
|
+
* every raced promise carries a no-op catch so late settlements can't surface
|
|
18
|
+
* as unhandledRejection (Node ≥15 crashes on those by default).
|
|
19
|
+
*/
|
|
20
|
+
import { warn } from "../utils.js";
|
|
21
|
+
/**
|
|
22
|
+
* Track every connection opened on the app (stdio editor + remote WebSocket)
|
|
23
|
+
* in the registry, removing each on close. Wired once by the entry point
|
|
24
|
+
* BEFORE `connect()` so the stdio connection is captured too.
|
|
25
|
+
*/
|
|
26
|
+
export function trackConnections(app, clients) {
|
|
27
|
+
app.onConnect((conn) => {
|
|
28
|
+
clients.add(conn.client);
|
|
29
|
+
void conn.closed.then(() => clients.remove(conn.client));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Registry of connected ACP clients (stdio editor + remote WebSocket clients).
|
|
34
|
+
* Membership is managed by the entry point via the SDK's per-connection
|
|
35
|
+
* lifecycle; the broadcast proxy reads membership live on every call.
|
|
36
|
+
*/
|
|
37
|
+
export class ClientRegistry {
|
|
38
|
+
clients = new Set();
|
|
39
|
+
proxy = null;
|
|
40
|
+
add(cx) {
|
|
41
|
+
this.clients.add(cx);
|
|
42
|
+
}
|
|
43
|
+
remove(cx) {
|
|
44
|
+
this.clients.delete(cx);
|
|
45
|
+
}
|
|
46
|
+
get size() {
|
|
47
|
+
return this.clients.size;
|
|
48
|
+
}
|
|
49
|
+
/** Stable broadcast proxy satisfying the `AgentContext` call surface. */
|
|
50
|
+
broadcast() {
|
|
51
|
+
if (!this.proxy)
|
|
52
|
+
this.proxy = createBroadcastProxy(this);
|
|
53
|
+
return this.proxy;
|
|
54
|
+
}
|
|
55
|
+
snapshot() {
|
|
56
|
+
return Array.from(this.clients);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Build the stable proxy once per registry (module factory: no `this` alias). */
|
|
60
|
+
function createBroadcastProxy(registry) {
|
|
61
|
+
const proxy = Object.create(null);
|
|
62
|
+
proxy.notify = (method, params) => notifyAll(registry, method, params);
|
|
63
|
+
proxy.request = (method, params, options) => requestAny(registry, method, params, options);
|
|
64
|
+
return proxy;
|
|
65
|
+
}
|
|
66
|
+
async function notifyAll(registry, method, params) {
|
|
67
|
+
const results = await Promise.allSettled(registry.snapshot().map((cx) => cx.notify(method, params)));
|
|
68
|
+
for (const r of results) {
|
|
69
|
+
if (r.status === "rejected") {
|
|
70
|
+
warn(`broadcast: notify ${method} failed on one client: ` +
|
|
71
|
+
`${r.reason instanceof Error ? r.reason.message : String(r.reason)}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function requestAny(registry, method, params, options) {
|
|
76
|
+
const clients = registry.snapshot();
|
|
77
|
+
if (clients.length === 0) {
|
|
78
|
+
throw new Error(`broadcast: no connected clients (${method})`);
|
|
79
|
+
}
|
|
80
|
+
const controllers = clients.map(() => new AbortController());
|
|
81
|
+
// Link a caller-provided signal: aborting it cancels EVERY inner request.
|
|
82
|
+
const outerSignal = options?.cancellationSignal;
|
|
83
|
+
const onOuterAbort = () => {
|
|
84
|
+
for (const c of controllers)
|
|
85
|
+
c.abort();
|
|
86
|
+
};
|
|
87
|
+
if (outerSignal) {
|
|
88
|
+
if (outerSignal.aborted)
|
|
89
|
+
onOuterAbort();
|
|
90
|
+
else
|
|
91
|
+
outerSignal.addEventListener("abort", onOuterAbort, { once: true });
|
|
92
|
+
}
|
|
93
|
+
const attempts = clients.map((cx, i) => {
|
|
94
|
+
const promise = cx.request(method, params, {
|
|
95
|
+
...options,
|
|
96
|
+
cancellationSignal: controllers[i].signal,
|
|
97
|
+
});
|
|
98
|
+
// Mark handled: losing promises settle AFTER Promise.any is done.
|
|
99
|
+
promise.catch(() => undefined);
|
|
100
|
+
return promise.then((value) => ({ value, index: i }));
|
|
101
|
+
});
|
|
102
|
+
try {
|
|
103
|
+
const winner = await Promise.any(attempts);
|
|
104
|
+
for (let i = 0; i < controllers.length; i++) {
|
|
105
|
+
if (i !== winner.index)
|
|
106
|
+
controllers[i].abort();
|
|
107
|
+
}
|
|
108
|
+
return winner.value;
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
// All clients failed — surface the first error like a single client would.
|
|
112
|
+
if (e instanceof AggregateError)
|
|
113
|
+
throw e.errors[0] ?? e;
|
|
114
|
+
throw e;
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
if (outerSignal)
|
|
118
|
+
outerSignal.removeEventListener("abort", onOuterAbort);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=broadcast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"broadcast.js","sourceRoot":"","sources":["../../src/remote/broadcast.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote access configuration from environment variables.
|
|
3
|
+
*
|
|
4
|
+
* Remote access is opt-in via ZCODE_ACP_REMOTE=1 and REQUIRES a token — the
|
|
5
|
+
* endpoint is expected to sit behind a public tunnel (Cloudflare Tunnel, frp),
|
|
6
|
+
* so "loopback-only" is never a safe assumption here. A missing token disables
|
|
7
|
+
* the feature with a warning instead of failing the bridge: the stdio link to
|
|
8
|
+
* the editor must keep working no matter what.
|
|
9
|
+
*
|
|
10
|
+
* Variables:
|
|
11
|
+
* ZCODE_ACP_REMOTE=1 enable the remote endpoint (gate)
|
|
12
|
+
* ZCODE_ACP_REMOTE_TOKEN=<s> auth token (mandatory when enabled)
|
|
13
|
+
* ZCODE_ACP_HUB_PORT=8377 hub's fixed port (the one a tunnel maps)
|
|
14
|
+
* ZCODE_ACP_HUB_HOST=127.0.0.1 hub bind address (e.g. 0.0.0.0 for a
|
|
15
|
+
* containerized tunnel agent)
|
|
16
|
+
* ZCODE_ACP_REMOTE_PORT=8378 bridge endpoint start port (auto-increment
|
|
17
|
+
* when taken; loopback only)
|
|
18
|
+
*/
|
|
19
|
+
export interface RemoteConfig {
|
|
20
|
+
token: string;
|
|
21
|
+
hubPort: number;
|
|
22
|
+
hubHost: string;
|
|
23
|
+
bridgePort: number;
|
|
24
|
+
}
|
|
25
|
+
export declare const DEFAULT_HUB_PORT = 8377;
|
|
26
|
+
export declare const DEFAULT_BRIDGE_PORT = 8378;
|
|
27
|
+
export declare const DEFAULT_HUB_HOST = "127.0.0.1";
|
|
28
|
+
/** Parse remote config; null = disabled (or misconfigured → warned). */
|
|
29
|
+
export declare function parseRemoteConfig(env?: NodeJS.ProcessEnv): RemoteConfig | null;
|
|
30
|
+
/** Parse hub-side config for the standalone `zcode-acp-hub` bin. */
|
|
31
|
+
export declare function parseHubConfig(env?: NodeJS.ProcessEnv): RemoteConfig | null;
|
|
32
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/remote/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAY5C,wEAAwE;AACxE,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,YAAY,GAAG,IAAI,CAiB3F;AAED,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,YAAY,GAAG,IAAI,CAYxF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote access configuration from environment variables.
|
|
3
|
+
*
|
|
4
|
+
* Remote access is opt-in via ZCODE_ACP_REMOTE=1 and REQUIRES a token — the
|
|
5
|
+
* endpoint is expected to sit behind a public tunnel (Cloudflare Tunnel, frp),
|
|
6
|
+
* so "loopback-only" is never a safe assumption here. A missing token disables
|
|
7
|
+
* the feature with a warning instead of failing the bridge: the stdio link to
|
|
8
|
+
* the editor must keep working no matter what.
|
|
9
|
+
*
|
|
10
|
+
* Variables:
|
|
11
|
+
* ZCODE_ACP_REMOTE=1 enable the remote endpoint (gate)
|
|
12
|
+
* ZCODE_ACP_REMOTE_TOKEN=<s> auth token (mandatory when enabled)
|
|
13
|
+
* ZCODE_ACP_HUB_PORT=8377 hub's fixed port (the one a tunnel maps)
|
|
14
|
+
* ZCODE_ACP_HUB_HOST=127.0.0.1 hub bind address (e.g. 0.0.0.0 for a
|
|
15
|
+
* containerized tunnel agent)
|
|
16
|
+
* ZCODE_ACP_REMOTE_PORT=8378 bridge endpoint start port (auto-increment
|
|
17
|
+
* when taken; loopback only)
|
|
18
|
+
*/
|
|
19
|
+
import { warn } from "../utils.js";
|
|
20
|
+
export const DEFAULT_HUB_PORT = 8377;
|
|
21
|
+
export const DEFAULT_BRIDGE_PORT = 8378;
|
|
22
|
+
export const DEFAULT_HUB_HOST = "127.0.0.1";
|
|
23
|
+
function parsePort(raw, fallback, envName) {
|
|
24
|
+
if (!raw)
|
|
25
|
+
return fallback;
|
|
26
|
+
const port = Number.parseInt(raw, 10);
|
|
27
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
28
|
+
warn(`remote: invalid ${envName}="${raw}", falling back to ${fallback}`);
|
|
29
|
+
return fallback;
|
|
30
|
+
}
|
|
31
|
+
return port;
|
|
32
|
+
}
|
|
33
|
+
/** Parse remote config; null = disabled (or misconfigured → warned). */
|
|
34
|
+
export function parseRemoteConfig(env = process.env) {
|
|
35
|
+
const gate = (env.ZCODE_ACP_REMOTE ?? "").trim().toLowerCase();
|
|
36
|
+
if (!["1", "true", "yes", "on"].includes(gate))
|
|
37
|
+
return null;
|
|
38
|
+
const token = (env.ZCODE_ACP_REMOTE_TOKEN ?? "").trim();
|
|
39
|
+
if (!token) {
|
|
40
|
+
warn("remote: ZCODE_ACP_REMOTE is enabled but ZCODE_ACP_REMOTE_TOKEN is missing — " +
|
|
41
|
+
"remote access disabled (stdio unaffected)");
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
token,
|
|
46
|
+
hubPort: parsePort(env.ZCODE_ACP_HUB_PORT, DEFAULT_HUB_PORT, "ZCODE_ACP_HUB_PORT"),
|
|
47
|
+
hubHost: (env.ZCODE_ACP_HUB_HOST ?? "").trim() || DEFAULT_HUB_HOST,
|
|
48
|
+
bridgePort: parsePort(env.ZCODE_ACP_REMOTE_PORT, DEFAULT_BRIDGE_PORT, "ZCODE_ACP_REMOTE_PORT"),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Parse hub-side config for the standalone `zcode-acp-hub` bin. */
|
|
52
|
+
export function parseHubConfig(env = process.env) {
|
|
53
|
+
const token = (env.ZCODE_ACP_REMOTE_TOKEN ?? "").trim();
|
|
54
|
+
if (!token) {
|
|
55
|
+
warn("hub: ZCODE_ACP_REMOTE_TOKEN is required — refusing to start without auth");
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
token,
|
|
60
|
+
hubPort: parsePort(env.ZCODE_ACP_HUB_PORT, DEFAULT_HUB_PORT, "ZCODE_ACP_HUB_PORT"),
|
|
61
|
+
hubHost: (env.ZCODE_ACP_HUB_HOST ?? "").trim() || DEFAULT_HUB_HOST,
|
|
62
|
+
bridgePort: parsePort(env.ZCODE_ACP_REMOTE_PORT, DEFAULT_BRIDGE_PORT, "ZCODE_ACP_REMOTE_PORT"),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/remote/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AASnC,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AACrC,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AACxC,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAE5C,SAAS,SAAS,CAAC,GAAuB,EAAE,QAAgB,EAAE,OAAe;IAC3E,IAAI,CAAC,GAAG;QAAE,OAAO,QAAQ,CAAC;IAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACxD,IAAI,CAAC,mBAAmB,OAAO,KAAK,GAAG,sBAAsB,QAAQ,EAAE,CAAC,CAAC;QACzE,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,iBAAiB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACpE,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/D,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5D,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,CACF,8EAA8E;YAC5E,2CAA2C,CAC9C,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO;QACL,KAAK;QACL,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,kBAAkB,EAAE,gBAAgB,EAAE,oBAAoB,CAAC;QAClF,OAAO,EAAE,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,gBAAgB;QAClE,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,qBAAqB,EAAE,mBAAmB,EAAE,uBAAuB,CAAC;KAC/F,CAAC;AACJ,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG;IACjE,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,CAAC,0EAA0E,CAAC,CAAC;QACjF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO;QACL,KAAK;QACL,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,kBAAkB,EAAE,gBAAgB,EAAE,oBAAoB,CAAC;QAClF,OAAO,EAAE,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,gBAAgB;QAClE,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,qBAAqB,EAAE,mBAAmB,EAAE,uBAAuB,CAAC;KAC/F,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopback ACP endpoint + hub registration for remote access.
|
|
3
|
+
*
|
|
4
|
+
* When ZCODE_ACP_REMOTE is enabled, the bridge serves the SAME AgentApp that
|
|
5
|
+
* handles the stdio editor connection on a loopback HTTP/WebSocket endpoint
|
|
6
|
+
* (SDK AcpServer transport). Each remote connection gets its own JSON-RPC id
|
|
7
|
+
* space; fan-out to all clients is handled by the broadcast registry, not
|
|
8
|
+
* here. This endpoint is intentionally NOT exposed to the network — the hub
|
|
9
|
+
* (`zcode-acp-hub`) is the single public entry and proxies into it.
|
|
10
|
+
*
|
|
11
|
+
* The bridge also registers itself with the hub (spawning one if none is
|
|
12
|
+
* listening) and re-registers every 10s as a heartbeat carrying fresh session
|
|
13
|
+
* summaries. Everything here is best-effort: any failure warns and disables
|
|
14
|
+
* the remote side without touching the stdio link.
|
|
15
|
+
*/
|
|
16
|
+
import type * as acp from "@agentclientprotocol/sdk";
|
|
17
|
+
import type { ZcodeAcpServer } from "../server.js";
|
|
18
|
+
import type { RemoteConfig } from "./config.js";
|
|
19
|
+
export interface RemoteEndpointHandle {
|
|
20
|
+
/** Actual loopback port the endpoint bound (may differ from config). */
|
|
21
|
+
port: number;
|
|
22
|
+
/** Stop the endpoint and unregister from the hub (best-effort). */
|
|
23
|
+
stop(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Start the loopback endpoint and hub registration. Never throws — failures
|
|
27
|
+
* warn and leave the bridge running stdio-only.
|
|
28
|
+
*/
|
|
29
|
+
export declare function startRemoteEndpoint(server: ZcodeAcpServer, app: acp.AgentApp, config: RemoteConfig): Promise<RemoteEndpointHandle | null>;
|
|
30
|
+
//# sourceMappingURL=endpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint.d.ts","sourceRoot":"","sources":["../../src/remote/endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,KAAK,GAAG,MAAM,0BAA0B,CAAC;AAQrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAShD,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AA8BD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,GAAG,CAAC,QAAQ,EACjB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAiKtC"}
|