@promptctl/cc-candybar 1.42.1 → 1.43.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/dist/index.mjs +72 -71
- package/package.json +5 -6
- package/src/check.ts +0 -478
- package/src/cli-flags.ts +0 -8
- package/src/click/wire.ts +0 -158
- package/src/config/action.ts +0 -329
- package/src/config/cli.ts +0 -71
- package/src/config/default-dsl-config.ts +0 -1645
- package/src/config/disclosure.ts +0 -170
- package/src/config/dsl-loader.ts +0 -339
- package/src/config/dsl-types.ts +0 -581
- package/src/config/edit-chrome.ts +0 -559
- package/src/config/help.ts +0 -151
- package/src/config/ident.ts +0 -22
- package/src/config/layout-ops.ts +0 -177
- package/src/config/loader/actions.ts +0 -972
- package/src/config/loader/cache.ts +0 -206
- package/src/config/loader/cross-ref.ts +0 -714
- package/src/config/loader/cycles.ts +0 -148
- package/src/config/loader/diagnostics.ts +0 -99
- package/src/config/loader/discovery.ts +0 -182
- package/src/config/loader/edit-mode.ts +0 -137
- package/src/config/loader/emit-schema.ts +0 -68
- package/src/config/loader/globals.ts +0 -269
- package/src/config/loader/helpers.ts +0 -48
- package/src/config/loader/layout.ts +0 -693
- package/src/config/loader/looks.ts +0 -96
- package/src/config/loader/menu-synth.ts +0 -435
- package/src/config/loader/merge.ts +0 -115
- package/src/config/loader/persist-target.ts +0 -67
- package/src/config/loader/presets.ts +0 -119
- package/src/config/loader/refs.ts +0 -100
- package/src/config/loader/reserved-namespace.ts +0 -38
- package/src/config/loader/segments.ts +0 -120
- package/src/config/loader/validate-core.ts +0 -737
- package/src/config/loader/variables.ts +0 -260
- package/src/config/menu-keys.ts +0 -139
- package/src/config/option-domain.ts +0 -164
- package/src/config/presets.ts +0 -326
- package/src/config/settings-menu.ts +0 -775
- package/src/daemon/acquire.ts +0 -684
- package/src/daemon/cache/git.ts +0 -649
- package/src/daemon/cache/render.ts +0 -623
- package/src/daemon/cache/session-usage-store.ts +0 -720
- package/src/daemon/cache/watchers.ts +0 -249
- package/src/daemon/client-debug.ts +0 -120
- package/src/daemon/client-stats.ts +0 -130
- package/src/daemon/client-transport.ts +0 -273
- package/src/daemon/client.ts +0 -78
- package/src/daemon/config-overrides-store.ts +0 -663
- package/src/daemon/debug-types.ts +0 -91
- package/src/daemon/debug.ts +0 -264
- package/src/daemon/fork-bomb-breaker.ts +0 -351
- package/src/daemon/limits.ts +0 -211
- package/src/daemon/log.ts +0 -81
- package/src/daemon/parent-watchdog.ts +0 -87
- package/src/daemon/paths.ts +0 -211
- package/src/daemon/process-fingerprint.ts +0 -146
- package/src/daemon/protocol.ts +0 -292
- package/src/daemon/render-payload.ts +0 -1256
- package/src/daemon/server.ts +0 -1330
- package/src/daemon/session-state-file.ts +0 -108
- package/src/daemon/session-state.ts +0 -237
- package/src/daemon/socket-lease.ts +0 -209
- package/src/daemon/socket-ownership.ts +0 -209
- package/src/daemon/stats.ts +0 -235
- package/src/daemon/verbs/config-validators.ts +0 -250
- package/src/daemon/verbs/index.ts +0 -706
- package/src/daemon/verbs/state-validators.ts +0 -249
- package/src/daemon/verbs/validator-registry.ts +0 -457
- package/src/demo/dsl.ts +0 -143
- package/src/demo/mock-data.ts +0 -67
- package/src/demo/statusline.json5 +0 -94
- package/src/dsl/node-registry.ts +0 -374
- package/src/dsl/render.ts +0 -803
- package/src/help-text.ts +0 -90
- package/src/index.ts +0 -210
- package/src/install/currency.ts +0 -197
- package/src/install/index.ts +0 -557
- package/src/proc/launch.ts +0 -459
- package/src/proc/stats-handle.ts +0 -13
- package/src/render/action.ts +0 -883
- package/src/render/active-segment.ts +0 -78
- package/src/render/diagnostic-style.ts +0 -23
- package/src/render/diagnostic-text.ts +0 -77
- package/src/render/error-glyph.ts +0 -53
- package/src/render/menu.ts +0 -257
- package/src/render/outcome-plan.ts +0 -45
- package/src/render/picker.ts +0 -372
- package/src/render/segment-color.ts +0 -74
- package/src/render/split-lines.ts +0 -51
- package/src/render/strip.ts +0 -228
- package/src/segments/cache.ts +0 -131
- package/src/segments/context.ts +0 -190
- package/src/segments/git.ts +0 -1084
- package/src/segments/metrics.ts +0 -187
- package/src/segments/pricing.ts +0 -452
- package/src/segments/session.ts +0 -23
- package/src/segments/tmux.ts +0 -74
- package/src/template-engine/cells.ts +0 -90
- package/src/template-engine/colors.ts +0 -124
- package/src/template-engine/engine.ts +0 -108
- package/src/template-engine/funcs.ts +0 -232
- package/src/template-engine/index.ts +0 -11
- package/src/template-engine/layout.ts +0 -133
- package/src/template-engine/scope.ts +0 -62
- package/src/template-engine/sparkline.ts +0 -79
- package/src/themes/index.ts +0 -20
- package/src/themes/palette-resolvers.ts +0 -84
- package/src/themes/policy.ts +0 -393
- package/src/utils/cache.ts +0 -206
- package/src/utils/claude.ts +0 -683
- package/src/utils/color-support.ts +0 -118
- package/src/utils/formatters.ts +0 -99
- package/src/utils/logger.ts +0 -5
- package/src/utils/outcome.ts +0 -33
- package/src/utils/schema-validator.ts +0 -126
- package/src/utils/single-flight.ts +0 -57
- package/src/utils/terminal-width.ts +0 -51
- package/src/utils/terminal.ts +0 -11
- package/src/utils/transcript-fs.ts +0 -279
- package/src/var-system/index.ts +0 -24
- package/src/var-system/sources.ts +0 -1047
- package/src/var-system/store.ts +0 -223
- package/src/var-system/types.ts +0 -57
- package/src/version.ts +0 -17
package/src/daemon/protocol.ts
DELETED
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
import type { Socket } from "node:net";
|
|
2
|
-
import type { ClaudeHookData } from "../utils/claude";
|
|
3
|
-
import type { StatsSnapshot } from "./stats";
|
|
4
|
-
import type { DebugSnapshot, DebugWhat } from "./debug-types";
|
|
5
|
-
|
|
6
|
-
// [LAW:types-are-the-program] PROTOCOL_VERSION encodes one thing:
|
|
7
|
-
// "old-client × new-daemon (or vice versa) cannot communicate." It moves on
|
|
8
|
-
// BREAKING changes only — never on additive ones. The two cases are
|
|
9
|
-
// genuinely different theorems and the version field carries the stronger:
|
|
10
|
-
// incompatibility, not growth.
|
|
11
|
-
//
|
|
12
|
-
// **Additive** (no bump):
|
|
13
|
-
// - Adding a new request `kind`. Old daemons reject the unknown kind via
|
|
14
|
-
// the existing BAD_REQUEST fallthrough; old clients never send it.
|
|
15
|
-
// - Adding a new optional field that older parsers ignore safely.
|
|
16
|
-
// - Adding a new response variant produced only in response to a new kind.
|
|
17
|
-
//
|
|
18
|
-
// **Breaking** (bump):
|
|
19
|
-
// - Changing the semantics or required shape of an existing kind.
|
|
20
|
-
// - Removing a kind or field old clients depend on.
|
|
21
|
-
// - Renaming a wire field.
|
|
22
|
-
//
|
|
23
|
-
// The 452-corpse precedent (kz8.5) makes this discipline load-bearing: every
|
|
24
|
-
// bump forces every running statusbar through VERSION_MISMATCH until its
|
|
25
|
-
// session restarts, because the spiral-breaker contract refuses to kick on a
|
|
26
|
-
// permanent error. A bump for an additive change taxes every user with
|
|
27
|
-
// blank-statusbar minutes for a feature their session doesn't even use.
|
|
28
|
-
// Don't bump for growth.
|
|
29
|
-
export const PROTOCOL_VERSION = 3;
|
|
30
|
-
|
|
31
|
-
export interface RenderRequest {
|
|
32
|
-
v: number;
|
|
33
|
-
kind: "render";
|
|
34
|
-
hookData: ClaudeHookData;
|
|
35
|
-
args: string[];
|
|
36
|
-
cwd: string;
|
|
37
|
-
// ─── Client hints ────────────────────────────────────────────────────────
|
|
38
|
-
// [LAW:single-enforcer] Facts only the LIVE CLIENT can observe, captured at
|
|
39
|
-
// the trust boundary and trusted by the daemon. The daemon is detached and
|
|
40
|
-
// one-per-user, so its own env answers for whichever shell spawned it —
|
|
41
|
-
// possibly a different session, possibly hours ago. Every field below is
|
|
42
|
-
// typed here but arrives as untrusted JSON: callers MUST route the request
|
|
43
|
-
// through parseClientHints at the receive boundary, never read these
|
|
44
|
-
// directly. See the ClientHints doc block for the absence semantics.
|
|
45
|
-
termCols?: number;
|
|
46
|
-
ssh?: boolean;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// [LAW:locality-or-seam] The seam for "a fact the daemon cannot observe about
|
|
50
|
-
// the session it is rendering for". `termCols` established the pattern; `ssh`
|
|
51
|
-
// is the second member, and the documented-but-unbuilt client-aware
|
|
52
|
-
// `colorCompatibility: "auto"` is the next. Naming the set as ONE type is what
|
|
53
|
-
// keeps that third addition a field rather than another sanitizer, another
|
|
54
|
-
// wire read, and another parameter threaded through the render path.
|
|
55
|
-
//
|
|
56
|
-
// [LAW:parse-dont-validate] This is the stamped type. `RenderRequest`'s
|
|
57
|
-
// same-named fields are raw JSON of unknown provenance; a `ClientHints` has
|
|
58
|
-
// crossed the checkpoint, so nothing downstream re-checks them.
|
|
59
|
-
//
|
|
60
|
-
// [LAW:types-are-the-program] Both fields are optional, but they mean
|
|
61
|
-
// DIFFERENT things by absence, and each is the strongest true theorem for its
|
|
62
|
-
// own fact:
|
|
63
|
-
// • `termCols` absent — the client tried and could not determine a width
|
|
64
|
-
// (no COLUMNS, no TTY on stderr). A genuine "unknown", reachable from any
|
|
65
|
-
// client version.
|
|
66
|
-
// • `ssh` absent — the client did not REPORT. A current client always knows
|
|
67
|
-
// (its own env is total on this question) and so always sends `true` or
|
|
68
|
-
// `false`; absence therefore means one thing only: a client too old to
|
|
69
|
-
// carry the field — a real case, because `cc-candybar install` stages a
|
|
70
|
-
// native binary that does not turn over with the npm package. Collapsing
|
|
71
|
-
// that to `false` here would fuse "we know it's local" with "we don't
|
|
72
|
-
// know" ([LAW:no-silent-failure]); instead it travels onward as an absent
|
|
73
|
-
// payload field, where the DSL input-fallback chain emits the declared
|
|
74
|
-
// default AND records a `last_error` that `cc-candybar debug vars`
|
|
75
|
-
// surfaces.
|
|
76
|
-
export interface ClientHints {
|
|
77
|
-
readonly termCols?: number;
|
|
78
|
-
readonly ssh?: boolean;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// [LAW:single-enforcer] The ONE checkpoint where wire-supplied client hints
|
|
82
|
-
// become trusted values. Per-field sanitizers stay separate (each fact has its
|
|
83
|
-
// own validity rule) but nothing outside this function calls them, so a new
|
|
84
|
-
// hint cannot reach the render path un-sanitized.
|
|
85
|
-
export function parseClientHints(req: RenderRequest): ClientHints {
|
|
86
|
-
const termCols = sanitizeTermCols(req.termCols);
|
|
87
|
-
const ssh = sanitizeSsh(req.ssh);
|
|
88
|
-
return {
|
|
89
|
-
...(termCols !== undefined && { termCols }),
|
|
90
|
-
...(ssh !== undefined && { ssh }),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// [LAW:no-defensive-null-guards] exception: trust boundary. The wire is
|
|
95
|
-
// untrusted JSON; downstream code treats termCols as an integer in a sane
|
|
96
|
-
// range. Validate once here so the type's promise is true.
|
|
97
|
-
//
|
|
98
|
-
// Pathologically large values are capped (not rejected) so a future
|
|
99
|
-
// genuinely-huge terminal still renders — 10000 is two orders of magnitude
|
|
100
|
-
// above the largest plausible real terminal.
|
|
101
|
-
const MAX_TERM_COLS = 10000;
|
|
102
|
-
export function sanitizeTermCols(v: unknown): number | undefined {
|
|
103
|
-
if (typeof v !== "number") return undefined;
|
|
104
|
-
if (!Number.isFinite(v)) return undefined;
|
|
105
|
-
const n = Math.floor(v);
|
|
106
|
-
if (n <= 0) return undefined;
|
|
107
|
-
return n > MAX_TERM_COLS ? MAX_TERM_COLS : n;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// [LAW:no-defensive-null-guards] exception: trust boundary, same shape as
|
|
111
|
-
// sanitizeTermCols. A non-boolean (absent, or a malformed/hostile frame) is
|
|
112
|
-
// NOT coerced to `false` — the three wire states stay three
|
|
113
|
-
// ([LAW:no-silent-failure]): true, false, and "no answer from this client".
|
|
114
|
-
export function sanitizeSsh(v: unknown): boolean | undefined {
|
|
115
|
-
return typeof v === "boolean" ? v : undefined;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export interface ShutdownRequest {
|
|
119
|
-
v: number;
|
|
120
|
-
kind: "shutdown";
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export interface ClickRequest {
|
|
124
|
-
v: number;
|
|
125
|
-
kind: "click";
|
|
126
|
-
verb: string;
|
|
127
|
-
value: string;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export interface StatsRequest {
|
|
131
|
-
v: number;
|
|
132
|
-
kind: "stats";
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// [LAW:types-are-the-program] The `what` discriminator carries the response
|
|
136
|
-
// shape forward — a client requesting "vars" gets vars data, "segments" gets
|
|
137
|
-
// segments data, "config" gets config data. No string-keyed lookup on the
|
|
138
|
-
// client side; route by structure. See DebugSnapshot in ./debug-types.
|
|
139
|
-
export interface DebugRequest {
|
|
140
|
-
v: number;
|
|
141
|
-
kind: "debug";
|
|
142
|
-
what: DebugWhat;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export type Request =
|
|
146
|
-
| RenderRequest
|
|
147
|
-
| ShutdownRequest
|
|
148
|
-
| StatsRequest
|
|
149
|
-
| ClickRequest
|
|
150
|
-
| DebugRequest;
|
|
151
|
-
|
|
152
|
-
export type Response =
|
|
153
|
-
| { ok: true; output: string }
|
|
154
|
-
| { ok: true; stats: StatsSnapshot }
|
|
155
|
-
// [LAW:types-are-the-program] DebugSnapshot is itself a discriminated union
|
|
156
|
-
// on `what`, so the response type carries the requested kind through to the
|
|
157
|
-
// client. A `{ ok: true; debug: { what: "vars"; vars: [...] } }` shape is
|
|
158
|
-
// self-describing — the client doesn't need to remember which `what` it sent.
|
|
159
|
-
| { ok: true; debug: DebugSnapshot }
|
|
160
|
-
// [LAW:types-are-the-program] `daemonV` is the daemon's own
|
|
161
|
-
// PROTOCOL_VERSION, echoed on every error response so the client can render
|
|
162
|
-
// a meaningful diagnostic on VERSION_MISMATCH without parsing the human
|
|
163
|
-
// message. Optional for back-compat: older daemons (or test stubs) that
|
|
164
|
-
// omit it are still parseable by current clients.
|
|
165
|
-
| { ok: false; error: string; code: ErrorCode; daemonV?: number };
|
|
166
|
-
|
|
167
|
-
// [LAW:types-are-the-program] The wire-level discriminator splits failures
|
|
168
|
-
// into two recovery classes: TIMEOUT is transient — the daemon is alive but
|
|
169
|
-
// slow, so a respawn or retry has a real chance of recovering. Every other
|
|
170
|
-
// code is permanent — respawning would hit the same response identically
|
|
171
|
-
// (the daemon refuses the request for VERSION_MISMATCH or BAD_REQUEST, or
|
|
172
|
-
// fails internally for RENDER_FAILED in a way the spawn loop cannot cure),
|
|
173
|
-
// so the spiral-breaker contract requires the client NOT to kick. The
|
|
174
|
-
// kick-vs-no-kick decision is encoded off this code, not off the error
|
|
175
|
-
// string.
|
|
176
|
-
export type ErrorCode =
|
|
177
|
-
| "VERSION_MISMATCH"
|
|
178
|
-
| "TIMEOUT"
|
|
179
|
-
| "RENDER_FAILED"
|
|
180
|
-
| "BAD_REQUEST";
|
|
181
|
-
|
|
182
|
-
// [LAW:types-are-the-program] A typed error class for failures that originate
|
|
183
|
-
// inside the wire-protocol layer (oversized frame, JSON decode failure).
|
|
184
|
-
// Callers in src/daemon/client-transport.ts branch on `e instanceof ProtocolError`
|
|
185
|
-
// to classify these as `permanent/malformed_response` — far more robust than
|
|
186
|
-
// substring-matching against the message, which would silently drift if
|
|
187
|
-
// Node's JSON.parse error wording changes. The class is small but
|
|
188
|
-
// load-bearing: it makes the kick-vs-show-error decision a structural
|
|
189
|
-
// property of the thrown value, not a property of its english string.
|
|
190
|
-
export class ProtocolError extends Error {
|
|
191
|
-
constructor(message: string) {
|
|
192
|
-
super(message);
|
|
193
|
-
this.name = "ProtocolError";
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// 4-byte big-endian length prefix + UTF-8 JSON body. Length-prefix beats
|
|
198
|
-
// newline-delimited because error messages may contain embedded newlines and
|
|
199
|
-
// we'd rather not parse them out of-band.
|
|
200
|
-
//
|
|
201
|
-
// [LAW:one-source-of-truth] FRAME_HEADER_BYTES and MAX_FRAME_BYTES are part
|
|
202
|
-
// of the wire contract the Rust client mirrors (rust-client/src/main.rs);
|
|
203
|
-
// scripts/check-protocol.mjs diffs them, so they must stay named consts.
|
|
204
|
-
export const FRAME_HEADER_BYTES = 4;
|
|
205
|
-
export const MAX_FRAME_BYTES = 16 * 1024 * 1024;
|
|
206
|
-
|
|
207
|
-
export function encodeFrame(value: unknown): Buffer {
|
|
208
|
-
const body = Buffer.from(JSON.stringify(value), "utf8");
|
|
209
|
-
const header = Buffer.alloc(FRAME_HEADER_BYTES);
|
|
210
|
-
header.writeUInt32BE(body.length, 0);
|
|
211
|
-
return Buffer.concat([header, body]);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Streaming frame reader. Calls `onFrame` for each complete frame. Caller
|
|
215
|
-
// owns lifecycle — call `feed` with each chunk; reader keeps a buffer.
|
|
216
|
-
export function makeFrameReader(
|
|
217
|
-
onFrame: (frame: unknown) => void,
|
|
218
|
-
onError: (err: Error) => void,
|
|
219
|
-
) {
|
|
220
|
-
let buf = Buffer.alloc(0);
|
|
221
|
-
return function feed(chunk: Buffer): void {
|
|
222
|
-
buf = Buffer.concat([buf, chunk]);
|
|
223
|
-
while (buf.length >= FRAME_HEADER_BYTES) {
|
|
224
|
-
const len = buf.readUInt32BE(0);
|
|
225
|
-
// Hard cap to defend against a runaway sender allocating gigabytes.
|
|
226
|
-
// [LAW:types-are-the-program] ProtocolError carries the discriminator
|
|
227
|
-
// structurally — interpretException routes on `instanceof`, not on a
|
|
228
|
-
// brittle string match against the message body.
|
|
229
|
-
if (len > MAX_FRAME_BYTES) {
|
|
230
|
-
onError(new ProtocolError(`frame too large: ${len}`));
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
if (buf.length < FRAME_HEADER_BYTES + len) return;
|
|
234
|
-
const body = buf.subarray(FRAME_HEADER_BYTES, FRAME_HEADER_BYTES + len);
|
|
235
|
-
buf = buf.subarray(FRAME_HEADER_BYTES + len);
|
|
236
|
-
try {
|
|
237
|
-
onFrame(JSON.parse(body.toString("utf8")));
|
|
238
|
-
} catch (e) {
|
|
239
|
-
// JSON.parse throws SyntaxError; wrap as ProtocolError so the
|
|
240
|
-
// recovery class is structurally typed (not message-matched).
|
|
241
|
-
// Preserve the original cause for diagnostic logging.
|
|
242
|
-
const wrapped = new ProtocolError(
|
|
243
|
-
e instanceof Error ? e.message : String(e),
|
|
244
|
-
);
|
|
245
|
-
if (e instanceof Error) {
|
|
246
|
-
wrapped.cause = e;
|
|
247
|
-
}
|
|
248
|
-
onError(wrapped);
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Send one frame and await one response, with a hard total budget. Resolves
|
|
256
|
-
// to the parsed response or rejects on timeout / parse error / socket error.
|
|
257
|
-
export function sendOne(
|
|
258
|
-
sock: Socket,
|
|
259
|
-
req: Request,
|
|
260
|
-
totalBudgetMs: number,
|
|
261
|
-
): Promise<Response> {
|
|
262
|
-
return new Promise((resolve, reject) => {
|
|
263
|
-
let settled = false;
|
|
264
|
-
const finish = (fn: () => void) => {
|
|
265
|
-
if (settled) return;
|
|
266
|
-
settled = true;
|
|
267
|
-
clearTimeout(timer);
|
|
268
|
-
sock.removeAllListeners();
|
|
269
|
-
try {
|
|
270
|
-
fn();
|
|
271
|
-
} catch {}
|
|
272
|
-
};
|
|
273
|
-
const timer = setTimeout(() => {
|
|
274
|
-
finish(() => {
|
|
275
|
-
sock.destroy();
|
|
276
|
-
reject(new Error("TIMEOUT"));
|
|
277
|
-
});
|
|
278
|
-
}, totalBudgetMs);
|
|
279
|
-
|
|
280
|
-
const reader = makeFrameReader(
|
|
281
|
-
(frame) => finish(() => resolve(frame as Response)),
|
|
282
|
-
(err) => finish(() => reject(err)),
|
|
283
|
-
);
|
|
284
|
-
sock.on("data", reader);
|
|
285
|
-
sock.on("error", (err) => finish(() => reject(err)));
|
|
286
|
-
sock.on("close", () =>
|
|
287
|
-
finish(() => reject(new Error("socket closed before response"))),
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
sock.write(encodeFrame(req));
|
|
291
|
-
});
|
|
292
|
-
}
|