@grackle-ai/core 0.83.0 → 0.83.2
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/codespace-handlers.d.ts +6 -0
- package/dist/codespace-handlers.d.ts.map +1 -0
- package/dist/codespace-handlers.js +66 -0
- package/dist/codespace-handlers.js.map +1 -0
- package/dist/environment-handlers.d.ts +16 -0
- package/dist/environment-handlers.d.ts.map +1 -0
- package/dist/environment-handlers.js +237 -0
- package/dist/environment-handlers.js.map +1 -0
- package/dist/finding-handlers.d.ts +6 -0
- package/dist/finding-handlers.d.ts.map +1 -0
- package/dist/finding-handlers.js +27 -0
- package/dist/finding-handlers.js.map +1 -0
- package/dist/grpc-mcp-config.d.ts +11 -0
- package/dist/grpc-mcp-config.d.ts.map +1 -0
- package/dist/grpc-mcp-config.js +29 -0
- package/dist/grpc-mcp-config.js.map +1 -0
- package/dist/grpc-proto-converters.d.ts +25 -0
- package/dist/grpc-proto-converters.d.ts.map +1 -0
- package/dist/grpc-proto-converters.js +193 -0
- package/dist/grpc-proto-converters.js.map +1 -0
- package/dist/grpc-service.d.ts +3 -19
- package/dist/grpc-service.d.ts.map +1 -1
- package/dist/grpc-service.js +28 -2018
- package/dist/grpc-service.js.map +1 -1
- package/dist/grpc-shared.d.ts +25 -0
- package/dist/grpc-shared.d.ts.map +1 -0
- package/dist/grpc-shared.js +110 -0
- package/dist/grpc-shared.js.map +1 -0
- package/dist/knowledge-handlers.d.ts +12 -0
- package/dist/knowledge-handlers.d.ts.map +1 -0
- package/dist/knowledge-handlers.js +85 -0
- package/dist/knowledge-handlers.js.map +1 -0
- package/dist/persona-handlers.d.ts +12 -0
- package/dist/persona-handlers.d.ts.map +1 -0
- package/dist/persona-handlers.js +148 -0
- package/dist/persona-handlers.js.map +1 -0
- package/dist/schedule-handlers.d.ts +12 -0
- package/dist/schedule-handlers.d.ts.map +1 -0
- package/dist/schedule-handlers.js +122 -0
- package/dist/schedule-handlers.js.map +1 -0
- package/dist/session-handlers.d.ts +34 -0
- package/dist/session-handlers.d.ts.map +1 -0
- package/dist/session-handlers.js +488 -0
- package/dist/session-handlers.js.map +1 -0
- package/dist/settings-handlers.d.ts +10 -0
- package/dist/settings-handlers.d.ts.map +1 -0
- package/dist/settings-handlers.js +68 -0
- package/dist/settings-handlers.js.map +1 -0
- package/dist/signals/sigchld.d.ts.map +1 -1
- package/dist/signals/sigchld.js +4 -8
- package/dist/signals/sigchld.js.map +1 -1
- package/dist/stream-registry.js +2 -8
- package/dist/stream-registry.js.map +1 -1
- package/dist/task-handlers.d.ts +22 -0
- package/dist/task-handlers.d.ts.map +1 -0
- package/dist/task-handlers.js +463 -0
- package/dist/task-handlers.js.map +1 -0
- package/dist/token-handlers.d.ts +12 -0
- package/dist/token-handlers.d.ts.map +1 -0
- package/dist/token-handlers.js +85 -0
- package/dist/token-handlers.js.map +1 -0
- package/dist/workspace-handlers.d.ts +12 -0
- package/dist/workspace-handlers.d.ts.map +1 -0
- package/dist/workspace-handlers.js +86 -0
- package/dist/workspace-handlers.js.map +1 -0
- package/package.json +7 -7
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { ConnectError, Code } from "@connectrpc/connect";
|
|
2
|
+
import { create } from "@bufbuild/protobuf";
|
|
3
|
+
import { grackle, powerline } from "@grackle-ai/common";
|
|
4
|
+
import { DEFAULT_MCP_PORT, SESSION_STATUS, TERMINAL_SESSION_STATUSES, LOGS_DIR, eventTypeToEnum, } from "@grackle-ai/common";
|
|
5
|
+
import { envRegistry, sessionStore, taskStore, grackleHome } from "@grackle-ai/database";
|
|
6
|
+
import { v4 as uuid } from "uuid";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { reconnectOrProvision } from "@grackle-ai/adapter-sdk";
|
|
9
|
+
import * as adapterManager from "./adapter-manager.js";
|
|
10
|
+
import * as streamHub from "./stream-hub.js";
|
|
11
|
+
import * as tokenPush from "./token-push.js";
|
|
12
|
+
import { parseAdapterConfig } from "./adapter-config.js";
|
|
13
|
+
import { emit } from "./event-bus.js";
|
|
14
|
+
import { processEventStream } from "./event-processor.js";
|
|
15
|
+
import { recoverSuspendedSessions } from "./session-recovery.js";
|
|
16
|
+
import { logger } from "./logger.js";
|
|
17
|
+
import { reanimateAgent } from "./reanimate-agent.js";
|
|
18
|
+
import * as streamRegistry from "./stream-registry.js";
|
|
19
|
+
import * as pipeDelivery from "./pipe-delivery.js";
|
|
20
|
+
import * as logWriter from "./log-writer.js";
|
|
21
|
+
import { createScopedToken, loadOrCreateApiKey } from "@grackle-ai/auth";
|
|
22
|
+
import { resolvePersona, SystemPromptBuilder } from "@grackle-ai/prompt";
|
|
23
|
+
import { sendInputToSession } from "./signals/signal-delivery.js";
|
|
24
|
+
import { createEventStream } from "./event-hub.js";
|
|
25
|
+
import { sessionRowToProto } from "./grpc-proto-converters.js";
|
|
26
|
+
import { validatePipeInputs, toDialableHost, killSessionAndCleanup } from "./grpc-shared.js";
|
|
27
|
+
import { personaMcpServersToJson } from "./grpc-mcp-config.js";
|
|
28
|
+
/** Spawn a new agent session in the given environment. */
|
|
29
|
+
export async function spawnAgent(req) {
|
|
30
|
+
if (!req.environmentId) {
|
|
31
|
+
throw new ConnectError("environment_id is required", Code.InvalidArgument);
|
|
32
|
+
}
|
|
33
|
+
const env = envRegistry.getEnvironment(req.environmentId);
|
|
34
|
+
if (!env) {
|
|
35
|
+
throw new ConnectError(`Environment not found: ${req.environmentId}`, Code.NotFound);
|
|
36
|
+
}
|
|
37
|
+
let conn = adapterManager.getConnection(req.environmentId);
|
|
38
|
+
if (!conn) {
|
|
39
|
+
// Auto-provision: attempt to reconnect/provision a disconnected environment
|
|
40
|
+
const adapter = adapterManager.getAdapter(env.adapterType);
|
|
41
|
+
if (!adapter) {
|
|
42
|
+
throw new ConnectError(`No adapter for type: ${env.adapterType}`, Code.FailedPrecondition);
|
|
43
|
+
}
|
|
44
|
+
logger.info({ environmentId: req.environmentId }, "Auto-provisioning environment for SpawnAgent");
|
|
45
|
+
envRegistry.updateEnvironmentStatus(req.environmentId, "connecting");
|
|
46
|
+
emit("environment.changed", {});
|
|
47
|
+
const config = parseAdapterConfig(env.adapterConfig);
|
|
48
|
+
config.defaultRuntime = env.defaultRuntime;
|
|
49
|
+
const powerlineToken = env.powerlineToken;
|
|
50
|
+
try {
|
|
51
|
+
for await (const provEvent of reconnectOrProvision(req.environmentId, adapter, config, powerlineToken, !!env.bootstrapped)) {
|
|
52
|
+
logger.info({ environmentId: req.environmentId, stage: provEvent.stage }, "Auto-provision progress (SpawnAgent)");
|
|
53
|
+
emit("environment.provision_progress", {
|
|
54
|
+
environmentId: req.environmentId,
|
|
55
|
+
stage: provEvent.stage,
|
|
56
|
+
message: provEvent.message,
|
|
57
|
+
progress: provEvent.progress,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
conn = await adapter.connect(req.environmentId, config, powerlineToken);
|
|
61
|
+
adapterManager.setConnection(req.environmentId, conn);
|
|
62
|
+
await tokenPush.pushToEnv(req.environmentId);
|
|
63
|
+
envRegistry.updateEnvironmentStatus(req.environmentId, "connected");
|
|
64
|
+
envRegistry.markBootstrapped(req.environmentId);
|
|
65
|
+
emit("environment.changed", {});
|
|
66
|
+
// Auto-recover suspended sessions (fire-and-forget)
|
|
67
|
+
recoverSuspendedSessions(req.environmentId, conn).catch((err) => {
|
|
68
|
+
logger.error({ environmentId: req.environmentId, err }, "Session recovery failed");
|
|
69
|
+
});
|
|
70
|
+
logger.info({ environmentId: req.environmentId }, "Auto-provision complete (SpawnAgent)");
|
|
71
|
+
emit("environment.provision_progress", {
|
|
72
|
+
environmentId: req.environmentId,
|
|
73
|
+
stage: "ready",
|
|
74
|
+
message: "Environment connected",
|
|
75
|
+
progress: 1,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
logger.error({ environmentId: req.environmentId, err }, "Auto-provision failed (SpawnAgent)");
|
|
80
|
+
envRegistry.updateEnvironmentStatus(req.environmentId, "error");
|
|
81
|
+
emit("environment.changed", {});
|
|
82
|
+
throw new ConnectError(`Failed to auto-connect environment ${req.environmentId}: ${err instanceof Error ? err.message : String(err)}`, Code.FailedPrecondition);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Resolve persona via cascade (request → app default)
|
|
86
|
+
let resolved;
|
|
87
|
+
try {
|
|
88
|
+
resolved = resolvePersona(req.personaId);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
throw new ConnectError(err.message, Code.FailedPrecondition);
|
|
92
|
+
}
|
|
93
|
+
const sessionId = uuid();
|
|
94
|
+
const { runtime, model, systemPrompt, persona } = resolved;
|
|
95
|
+
const maxTurns = req.maxTurns || resolved.maxTurns;
|
|
96
|
+
const logPath = join(grackleHome, LOGS_DIR, sessionId);
|
|
97
|
+
const builderPrompt = new SystemPromptBuilder({
|
|
98
|
+
personaPrompt: systemPrompt,
|
|
99
|
+
}).build();
|
|
100
|
+
const systemContext = req.systemContext
|
|
101
|
+
? builderPrompt + "\n\n" + req.systemContext
|
|
102
|
+
: builderPrompt;
|
|
103
|
+
// Validate pipe inputs before creating the session or spawning the child
|
|
104
|
+
validatePipeInputs(req.pipe, req.parentSessionId);
|
|
105
|
+
const pipeMode = req.pipe;
|
|
106
|
+
sessionStore.createSession(sessionId, req.environmentId, runtime, req.prompt, model, logPath, "", // taskId
|
|
107
|
+
resolved.personaId, // personaId
|
|
108
|
+
req.parentSessionId || "", // parentSessionId
|
|
109
|
+
pipeMode || "");
|
|
110
|
+
const mcpServersJson = personaMcpServersToJson(persona);
|
|
111
|
+
const mcpPort = parseInt(process.env.GRACKLE_MCP_PORT || String(DEFAULT_MCP_PORT), 10);
|
|
112
|
+
const mcpDialHost = toDialableHost(process.env.GRACKLE_HOST || "127.0.0.1");
|
|
113
|
+
const mcpUrl = `http://${mcpDialHost}:${mcpPort}/mcp`;
|
|
114
|
+
const mcpToken = createScopedToken({ sub: sessionId, pid: "", per: resolved.personaId, sid: sessionId }, loadOrCreateApiKey(grackleHome));
|
|
115
|
+
const powerlineReq = create(powerline.SpawnRequestSchema, {
|
|
116
|
+
sessionId,
|
|
117
|
+
runtime,
|
|
118
|
+
prompt: req.prompt,
|
|
119
|
+
model,
|
|
120
|
+
maxTurns,
|
|
121
|
+
branch: req.branch,
|
|
122
|
+
workingDirectory: req.branch
|
|
123
|
+
? (req.workingDirectory.trim() || process.env.GRACKLE_WORKING_DIRECTORY || process.env.GRACKLE_WORKTREE_BASE || "/workspace")
|
|
124
|
+
: "",
|
|
125
|
+
systemContext,
|
|
126
|
+
mcpServersJson,
|
|
127
|
+
mcpUrl,
|
|
128
|
+
mcpToken,
|
|
129
|
+
scriptContent: resolved.type === "script" ? resolved.script : "",
|
|
130
|
+
pipe: req.pipe,
|
|
131
|
+
});
|
|
132
|
+
// Create lifecycle stream — every session gets one. The spawner holds
|
|
133
|
+
// a lifecycle fd; when it's closed, the session auto-stops.
|
|
134
|
+
const lifecycleStream = streamRegistry.createStream(`lifecycle:${sessionId}`);
|
|
135
|
+
const spawnerId = req.parentSessionId || "__server__";
|
|
136
|
+
streamRegistry.subscribe(lifecycleStream.id, spawnerId, "rw", "detach", true);
|
|
137
|
+
streamRegistry.subscribe(lifecycleStream.id, sessionId, "rw", "detach", false);
|
|
138
|
+
// Set up IPC pipe stream (optional, on top of lifecycle stream)
|
|
139
|
+
let pipeFd = 0;
|
|
140
|
+
if (pipeMode && pipeMode !== "detach" && req.parentSessionId) {
|
|
141
|
+
const ipcStream = streamRegistry.createStream(`pipe:${sessionId}`);
|
|
142
|
+
const parentSub = streamRegistry.subscribe(ipcStream.id, req.parentSessionId, "rw", pipeMode === "sync" ? "sync" : "async", true);
|
|
143
|
+
streamRegistry.subscribe(ipcStream.id, sessionId, "rw", "async", false);
|
|
144
|
+
pipeFd = parentSub.fd;
|
|
145
|
+
if (pipeMode === "async") {
|
|
146
|
+
pipeDelivery.ensureAsyncDeliveryListener(req.parentSessionId); // parent receives child messages
|
|
147
|
+
pipeDelivery.ensureAsyncDeliveryListener(sessionId); // child receives parent messages
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Push fresh credentials before spawning (best-effort).
|
|
151
|
+
// For local envs, skip file tokens — the PowerLine is on the same machine.
|
|
152
|
+
await tokenPush.refreshTokensForTask(req.environmentId, runtime, env.adapterType === "local" ? { excludeFileTokens: true } : undefined);
|
|
153
|
+
processEventStream(conn.client.spawn(powerlineReq), {
|
|
154
|
+
sessionId,
|
|
155
|
+
logPath,
|
|
156
|
+
systemContext,
|
|
157
|
+
prompt: req.prompt,
|
|
158
|
+
});
|
|
159
|
+
const row = sessionStore.getSession(sessionId);
|
|
160
|
+
const proto = sessionRowToProto(row);
|
|
161
|
+
proto.pipeFd = pipeFd;
|
|
162
|
+
return proto;
|
|
163
|
+
}
|
|
164
|
+
/** Resume a previously suspended agent session. */
|
|
165
|
+
export async function resumeAgent(req) {
|
|
166
|
+
const row = reanimateAgent(req.sessionId);
|
|
167
|
+
return sessionRowToProto(row);
|
|
168
|
+
}
|
|
169
|
+
/** Send text input to a running session. */
|
|
170
|
+
export async function sendInput(req) {
|
|
171
|
+
const session = sessionStore.getSession(req.sessionId);
|
|
172
|
+
if (!session) {
|
|
173
|
+
throw new ConnectError(`Session not found: ${req.sessionId}`, Code.NotFound);
|
|
174
|
+
}
|
|
175
|
+
if (TERMINAL_SESSION_STATUSES.has(session.status)) {
|
|
176
|
+
throw new ConnectError(`Session ${req.sessionId} has ended (status: ${session.status})`, Code.FailedPrecondition);
|
|
177
|
+
}
|
|
178
|
+
const conn = adapterManager.getConnection(session.environmentId);
|
|
179
|
+
if (!conn) {
|
|
180
|
+
throw new ConnectError(`Environment ${session.environmentId} not connected`, Code.FailedPrecondition);
|
|
181
|
+
}
|
|
182
|
+
// Persist and publish user input event so subscribers see the text in the event stream
|
|
183
|
+
const userInputEvent = create(grackle.SessionEventSchema, {
|
|
184
|
+
sessionId: req.sessionId,
|
|
185
|
+
type: grackle.EventType.USER_INPUT,
|
|
186
|
+
timestamp: new Date().toISOString(),
|
|
187
|
+
content: req.text,
|
|
188
|
+
raw: "",
|
|
189
|
+
});
|
|
190
|
+
if (session.logPath) {
|
|
191
|
+
logWriter.writeEvent(session.logPath, userInputEvent);
|
|
192
|
+
}
|
|
193
|
+
streamHub.publish(userInputEvent);
|
|
194
|
+
await conn.client.sendInput(create(powerline.InputMessageSchema, {
|
|
195
|
+
sessionId: req.sessionId,
|
|
196
|
+
text: req.text,
|
|
197
|
+
}));
|
|
198
|
+
return create(grackle.EmptySchema, {});
|
|
199
|
+
}
|
|
200
|
+
/** Kill (or gracefully stop) an agent session. */
|
|
201
|
+
export async function killAgent(req) {
|
|
202
|
+
const session = sessionStore.getSession(req.id);
|
|
203
|
+
if (!session) {
|
|
204
|
+
throw new ConnectError(`Session not found: ${req.id}`, Code.NotFound);
|
|
205
|
+
}
|
|
206
|
+
if (req.graceful) {
|
|
207
|
+
// ── SIGTERM: deliver signal message, return immediately ──
|
|
208
|
+
if (!TERMINAL_SESSION_STATUSES.has(session.status)) {
|
|
209
|
+
const message = "[SIGTERM] You have been asked to stop gracefully. " +
|
|
210
|
+
"Finish your current operation, save your work, close any open IPC fds " +
|
|
211
|
+
"(ipc_close for each owned fd), then call task_complete (if applicable) and stop.";
|
|
212
|
+
// Set sigtermSentAt BEFORE delivering so that if the session
|
|
213
|
+
// completes instantly (race), the event-processor sees the flag.
|
|
214
|
+
sessionStore.setSigtermSentAt(session.id);
|
|
215
|
+
const delivered = await sendInputToSession(session.id, session.environmentId, message, "sigterm");
|
|
216
|
+
if (delivered) {
|
|
217
|
+
return create(grackle.EmptySchema, {});
|
|
218
|
+
}
|
|
219
|
+
// Delivery failed — clear the flag since SIGTERM wasn't actually sent
|
|
220
|
+
sessionStore.clearSigtermSentAt(session.id);
|
|
221
|
+
// If delivery failed (env disconnected), fall through to hard kill
|
|
222
|
+
logger.warn({ sessionId: session.id }, "SIGTERM delivery failed, falling back to hard kill");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// ── SIGKILL: terminate immediately ──
|
|
226
|
+
// Set STOPPED + killed BEFORE closing the lifecycle FD so the orphan
|
|
227
|
+
// callback sees the session is already terminal and skips. Without this,
|
|
228
|
+
// the orphan callback would see IDLE → reason="completed", which is wrong
|
|
229
|
+
// for an explicit kill.
|
|
230
|
+
killSessionAndCleanup(session);
|
|
231
|
+
return create(grackle.EmptySchema, {});
|
|
232
|
+
}
|
|
233
|
+
/** Get aggregated usage stats for a session, task, task tree, workspace, or environment. */
|
|
234
|
+
export async function getUsage(req) {
|
|
235
|
+
if (!req.id) {
|
|
236
|
+
throw new ConnectError("id is required", Code.InvalidArgument);
|
|
237
|
+
}
|
|
238
|
+
switch (req.scope) {
|
|
239
|
+
case "session": {
|
|
240
|
+
const session = sessionStore.getSession(req.id);
|
|
241
|
+
if (!session) {
|
|
242
|
+
throw new ConnectError(`Session not found: ${req.id}`, Code.NotFound);
|
|
243
|
+
}
|
|
244
|
+
return create(grackle.UsageStatsSchema, {
|
|
245
|
+
inputTokens: session.inputTokens,
|
|
246
|
+
outputTokens: session.outputTokens,
|
|
247
|
+
costUsd: session.costUsd,
|
|
248
|
+
sessionCount: 1,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
case "task": {
|
|
252
|
+
const usage = sessionStore.aggregateUsage({ taskId: req.id });
|
|
253
|
+
return create(grackle.UsageStatsSchema, usage);
|
|
254
|
+
}
|
|
255
|
+
case "task_tree": {
|
|
256
|
+
const descendants = taskStore.getDescendants(req.id);
|
|
257
|
+
const taskIds = [req.id, ...descendants.map((d) => d.id)];
|
|
258
|
+
const usage = sessionStore.aggregateUsage({ taskIds });
|
|
259
|
+
return create(grackle.UsageStatsSchema, usage);
|
|
260
|
+
}
|
|
261
|
+
case "workspace": {
|
|
262
|
+
const tasks = taskStore.listTasks(req.id);
|
|
263
|
+
const taskIds = tasks.map((t) => t.id);
|
|
264
|
+
const usage = taskIds.length > 0
|
|
265
|
+
? sessionStore.aggregateUsage({ taskIds })
|
|
266
|
+
: { inputTokens: 0, outputTokens: 0, costUsd: 0, sessionCount: 0 };
|
|
267
|
+
return create(grackle.UsageStatsSchema, usage);
|
|
268
|
+
}
|
|
269
|
+
case "environment": {
|
|
270
|
+
const usage = sessionStore.aggregateUsage({ environmentId: req.id });
|
|
271
|
+
return create(grackle.UsageStatsSchema, usage);
|
|
272
|
+
}
|
|
273
|
+
default:
|
|
274
|
+
throw new ConnectError(`Invalid usage scope: ${req.scope}`, Code.InvalidArgument);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/** Wait for a message on a synchronous pipe subscription. */
|
|
278
|
+
export async function waitForPipe(req) {
|
|
279
|
+
const sub = streamRegistry.getSubscription(req.sessionId, req.fd);
|
|
280
|
+
if (!sub) {
|
|
281
|
+
throw new ConnectError(`No subscription found for session ${req.sessionId} fd ${req.fd}`, Code.NotFound);
|
|
282
|
+
}
|
|
283
|
+
if (sub.deliveryMode !== "sync") {
|
|
284
|
+
throw new ConnectError(`Subscription fd ${req.fd} is not a sync subscription (mode: ${sub.deliveryMode})`, Code.FailedPrecondition);
|
|
285
|
+
}
|
|
286
|
+
// Capture child session ID before blocking — the pipe stream may be
|
|
287
|
+
// removed by a concurrent fd close while consumeSync is awaiting.
|
|
288
|
+
const pipeStream = streamRegistry.getStream(sub.streamId);
|
|
289
|
+
const childSessionId = pipeStream?.name.startsWith("pipe:")
|
|
290
|
+
? pipeStream.name.slice("pipe:".length)
|
|
291
|
+
: undefined;
|
|
292
|
+
// Use try/finally so the pipe stream (and lifecycle stream) are cleaned up
|
|
293
|
+
// even if consumeSync rejects (e.g., the request is cancelled or times out)
|
|
294
|
+
// to prevent unbounded memory growth. Lifecycle cleanup also orphans the child,
|
|
295
|
+
// triggering auto-stop so it doesn't linger in waiting_input (#824).
|
|
296
|
+
let msg;
|
|
297
|
+
try {
|
|
298
|
+
msg = await streamRegistry.consumeSync(sub.id);
|
|
299
|
+
}
|
|
300
|
+
finally {
|
|
301
|
+
pipeDelivery.cleanupSyncPipeAndLifecycle(sub.streamId, childSessionId);
|
|
302
|
+
}
|
|
303
|
+
return create(grackle.WaitForPipeResponseSchema, {
|
|
304
|
+
content: msg.content,
|
|
305
|
+
senderSessionId: msg.senderId,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
/** Write a message to a pipe fd. */
|
|
309
|
+
export async function writeToFd(req) {
|
|
310
|
+
const sub = streamRegistry.getSubscription(req.sessionId, req.fd);
|
|
311
|
+
if (!sub) {
|
|
312
|
+
throw new ConnectError(`No subscription found for session ${req.sessionId} fd ${req.fd}`, Code.NotFound);
|
|
313
|
+
}
|
|
314
|
+
if (sub.permission !== "w" && sub.permission !== "rw") {
|
|
315
|
+
throw new ConnectError(`Subscription fd ${req.fd} does not have write permission (permission: ${sub.permission})`, Code.FailedPrecondition);
|
|
316
|
+
}
|
|
317
|
+
const stream = streamRegistry.getStream(sub.streamId);
|
|
318
|
+
if (!stream) {
|
|
319
|
+
throw new ConnectError("Stream no longer exists", Code.FailedPrecondition);
|
|
320
|
+
}
|
|
321
|
+
// Publish to stream — delivery is handled by async listeners registered
|
|
322
|
+
// at spawn time via ensureAsyncDeliveryListener. This is the same path
|
|
323
|
+
// used by publishChildCompletion for child→parent delivery.
|
|
324
|
+
const msg = streamRegistry.publish(sub.streamId, req.sessionId, req.message);
|
|
325
|
+
// Verify delivery to async subscribers — check if the published message
|
|
326
|
+
// was marked as delivered for each async target. Sync and detach subscribers
|
|
327
|
+
// are excluded (sync waits for consumeSync, detach buffers silently).
|
|
328
|
+
for (const targetSub of stream.subscriptions.values()) {
|
|
329
|
+
if (targetSub.sessionId === req.sessionId) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (targetSub.deliveryMode === "async" && !msg.deliveredTo.has(targetSub.id)) {
|
|
333
|
+
throw new ConnectError("Message delivery failed — target environment may be disconnected", Code.FailedPrecondition);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return create(grackle.EmptySchema, {});
|
|
337
|
+
}
|
|
338
|
+
/** Close a pipe file descriptor, optionally stopping child sessions. */
|
|
339
|
+
export async function closeFd(req) {
|
|
340
|
+
const sub = streamRegistry.getSubscription(req.sessionId, req.fd);
|
|
341
|
+
if (!sub) {
|
|
342
|
+
throw new ConnectError(`No subscription found for session ${req.sessionId} fd ${req.fd}`, Code.NotFound);
|
|
343
|
+
}
|
|
344
|
+
if (streamRegistry.hasUndeliveredMessages(sub.id)) {
|
|
345
|
+
throw new ConnectError(`Cannot close fd ${req.fd}: undelivered messages pending. Process or consume them first.`, Code.FailedPrecondition);
|
|
346
|
+
}
|
|
347
|
+
const streamId = sub.streamId;
|
|
348
|
+
const stream = streamRegistry.getStream(streamId);
|
|
349
|
+
// Collect child sessions (inherited subscriptions, not the caller's)
|
|
350
|
+
const childSubs = [];
|
|
351
|
+
if (stream) {
|
|
352
|
+
for (const s of stream.subscriptions.values()) {
|
|
353
|
+
if (s.sessionId !== req.sessionId) {
|
|
354
|
+
childSubs.push({ sessionId: s.sessionId, subId: s.id });
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Unsubscribe the caller
|
|
359
|
+
streamRegistry.unsubscribe(sub.id);
|
|
360
|
+
// Also unsubscribe children — when their last subscription is removed,
|
|
361
|
+
// the lifecycle manager's orphan callback auto-stops them.
|
|
362
|
+
let stopped = false;
|
|
363
|
+
for (const child of childSubs) {
|
|
364
|
+
streamRegistry.unsubscribe(child.subId);
|
|
365
|
+
// Check if the child was orphaned (auto-stopped)
|
|
366
|
+
const childSession = sessionStore.getSession(child.sessionId);
|
|
367
|
+
if (childSession?.status === SESSION_STATUS.STOPPED) {
|
|
368
|
+
stopped = true;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// Clean up async listeners for caller and any unsubscribed children
|
|
372
|
+
pipeDelivery.cleanupAsyncListenerIfEmpty(req.sessionId);
|
|
373
|
+
for (const child of childSubs) {
|
|
374
|
+
pipeDelivery.cleanupAsyncListenerIfEmpty(child.sessionId);
|
|
375
|
+
}
|
|
376
|
+
return create(grackle.CloseFdResponseSchema, { stopped });
|
|
377
|
+
}
|
|
378
|
+
/** Get all open file descriptors for a session. */
|
|
379
|
+
export function getSessionFds(req) {
|
|
380
|
+
const subs = streamRegistry.getSubscriptionsForSession(req.id);
|
|
381
|
+
const fds = subs.map((sub) => {
|
|
382
|
+
const stream = streamRegistry.getStream(sub.streamId);
|
|
383
|
+
let targetSessionId = "";
|
|
384
|
+
if (stream) {
|
|
385
|
+
for (const s of stream.subscriptions.values()) {
|
|
386
|
+
if (s.sessionId !== req.id) {
|
|
387
|
+
targetSessionId = s.sessionId;
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return create(grackle.FdInfoSchema, {
|
|
393
|
+
fd: sub.fd,
|
|
394
|
+
streamName: stream?.name || "",
|
|
395
|
+
permission: sub.permission,
|
|
396
|
+
deliveryMode: sub.deliveryMode,
|
|
397
|
+
owned: sub.createdBySpawn,
|
|
398
|
+
targetSessionId,
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
return create(grackle.SessionFdsSchema, { fds });
|
|
402
|
+
}
|
|
403
|
+
/** List sessions with optional filters. */
|
|
404
|
+
export async function listSessions(req) {
|
|
405
|
+
const rows = sessionStore.listSessions(req.environmentId, req.status);
|
|
406
|
+
return create(grackle.SessionListSchema, {
|
|
407
|
+
sessions: rows.map(sessionRowToProto),
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
/** Get a session by ID. */
|
|
411
|
+
export async function getSession(req) {
|
|
412
|
+
const row = sessionStore.getSession(req.id);
|
|
413
|
+
if (!row) {
|
|
414
|
+
throw new ConnectError(`Session not found: ${req.id}`, Code.NotFound);
|
|
415
|
+
}
|
|
416
|
+
return sessionRowToProto(row);
|
|
417
|
+
}
|
|
418
|
+
/** Get all events recorded for a session. */
|
|
419
|
+
export async function getSessionEvents(req) {
|
|
420
|
+
const session = sessionStore.getSession(req.id);
|
|
421
|
+
if (!session) {
|
|
422
|
+
throw new ConnectError(`Session not found: ${req.id}`, Code.NotFound);
|
|
423
|
+
}
|
|
424
|
+
if (!session.logPath) {
|
|
425
|
+
return create(grackle.SessionEventListSchema, {
|
|
426
|
+
sessionId: req.id,
|
|
427
|
+
events: [],
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
const entries = logWriter.readLog(session.logPath);
|
|
431
|
+
return create(grackle.SessionEventListSchema, {
|
|
432
|
+
sessionId: req.id,
|
|
433
|
+
events: entries.map((e) => create(grackle.SessionEventSchema, {
|
|
434
|
+
sessionId: e.session_id,
|
|
435
|
+
type: eventTypeToEnum(e.type),
|
|
436
|
+
timestamp: e.timestamp,
|
|
437
|
+
content: e.content,
|
|
438
|
+
raw: e.raw || "",
|
|
439
|
+
})),
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
/** Get all sessions for a task. */
|
|
443
|
+
export async function getTaskSessions(req) {
|
|
444
|
+
if (!req.id) {
|
|
445
|
+
throw new ConnectError("task id is required", Code.InvalidArgument);
|
|
446
|
+
}
|
|
447
|
+
const rows = sessionStore.listSessionsForTask(req.id);
|
|
448
|
+
return create(grackle.SessionListSchema, {
|
|
449
|
+
sessions: rows.map(sessionRowToProto),
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
/** Stream session events as they occur. */
|
|
453
|
+
export async function* streamSession(req) {
|
|
454
|
+
const stream = streamHub.createStream(req.id);
|
|
455
|
+
try {
|
|
456
|
+
for await (const event of stream) {
|
|
457
|
+
yield event;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
finally {
|
|
461
|
+
stream.cancel();
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/** Stream all session events across all sessions. */
|
|
465
|
+
export async function* streamAll() {
|
|
466
|
+
const stream = streamHub.createGlobalStream();
|
|
467
|
+
try {
|
|
468
|
+
for await (const event of stream) {
|
|
469
|
+
yield event;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
finally {
|
|
473
|
+
stream.cancel();
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/** Stream domain events (replaces WebSocket event broadcasting). */
|
|
477
|
+
export async function* streamEvents() {
|
|
478
|
+
const stream = createEventStream();
|
|
479
|
+
try {
|
|
480
|
+
for await (const event of stream) {
|
|
481
|
+
yield event;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
finally {
|
|
485
|
+
stream.cancel();
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
//# sourceMappingURL=session-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-handlers.js","sourceRoot":"","sources":["../src/session-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,yBAAyB,EAEzB,QAAQ,EACR,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACzF,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,cAAc,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,cAAc,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAyB;IACxD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QACvB,MAAM,IAAI,YAAY,CAAC,4BAA4B,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IAC7E,CAAC;IACD,MAAM,GAAG,GAAG,WAAW,CAAC,cAAc,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC1D,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,YAAY,CAAC,0BAA0B,GAAG,CAAC,aAAa,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvF,CAAC;IAED,IAAI,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC3D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,4EAA4E;QAC5E,MAAM,OAAO,GAAG,cAAc,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC3D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,YAAY,CAAC,wBAAwB,GAAG,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAC7F,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,aAAa,EAAE,EAAE,8CAA8C,CAAC,CAAC;QAClG,WAAW,CAAC,uBAAuB,CAAC,GAAG,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;QACrE,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;QAEhC,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACrD,MAAM,CAAC,cAAc,GAAG,GAAG,CAAC,cAAc,CAAC;QAC3C,MAAM,cAAc,GAAG,GAAG,CAAC,cAAc,CAAC;QAE1C,IAAI,CAAC;YACH,IAAI,KAAK,EAAE,MAAM,SAAS,IAAI,oBAAoB,CAChD,GAAG,CAAC,aAAa,EACjB,OAAO,EACP,MAAM,EACN,cAAc,EACd,CAAC,CAAC,GAAG,CAAC,YAAY,CACnB,EAAE,CAAC;gBACF,MAAM,CAAC,IAAI,CACT,EAAE,aAAa,EAAE,GAAG,CAAC,aAAa,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,EAC5D,sCAAsC,CACvC,CAAC;gBACF,IAAI,CAAC,gCAAgC,EAAE;oBACrC,aAAa,EAAE,GAAG,CAAC,aAAa;oBAChC,KAAK,EAAE,SAAS,CAAC,KAAK;oBACtB,OAAO,EAAE,SAAS,CAAC,OAAO;oBAC1B,QAAQ,EAAE,SAAS,CAAC,QAAQ;iBAC7B,CAAC,CAAC;YACL,CAAC;YAED,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;YACxE,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;YACtD,MAAM,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YAC7C,WAAW,CAAC,uBAAuB,CAAC,GAAG,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;YACpE,WAAW,CAAC,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YAChD,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;YAChC,oDAAoD;YACpD,wBAAwB,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC9D,MAAM,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,yBAAyB,CAAC,CAAC;YACrF,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,aAAa,EAAE,EAAE,sCAAsC,CAAC,CAAC;YAC1F,IAAI,CAAC,gCAAgC,EAAE;gBACrC,aAAa,EAAE,GAAG,CAAC,aAAa;gBAChC,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,uBAAuB;gBAChC,QAAQ,EAAE,CAAC;aACZ,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,oCAAoC,CAAC,CAAC;YAC9F,WAAW,CAAC,uBAAuB,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;YAChE,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;YAChC,MAAM,IAAI,YAAY,CACpB,sCAAsC,GAAG,CAAC,aAAa,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAC9G,IAAI,CAAC,kBAAkB,CACxB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,sDAAsD;IACtD,IAAI,QAA2C,CAAC;IAChD,IAAI,CAAC;QACH,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,YAAY,CAAE,GAAa,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,EAAE,CAAC;IACzB,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC;IAC3D,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC;IACnD,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAEvD,MAAM,aAAa,GAAG,IAAI,mBAAmB,CAAC;QAC5C,aAAa,EAAE,YAAY;KAC5B,CAAC,CAAC,KAAK,EAAE,CAAC;IACX,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa;QACrC,CAAC,CAAC,aAAa,GAAG,MAAM,GAAG,GAAG,CAAC,aAAa;QAC5C,CAAC,CAAC,aAAa,CAAC;IAElB,yEAAyE;IACzE,kBAAkB,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAgB,CAAC;IAEtC,YAAY,CAAC,aAAa,CACxB,SAAS,EACT,GAAG,CAAC,aAAa,EACjB,OAAO,EACP,GAAG,CAAC,MAAM,EACV,KAAK,EACL,OAAO,EACP,EAAE,EAAuB,SAAS;IAClC,QAAQ,CAAC,SAAS,EAAO,YAAY;IACrC,GAAG,CAAC,eAAe,IAAI,EAAE,EAAG,kBAAkB;IAC9C,QAAQ,IAAI,EAAE,CACf,CAAC;IAEF,MAAM,cAAc,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAExD,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,MAAM,CAAC,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAC;IACvF,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,WAAW,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,UAAU,WAAW,IAAI,OAAO,MAAM,CAAC;IACtD,MAAM,QAAQ,GAAG,iBAAiB,CAChC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,EACpE,kBAAkB,CAAC,WAAW,CAAC,CAChC,CAAC;IAEF,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,kBAAkB,EAAE;QACxD,SAAS;QACT,OAAO;QACP,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,KAAK;QACL,QAAQ;QACR,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,gBAAgB,EAAE,GAAG,CAAC,MAAM;YAC1B,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,YAAY,CAAC;YAC7H,CAAC,CAAC,EAAE;QACN,aAAa;QACb,cAAc;QACd,MAAM;QACN,QAAQ;QACR,aAAa,EAAE,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;QAChE,IAAI,EAAE,GAAG,CAAC,IAAI;KACf,CAAC,CAAC;IAEH,sEAAsE;IACtE,4DAA4D;IAC5D,MAAM,eAAe,GAAG,cAAc,CAAC,YAAY,CAAC,aAAa,SAAS,EAAE,CAAC,CAAC;IAC9E,MAAM,SAAS,GAAG,GAAG,CAAC,eAAe,IAAI,YAAY,CAAC;IACtD,cAAc,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9E,cAAc,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAE/E,gEAAgE;IAChE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,QAAQ,IAAI,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,eAAe,EAAE,CAAC;QAC7D,MAAM,SAAS,GAAG,cAAc,CAAC,YAAY,CAAC,QAAQ,SAAS,EAAE,CAAC,CAAC;QACnE,MAAM,SAAS,GAAG,cAAc,CAAC,SAAS,CACxC,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,eAAe,EAAE,IAAI,EACvC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EACtC,IAAI,CACL,CAAC;QACF,cAAc,CAAC,SAAS,CACtB,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EACtC,KAAK,CACN,CAAC;QACF,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;QAEtB,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACzB,YAAY,CAAC,2BAA2B,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAE,iCAAiC;YACjG,YAAY,CAAC,2BAA2B,CAAC,SAAS,CAAC,CAAC,CAAa,iCAAiC;QACpG,CAAC;IACH,CAAC;IAED,wDAAwD;IACxD,2EAA2E;IAC3E,MAAM,SAAS,CAAC,oBAAoB,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,EAC7D,GAAG,CAAC,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEzE,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE;QAClD,SAAS;QACT,OAAO;QACP,aAAa;QACb,MAAM,EAAE,GAAG,CAAC,MAAM;KACnB,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAI,CAAC,CAAC;IACtC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,mDAAmD;AACnD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAA0B;IAC1D,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC1C,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED,4CAA4C;AAC5C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAyB;IACvD,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACvD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,YAAY,CAAC,sBAAsB,GAAG,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,yBAAyB,CAAC,GAAG,CAAC,OAAO,CAAC,MAAuB,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,YAAY,CACpB,WAAW,GAAG,CAAC,SAAS,uBAAuB,OAAO,CAAC,MAAM,GAAG,EAChE,IAAI,CAAC,kBAAkB,CACxB,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACjE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,YAAY,CAAC,eAAe,OAAO,CAAC,aAAa,gBAAgB,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACxG,CAAC;IAED,uFAAuF;IACvF,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE;QACxD,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,UAAU;QAClC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO,EAAE,GAAG,CAAC,IAAI;QACjB,GAAG,EAAE,EAAE;KACR,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IACxD,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAElC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CACzB,MAAM,CAAC,SAAS,CAAC,kBAAkB,EAAE;QACnC,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,IAAI,EAAE,GAAG,CAAC,IAAI;KACf,CAAC,CACH,CAAC;IAEF,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,kDAAkD;AAClD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAA6B;IAC3D,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,YAAY,CAAC,sBAAsB,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QACjB,4DAA4D;QAC5D,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,OAAO,CAAC,MAAuB,CAAC,EAAE,CAAC;YACpE,MAAM,OAAO,GACX,oDAAoD;gBACpD,wEAAwE;gBACxE,kFAAkF,CAAC;YACrF,6DAA6D;YAC7D,iEAAiE;YACjE,YAAY,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC1C,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;YAClG,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YACzC,CAAC;YACD,sEAAsE;YACtE,YAAY,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC5C,mEAAmE;YACnE,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,oDAAoD,CAAC,CAAC;QAC/F,CAAC;IACH,CAAC;IAED,uCAAuC;IACvC,qEAAqE;IACrE,yEAAyE;IACzE,0EAA0E;IAC1E,wBAAwB;IACxB,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAE/B,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,4FAA4F;AAC5F,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAA4B;IACzD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,YAAY,CAAC,gBAAgB,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IACjE,CAAC;IACD,QAAQ,GAAG,CAAC,KAAK,EAAE,CAAC;QAClB,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,YAAY,CAAC,sBAAsB,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxE,CAAC;YACD,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE;gBACtC,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,YAAY,EAAE,CAAC;aAChB,CAAC,CAAC;QACL,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,KAAK,GAAG,YAAY,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;YAC9D,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QACjD,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,WAAW,GAAG,SAAS,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrD,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1D,MAAM,KAAK,GAAG,YAAY,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YACvD,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QACjD,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACvC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC;gBAC9B,CAAC,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,CAAC;gBAC1C,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;YACrE,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QACjD,CAAC;QACD,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,KAAK,GAAG,YAAY,CAAC,cAAc,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;YACrE,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QACjD,CAAC;QACD;YACE,MAAM,IAAI,YAAY,CAAC,wBAAwB,GAAG,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IACtF,CAAC;AACH,CAAC;AAED,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAA+B;IAC/D,MAAM,GAAG,GAAG,cAAc,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAClE,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,YAAY,CACpB,qCAAqC,GAAG,CAAC,SAAS,OAAO,GAAG,CAAC,EAAE,EAAE,EACjE,IAAI,CAAC,QAAQ,CACd,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,CAAC,YAAY,KAAK,MAAM,EAAE,CAAC;QAChC,MAAM,IAAI,YAAY,CACpB,mBAAmB,GAAG,CAAC,EAAE,sCAAsC,GAAG,CAAC,YAAY,GAAG,EAClF,IAAI,CAAC,kBAAkB,CACxB,CAAC;IACJ,CAAC;IAED,oEAAoE;IACpE,kEAAkE;IAClE,MAAM,UAAU,GAAG,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC1D,MAAM,cAAc,GAAG,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QACzD,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QACvC,CAAC,CAAC,SAAS,CAAC;IAEd,2EAA2E;IAC3E,4EAA4E;IAC5E,gFAAgF;IAChF,qEAAqE;IACrE,IAAI,GAA2D,CAAC;IAChE,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACjD,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,2BAA2B,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,yBAAyB,EAAE;QAC/C,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,eAAe,EAAE,GAAG,CAAC,QAAQ;KAC9B,CAAC,CAAC;AACL,CAAC;AAED,oCAAoC;AACpC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAA6B;IAC3D,MAAM,GAAG,GAAG,cAAc,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAClE,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,YAAY,CACpB,qCAAqC,GAAG,CAAC,SAAS,OAAO,GAAG,CAAC,EAAE,EAAE,EACjE,IAAI,CAAC,QAAQ,CACd,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,IAAI,GAAG,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QACtD,MAAM,IAAI,YAAY,CACpB,mBAAmB,GAAG,CAAC,EAAE,gDAAgD,GAAG,CAAC,UAAU,GAAG,EAC1F,IAAI,CAAC,kBAAkB,CACxB,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,YAAY,CAAC,yBAAyB,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC7E,CAAC;IAED,wEAAwE;IACxE,uEAAuE;IACvE,4DAA4D;IAC5D,MAAM,GAAG,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAE7E,wEAAwE;IACxE,6EAA6E;IAC7E,sEAAsE;IACtE,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC;QACtD,IAAI,SAAS,CAAC,SAAS,KAAK,GAAG,CAAC,SAAS,EAAE,CAAC;YAC1C,SAAS;QACX,CAAC;QACD,IAAI,SAAS,CAAC,YAAY,KAAK,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YAC7E,MAAM,IAAI,YAAY,CACpB,kEAAkE,EAClE,IAAI,CAAC,kBAAkB,CACxB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,wEAAwE;AACxE,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAA2B;IACvD,MAAM,GAAG,GAAG,cAAc,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAClE,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,YAAY,CACpB,qCAAqC,GAAG,CAAC,SAAS,OAAO,GAAG,CAAC,EAAE,EAAE,EACjE,IAAI,CAAC,QAAQ,CACd,CAAC;IACJ,CAAC;IACD,IAAI,cAAc,CAAC,sBAAsB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,YAAY,CACpB,mBAAmB,GAAG,CAAC,EAAE,gEAAgE,EACzF,IAAI,CAAC,kBAAkB,CACxB,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC9B,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAElD,qEAAqE;IACrE,MAAM,SAAS,GAAgD,EAAE,CAAC;IAClE,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9C,IAAI,CAAC,CAAC,SAAS,KAAK,GAAG,CAAC,SAAS,EAAE,CAAC;gBAClC,SAAS,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,cAAc,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEnC,uEAAuE;IACvE,2DAA2D;IAC3D,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,cAAc,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACxC,iDAAiD;QACjD,MAAM,YAAY,GAAG,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC9D,IAAI,YAAY,EAAE,MAAM,KAAK,cAAc,CAAC,OAAO,EAAE,CAAC;YACpD,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;IACH,CAAC;IAED,oEAAoE;IACpE,YAAY,CAAC,2BAA2B,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACxD,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,YAAY,CAAC,2BAA2B,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,aAAa,CAAC,GAAsB;IAClD,MAAM,IAAI,GAAG,cAAc,CAAC,0BAA0B,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACtD,IAAI,eAAe,GAAG,EAAE,CAAC;QACzB,IAAI,MAAM,EAAE,CAAC;YACX,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC9C,IAAI,CAAC,CAAC,SAAS,KAAK,GAAG,CAAC,EAAE,EAAE,CAAC;oBAC3B,eAAe,GAAG,CAAC,CAAC,SAAS,CAAC;oBAC9B,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE;YAClC,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,UAAU,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE;YAC9B,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,KAAK,EAAE,GAAG,CAAC,cAAc;YACzB,eAAe;SAChB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,2CAA2C;AAC3C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAA0B;IAC3D,MAAM,IAAI,GAAG,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtE,OAAO,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE;QACvC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC;KACtC,CAAC,CAAC;AACL,CAAC;AAED,2BAA2B;AAC3B,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAsB;IACrD,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,YAAY,CAAC,sBAAsB,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED,6CAA6C;AAC7C,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAsB;IAC3D,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,YAAY,CAAC,sBAAsB,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACrB,OAAO,MAAM,CAAC,OAAO,CAAC,sBAAsB,EAAE;YAC5C,SAAS,EAAE,GAAG,CAAC,EAAE;YACjB,MAAM,EAAE,EAAE;SACX,CAAC,CAAC;IACL,CAAC;IACD,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,OAAO,MAAM,CAAC,OAAO,CAAC,sBAAsB,EAAE;QAC5C,SAAS,EAAE,GAAG,CAAC,EAAE;QACjB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACxB,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE;YACjC,SAAS,EAAE,CAAC,CAAC,UAAU;YACvB,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7B,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE;SACjB,CAAC,CACH;KACF,CAAC,CAAC;AACL,CAAC;AAED,mCAAmC;AACnC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAmB;IACvD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,YAAY,CAAC,qBAAqB,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,IAAI,GAAG,YAAY,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE;QACvC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC;KACtC,CAAC,CAAC;AACL,CAAC;AAED,2CAA2C;AAC3C,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,aAAa,CAAC,GAAsB;IACzD,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACjC,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,MAAM,EAAE,CAAC;IAClB,CAAC;AACH,CAAC;AAED,qDAAqD;AACrD,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,SAAS;IAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,kBAAkB,EAAE,CAAC;IAC9C,IAAI,CAAC;QACH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACjC,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,MAAM,EAAE,CAAC;IAClB,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,YAAY;IACjC,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,IAAI,CAAC;QACH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACjC,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,MAAM,EAAE,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { grackle } from "@grackle-ai/common";
|
|
2
|
+
/** Get the value of a setting by key. */
|
|
3
|
+
export declare function getSetting(req: grackle.GetSettingRequest): Promise<grackle.SettingResponse>;
|
|
4
|
+
/** Set the value of a setting. */
|
|
5
|
+
export declare function setSetting(req: grackle.SetSettingRequest): Promise<grackle.SettingResponse>;
|
|
6
|
+
/** Generate a new pairing code for web UI access. */
|
|
7
|
+
export declare function generatePairingCode(): Promise<grackle.PairingCodeResponse>;
|
|
8
|
+
/** Get the current version status (update available, current/latest versions). */
|
|
9
|
+
export declare function getVersionStatus(): Promise<grackle.VersionStatus>;
|
|
10
|
+
//# sourceMappingURL=settings-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-handlers.d.ts","sourceRoot":"","sources":["../src/settings-handlers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAQ7C,yCAAyC;AACzC,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CASjG;AAED,kCAAkC;AAClC,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAuBjG;AAED,qDAAqD;AACrD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAiBhF;AAED,kFAAkF;AAClF,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAQvE"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ConnectError, Code } from "@connectrpc/connect";
|
|
2
|
+
import { create } from "@bufbuild/protobuf";
|
|
3
|
+
import { grackle } from "@grackle-ai/common";
|
|
4
|
+
import { DEFAULT_WEB_PORT } from "@grackle-ai/common";
|
|
5
|
+
import { settingsStore, personaStore, isAllowedSettingKey } from "@grackle-ai/database";
|
|
6
|
+
import { generatePairingCode as authGeneratePairingCode } from "@grackle-ai/auth";
|
|
7
|
+
import { checkVersionStatus } from "./version-check.js";
|
|
8
|
+
import { detectLanIp } from "./utils/network.js";
|
|
9
|
+
import { emit } from "./event-bus.js";
|
|
10
|
+
/** Get the value of a setting by key. */
|
|
11
|
+
export async function getSetting(req) {
|
|
12
|
+
if (!isAllowedSettingKey(req.key)) {
|
|
13
|
+
throw new ConnectError(`Setting key not allowed: ${req.key}`, Code.InvalidArgument);
|
|
14
|
+
}
|
|
15
|
+
const value = settingsStore.getSetting(req.key);
|
|
16
|
+
return create(grackle.SettingResponseSchema, {
|
|
17
|
+
key: req.key,
|
|
18
|
+
value: value ?? "",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/** Set the value of a setting. */
|
|
22
|
+
export async function setSetting(req) {
|
|
23
|
+
if (!isAllowedSettingKey(req.key)) {
|
|
24
|
+
throw new ConnectError(`Setting key not allowed: ${req.key}`, Code.InvalidArgument);
|
|
25
|
+
}
|
|
26
|
+
// Validate persona exists and has required fields when setting default_persona_id
|
|
27
|
+
if (req.key === "default_persona_id" && req.value) {
|
|
28
|
+
const persona = personaStore.getPersona(req.value);
|
|
29
|
+
if (!persona) {
|
|
30
|
+
throw new ConnectError(`Persona not found: ${req.value}`, Code.NotFound);
|
|
31
|
+
}
|
|
32
|
+
if (!persona.runtime || !persona.model) {
|
|
33
|
+
throw new ConnectError(`Persona "${persona.name}" must have runtime and model configured`, Code.FailedPrecondition);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
settingsStore.setSetting(req.key, req.value);
|
|
37
|
+
emit("setting.changed", { key: req.key, value: req.value });
|
|
38
|
+
return create(grackle.SettingResponseSchema, {
|
|
39
|
+
key: req.key,
|
|
40
|
+
value: req.value,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** Generate a new pairing code for web UI access. */
|
|
44
|
+
export async function generatePairingCode() {
|
|
45
|
+
const code = authGeneratePairingCode();
|
|
46
|
+
if (!code) {
|
|
47
|
+
throw new ConnectError("Maximum active pairing codes reached. Wait for existing codes to expire.", Code.ResourceExhausted);
|
|
48
|
+
}
|
|
49
|
+
const webPort = parseInt(process.env.GRACKLE_WEB_PORT || String(DEFAULT_WEB_PORT), 10);
|
|
50
|
+
const bindHost = process.env.GRACKLE_HOST || "127.0.0.1";
|
|
51
|
+
const WILDCARD_ADDRESSES = new Set(["0.0.0.0", "::", "0:0:0:0:0:0:0:0"]);
|
|
52
|
+
const pairingHost = WILDCARD_ADDRESSES.has(bindHost)
|
|
53
|
+
? (detectLanIp() || "localhost")
|
|
54
|
+
: (bindHost === "127.0.0.1" || bindHost === "::1" ? "localhost" : bindHost);
|
|
55
|
+
const url = `http://${pairingHost}:${webPort}/pair?code=${code}`;
|
|
56
|
+
return create(grackle.PairingCodeResponseSchema, { code, url });
|
|
57
|
+
}
|
|
58
|
+
/** Get the current version status (update available, current/latest versions). */
|
|
59
|
+
export async function getVersionStatus() {
|
|
60
|
+
const status = await checkVersionStatus();
|
|
61
|
+
return create(grackle.VersionStatusSchema, {
|
|
62
|
+
currentVersion: status.currentVersion,
|
|
63
|
+
latestVersion: status.latestVersion,
|
|
64
|
+
updateAvailable: status.updateAvailable,
|
|
65
|
+
isDocker: status.isDocker,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=settings-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-handlers.js","sourceRoot":"","sources":["../src/settings-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sigchld.d.ts","sourceRoot":"","sources":["../../src/signals/sigchld.ts"],"names":[],"mappings":"AAwCA,wBAAgB,qBAAqB,IAAI,IAAI,CA2B5C;
|
|
1
|
+
{"version":3,"file":"sigchld.d.ts","sourceRoot":"","sources":["../../src/signals/sigchld.ts"],"names":[],"mappings":"AAwCA,wBAAgB,qBAAqB,IAAI,IAAI,CA2B5C;AAqHD;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC"}
|
package/dist/signals/sigchld.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SESSION_STATUS } from "@grackle-ai/common";
|
|
2
2
|
import { subscribe } from "../event-bus.js";
|
|
3
3
|
import { taskStore, sessionStore } from "@grackle-ai/database";
|
|
4
|
-
import {
|
|
4
|
+
import { readLastTextEntry } from "../log-writer.js";
|
|
5
5
|
import { deliverSignalToTask } from "./signal-delivery.js";
|
|
6
6
|
import { logger } from "../logger.js";
|
|
7
7
|
/** Maximum length for the child's last text message in the notification. */
|
|
@@ -137,6 +137,8 @@ function pruneDelivered(now) {
|
|
|
137
137
|
}
|
|
138
138
|
/**
|
|
139
139
|
* Read the session log and extract the content of the last "text" entry.
|
|
140
|
+
* Uses readLastTextEntry which only reads the tail of the file (up to 64 KB)
|
|
141
|
+
* instead of parsing the entire log into memory.
|
|
140
142
|
* Returns an empty string if no text entries exist or the log cannot be read.
|
|
141
143
|
*/
|
|
142
144
|
function extractLastTextMessage(logPath) {
|
|
@@ -144,13 +146,7 @@ function extractLastTextMessage(logPath) {
|
|
|
144
146
|
return "";
|
|
145
147
|
}
|
|
146
148
|
try {
|
|
147
|
-
|
|
148
|
-
for (let i = entries.length - 1; i >= 0; i--) {
|
|
149
|
-
if (entries[i].type === "text") {
|
|
150
|
-
return entries[i].content;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return "";
|
|
149
|
+
return readLastTextEntry(logPath)?.content ?? "";
|
|
154
150
|
}
|
|
155
151
|
catch {
|
|
156
152
|
return "";
|