@bytespell/amux 0.0.1 → 0.0.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.
@@ -0,0 +1,333 @@
1
+ import {
2
+ agentManager,
3
+ getStressRuntimeConfig,
4
+ setStressRuntimeConfig
5
+ } from "./chunk-RSLSN7F2.js";
6
+ import {
7
+ clearEventsForSession
8
+ } from "./chunk-SX7NC3ZM.js";
9
+ import {
10
+ filesRouter,
11
+ publicProcedure,
12
+ router
13
+ } from "./chunk-L4DBPVMA.js";
14
+ import {
15
+ agentConfigs,
16
+ appState,
17
+ db,
18
+ getDbPath,
19
+ getStartupCwd,
20
+ sessions
21
+ } from "./chunk-OQ5K5ON2.js";
22
+
23
+ // src/server.ts
24
+ import express from "express";
25
+ import cors from "cors";
26
+ import { createServer } from "http";
27
+ import { WebSocketServer } from "ws";
28
+ import { createExpressMiddleware } from "@trpc/server/adapters/express";
29
+ import { applyWSSHandler } from "@trpc/server/adapters/ws";
30
+
31
+ // src/trpc/sessions.ts
32
+ import { z } from "zod";
33
+ import { eq } from "drizzle-orm";
34
+ import { randomUUID } from "crypto";
35
+ var sessionsRouter = router({
36
+ /**
37
+ * List all sessions with agent configs.
38
+ * Note: No activeSessionId - that's a UI concern (Shella).
39
+ */
40
+ list: publicProcedure.query(async () => {
41
+ const allSessions = db.select().from(sessions).orderBy(sessions.createdAt).all();
42
+ const configs = db.select().from(agentConfigs).all();
43
+ const lastUsedRow = db.select().from(appState).where(eq(appState.key, "last_used_agent_config_id")).get();
44
+ const acpConfigs = configs.filter((c) => c.streamType === "acp");
45
+ return {
46
+ sessions: allSessions,
47
+ agentConfigs: configs,
48
+ lastUsedAgentConfigId: lastUsedRow?.value ?? acpConfigs[0]?.id ?? null
49
+ };
50
+ }),
51
+ /**
52
+ * Get a single session by ID.
53
+ */
54
+ get: publicProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => {
55
+ const session = db.select().from(sessions).where(eq(sessions.id, input.id)).get();
56
+ if (!session) {
57
+ throw new Error(`Session ${input.id} not found`);
58
+ }
59
+ return session;
60
+ }),
61
+ /**
62
+ * Create a new session.
63
+ * Note: No title - that's a UI concern (Shella).
64
+ */
65
+ create: publicProcedure.input(z.object({
66
+ id: z.string().optional(),
67
+ // Client can provide ID for optimistic updates
68
+ directory: z.string().optional(),
69
+ agentConfigId: z.string().optional()
70
+ })).mutation(async ({ input }) => {
71
+ const allConfigs = db.select().from(agentConfigs).all();
72
+ let configId = input.agentConfigId;
73
+ if (!configId) {
74
+ const lastUsedRow = db.select().from(appState).where(eq(appState.key, "last_used_agent_config_id")).get();
75
+ if (lastUsedRow?.value) {
76
+ const config = allConfigs.find((c) => c.id === lastUsedRow.value && c.streamType === "acp");
77
+ if (config) configId = config.id;
78
+ }
79
+ if (!configId) {
80
+ const firstAcp = allConfigs.find((c) => c.streamType === "acp");
81
+ if (!firstAcp) {
82
+ throw new Error("No agent configs available");
83
+ }
84
+ configId = firstAcp.id;
85
+ }
86
+ } else {
87
+ const config = allConfigs.find((c) => c.id === configId);
88
+ if (config?.streamType === "acp") {
89
+ db.insert(appState).values({ key: "last_used_agent_config_id", value: configId }).onConflictDoUpdate({ target: appState.key, set: { value: configId } }).run();
90
+ }
91
+ }
92
+ const id = input.id ?? randomUUID();
93
+ const now = /* @__PURE__ */ new Date();
94
+ db.insert(sessions).values({
95
+ id,
96
+ directory: input.directory ?? getStartupCwd(),
97
+ agentConfigId: configId,
98
+ acpSessionId: null,
99
+ model: null,
100
+ mode: null,
101
+ createdAt: now
102
+ }).run();
103
+ return db.select().from(sessions).where(eq(sessions.id, id)).get();
104
+ }),
105
+ /**
106
+ * Update a session.
107
+ * Note: No title/hasCustomTitle/lastAccessedAt - those are UI concerns (Shella).
108
+ */
109
+ update: publicProcedure.input(z.object({
110
+ id: z.string(),
111
+ directory: z.string().optional(),
112
+ agentConfigId: z.string().optional(),
113
+ acpSessionId: z.string().nullable().optional(),
114
+ model: z.string().nullable().optional(),
115
+ mode: z.string().nullable().optional()
116
+ })).mutation(async ({ input }) => {
117
+ const { id, ...updates } = input;
118
+ db.update(sessions).set(updates).where(eq(sessions.id, id)).run();
119
+ if (input.agentConfigId) {
120
+ const config = db.select().from(agentConfigs).where(eq(agentConfigs.id, input.agentConfigId)).get();
121
+ if (config?.streamType === "acp") {
122
+ db.insert(appState).values({ key: "last_used_agent_config_id", value: input.agentConfigId }).onConflictDoUpdate({ target: appState.key, set: { value: input.agentConfigId } }).run();
123
+ }
124
+ }
125
+ return db.select().from(sessions).where(eq(sessions.id, id)).get();
126
+ }),
127
+ /**
128
+ * Delete a session.
129
+ * Note: No active session management - that's a UI concern (Shella).
130
+ */
131
+ delete: publicProcedure.input(z.object({ id: z.string() })).mutation(async ({ input }) => {
132
+ await agentManager.stopForSession(input.id);
133
+ clearEventsForSession(input.id);
134
+ db.delete(sessions).where(eq(sessions.id, input.id)).run();
135
+ return { ok: true };
136
+ })
137
+ });
138
+
139
+ // src/trpc/agents.ts
140
+ import { z as z2 } from "zod";
141
+ import { observable } from "@trpc/server/observable";
142
+ import { eq as eq2 } from "drizzle-orm";
143
+
144
+ // src/lib/shutdown.ts
145
+ var isShuttingDown = false;
146
+ function setShuttingDown(value) {
147
+ isShuttingDown = value;
148
+ }
149
+
150
+ // src/trpc/agents.ts
151
+ var sessionIdInput = z2.object({
152
+ sessionId: z2.string()
153
+ });
154
+ var agentsRouter = router({
155
+ start: publicProcedure.input(sessionIdInput).mutation(async ({ input }) => {
156
+ if (isShuttingDown) {
157
+ throw new Error("Server is shutting down");
158
+ }
159
+ return agentManager.startForSession(input.sessionId);
160
+ }),
161
+ stop: publicProcedure.input(sessionIdInput).mutation(async ({ input }) => {
162
+ await agentManager.stopForSession(input.sessionId);
163
+ return { ok: true };
164
+ }),
165
+ switchAgent: publicProcedure.input(z2.object({
166
+ sessionId: z2.string(),
167
+ agentConfigId: z2.string()
168
+ })).mutation(async ({ input }) => {
169
+ const { sessionId, agentConfigId } = input;
170
+ await agentManager.stopForSession(sessionId);
171
+ clearEventsForSession(sessionId);
172
+ db.update(sessions).set({
173
+ agentConfigId,
174
+ acpSessionId: null,
175
+ model: null,
176
+ mode: null
177
+ }).where(eq2(sessions.id, sessionId)).run();
178
+ db.insert(appState).values({ key: "last_used_agent_config_id", value: agentConfigId }).onConflictDoUpdate({ target: appState.key, set: { value: agentConfigId } }).run();
179
+ return { ok: true };
180
+ }),
181
+ prompt: publicProcedure.input(z2.object({
182
+ sessionId: z2.string(),
183
+ message: z2.string()
184
+ })).mutation(async ({ input }) => {
185
+ await agentManager.prompt(input.sessionId, input.message);
186
+ return { ok: true };
187
+ }),
188
+ cancel: publicProcedure.input(sessionIdInput).mutation(async ({ input }) => {
189
+ await agentManager.cancel(input.sessionId);
190
+ return { ok: true };
191
+ }),
192
+ setMode: publicProcedure.input(z2.object({
193
+ sessionId: z2.string(),
194
+ modeId: z2.string()
195
+ })).mutation(async ({ input }) => {
196
+ await agentManager.setMode(input.sessionId, input.modeId);
197
+ return { ok: true };
198
+ }),
199
+ setModel: publicProcedure.input(z2.object({
200
+ sessionId: z2.string(),
201
+ modelId: z2.string()
202
+ })).mutation(async ({ input }) => {
203
+ await agentManager.setModel(input.sessionId, input.modelId);
204
+ return { ok: true };
205
+ }),
206
+ respondPermission: publicProcedure.input(z2.object({
207
+ sessionId: z2.string(),
208
+ requestId: z2.string(),
209
+ optionId: z2.string()
210
+ })).mutation(({ input }) => {
211
+ agentManager.respondPermission(input.sessionId, input.requestId, input.optionId);
212
+ return { ok: true };
213
+ }),
214
+ subscribe: publicProcedure.input(sessionIdInput).subscription(({ input }) => {
215
+ return observable((emit) => {
216
+ const handler = (event) => {
217
+ if (event.sessionId === input.sessionId) {
218
+ emit.next(event.update);
219
+ }
220
+ };
221
+ agentManager.on("update", handler);
222
+ return () => agentManager.off("update", handler);
223
+ });
224
+ }),
225
+ subscribeAll: publicProcedure.subscription(() => {
226
+ return observable((emit) => {
227
+ const handler = (event) => emit.next(event);
228
+ agentManager.on("update", handler);
229
+ return () => agentManager.off("update", handler);
230
+ });
231
+ }),
232
+ // Stress testing config
233
+ setStressConfig: publicProcedure.input(z2.object({
234
+ turnDelay: z2.number().min(0).max(60).optional(),
235
+ eventsPerTurn: z2.number().min(10).max(500).optional(),
236
+ eventDelay: z2.number().min(0).max(200).optional(),
237
+ chunkSize: z2.number().min(5).max(100).optional()
238
+ })).mutation(({ input }) => {
239
+ setStressRuntimeConfig(input);
240
+ return { ok: true };
241
+ }),
242
+ getStressConfig: publicProcedure.query(() => {
243
+ return getStressRuntimeConfig();
244
+ })
245
+ });
246
+
247
+ // src/trpc/agentConfigs.ts
248
+ import { z as z3 } from "zod";
249
+ import { eq as eq3 } from "drizzle-orm";
250
+ import { randomUUID as randomUUID2 } from "crypto";
251
+ var agentConfigsRouter = router({
252
+ list: publicProcedure.query(async () => {
253
+ return db.select().from(agentConfigs).all();
254
+ }),
255
+ get: publicProcedure.input(z3.object({ id: z3.string() })).query(async ({ input }) => {
256
+ return db.select().from(agentConfigs).where(eq3(agentConfigs.id, input.id)).get();
257
+ }),
258
+ create: publicProcedure.input(z3.object({
259
+ name: z3.string(),
260
+ command: z3.string(),
261
+ args: z3.array(z3.string()).default([]),
262
+ env: z3.record(z3.string()).default({})
263
+ })).mutation(async ({ input }) => {
264
+ const id = randomUUID2();
265
+ db.insert(agentConfigs).values({ id, ...input }).run();
266
+ return db.select().from(agentConfigs).where(eq3(agentConfigs.id, id)).get();
267
+ }),
268
+ update: publicProcedure.input(z3.object({
269
+ id: z3.string(),
270
+ name: z3.string().optional(),
271
+ command: z3.string().optional(),
272
+ args: z3.array(z3.string()).optional(),
273
+ env: z3.record(z3.string()).optional()
274
+ })).mutation(async ({ input }) => {
275
+ const { id, ...updates } = input;
276
+ db.update(agentConfigs).set(updates).where(eq3(agentConfigs.id, id)).run();
277
+ return db.select().from(agentConfigs).where(eq3(agentConfigs.id, id)).get();
278
+ }),
279
+ delete: publicProcedure.input(z3.object({ id: z3.string() })).mutation(async ({ input }) => {
280
+ db.delete(agentConfigs).where(eq3(agentConfigs.id, input.id)).run();
281
+ return { ok: true };
282
+ })
283
+ });
284
+
285
+ // src/trpc/router.ts
286
+ var appRouter = router({
287
+ sessions: sessionsRouter,
288
+ agents: agentsRouter,
289
+ agentConfigs: agentConfigsRouter,
290
+ files: filesRouter
291
+ });
292
+
293
+ // src/server.ts
294
+ function createAmuxServer(options = {}) {
295
+ const app = express();
296
+ app.use(cors());
297
+ app.use(express.json());
298
+ app.use("/trpc", createExpressMiddleware({ router: appRouter }));
299
+ app.get("/health", (_req, res) => res.json({ status: "ok", dbPath: getDbPath() }));
300
+ const server = createServer(app);
301
+ const wss = new WebSocketServer({ server, path: "/trpc" });
302
+ applyWSSHandler({ wss, router: appRouter });
303
+ return {
304
+ app,
305
+ server,
306
+ wss,
307
+ start: (port) => {
308
+ return new Promise((resolve) => {
309
+ server.listen(port, () => {
310
+ console.log(`[amux] Running on http://localhost:${port}`);
311
+ console.log(`[amux] WebSocket at ws://localhost:${port}/trpc`);
312
+ console.log(`[amux] Database at ${getDbPath()}`);
313
+ options.onReady?.(port);
314
+ resolve();
315
+ });
316
+ });
317
+ },
318
+ stop: async () => {
319
+ setShuttingDown(true);
320
+ console.log("[amux] Shutting down...");
321
+ wss.close();
322
+ await agentManager.stopAll();
323
+ console.log("[amux] Agents stopped");
324
+ server.close();
325
+ }
326
+ };
327
+ }
328
+
329
+ export {
330
+ appRouter,
331
+ createAmuxServer
332
+ };
333
+ //# sourceMappingURL=chunk-KUFDA4M3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/trpc/sessions.ts","../src/trpc/agents.ts","../src/lib/shutdown.ts","../src/trpc/agentConfigs.ts","../src/trpc/router.ts"],"sourcesContent":["/**\n * Amux server factory - creates the Express app and WebSocket server\n * for the agent multiplexer API.\n */\n\nimport express, { type Express } from 'express';\nimport cors from 'cors';\nimport { createServer, type Server } from 'http';\nimport { WebSocketServer } from 'ws';\nimport { createExpressMiddleware } from '@trpc/server/adapters/express';\nimport { applyWSSHandler } from '@trpc/server/adapters/ws';\nimport { appRouter } from './trpc/router.js';\nimport { getDbPath } from './lib/paths.js';\nimport { agentManager } from './agents/manager.js';\nimport { setShuttingDown } from './lib/shutdown.js';\n\n// Import db to trigger initialization\nimport './db/index.js';\n\nexport interface AmuxServer {\n app: Express;\n server: Server;\n wss: WebSocketServer;\n start: (port: number) => Promise<void>;\n stop: () => Promise<void>;\n}\n\nexport interface CreateServerOptions {\n /** Called when server is ready */\n onReady?: (port: number) => void;\n}\n\n/**\n * Creates an Amux server instance.\n *\n * Usage:\n * ```ts\n * const amux = createAmuxServer();\n * await amux.start(3078);\n * ```\n *\n * Or mount in existing Express app:\n * ```ts\n * const amux = createAmuxServer();\n * myApp.use('/api', amux.app);\n * ```\n */\nexport function createAmuxServer(options: CreateServerOptions = {}): AmuxServer {\n const app = express();\n\n app.use(cors());\n app.use(express.json());\n\n // tRPC HTTP endpoint\n app.use('/trpc', createExpressMiddleware({ router: appRouter }));\n\n // Health check\n app.get('/health', (_req, res) => res.json({ status: 'ok', dbPath: getDbPath() }));\n\n const server = createServer(app);\n\n // WebSocket for subscriptions\n const wss = new WebSocketServer({ server, path: '/trpc' });\n applyWSSHandler({ wss, router: appRouter });\n\n return {\n app,\n server,\n wss,\n\n start: (port: number) => {\n return new Promise((resolve) => {\n server.listen(port, () => {\n console.log(`[amux] Running on http://localhost:${port}`);\n console.log(`[amux] WebSocket at ws://localhost:${port}/trpc`);\n console.log(`[amux] Database at ${getDbPath()}`);\n options.onReady?.(port);\n resolve();\n });\n });\n },\n\n stop: async () => {\n setShuttingDown(true);\n console.log('[amux] Shutting down...');\n\n // Close WebSocket server immediately to prevent reconnects\n wss.close();\n\n // Stop all agent processes\n await agentManager.stopAll();\n console.log('[amux] Agents stopped');\n\n server.close();\n },\n };\n}\n\n// Re-export router type for consumers\nexport { appRouter, type AppRouter } from './trpc/router.js';\n","import { z } from 'zod';\nimport { router, publicProcedure } from './trpc.js';\nimport { db } from '../db/index.js';\nimport { sessions, appState, agentConfigs } from '../db/schema.js';\nimport { eq } from 'drizzle-orm';\nimport { randomUUID } from 'crypto';\nimport { getStartupCwd } from '../lib/paths.js';\nimport { agentManager } from '../agents/manager.js';\nimport { clearEventsForSession } from '../agents/eventStore.js';\n\nexport const sessionsRouter = router({\n /**\n * List all sessions with agent configs.\n * Note: No activeSessionId - that's a UI concern (Shella).\n */\n list: publicProcedure.query(async () => {\n const allSessions = db.select().from(sessions).orderBy(sessions.createdAt).all();\n const configs = db.select().from(agentConfigs).all();\n const lastUsedRow = db.select().from(appState).where(eq(appState.key, 'last_used_agent_config_id')).get();\n // Default to first acp agent (not pty terminals)\n const acpConfigs = configs.filter((c) => c.streamType === 'acp');\n return {\n sessions: allSessions,\n agentConfigs: configs,\n lastUsedAgentConfigId: lastUsedRow?.value ?? acpConfigs[0]?.id ?? null,\n };\n }),\n\n /**\n * Get a single session by ID.\n */\n get: publicProcedure\n .input(z.object({ id: z.string() }))\n .query(async ({ input }) => {\n const session = db.select().from(sessions).where(eq(sessions.id, input.id)).get();\n if (!session) {\n throw new Error(`Session ${input.id} not found`);\n }\n return session;\n }),\n\n /**\n * Create a new session.\n * Note: No title - that's a UI concern (Shella).\n */\n create: publicProcedure\n .input(z.object({\n id: z.string().optional(), // Client can provide ID for optimistic updates\n directory: z.string().optional(),\n agentConfigId: z.string().optional(),\n }))\n .mutation(async ({ input }) => {\n // Get agent config - use provided, or last used, or first available acp agent\n const allConfigs = db.select().from(agentConfigs).all();\n let configId = input.agentConfigId;\n if (!configId) {\n // Try last used first (must be acp type)\n const lastUsedRow = db.select().from(appState).where(eq(appState.key, 'last_used_agent_config_id')).get();\n if (lastUsedRow?.value) {\n const config = allConfigs.find((c) => c.id === lastUsedRow.value && c.streamType === 'acp');\n if (config) configId = config.id;\n }\n // Fall back to first acp agent\n if (!configId) {\n const firstAcp = allConfigs.find((c) => c.streamType === 'acp');\n if (!firstAcp) {\n throw new Error('No agent configs available');\n }\n configId = firstAcp.id;\n }\n } else {\n // User explicitly selected an agent - save as last used if it's acp\n const config = allConfigs.find((c) => c.id === configId);\n if (config?.streamType === 'acp') {\n db.insert(appState)\n .values({ key: 'last_used_agent_config_id', value: configId })\n .onConflictDoUpdate({ target: appState.key, set: { value: configId } })\n .run();\n }\n }\n\n // Use client-provided ID or generate new one\n const id = input.id ?? randomUUID();\n const now = new Date();\n db.insert(sessions).values({\n id,\n directory: input.directory ?? getStartupCwd(),\n agentConfigId: configId,\n acpSessionId: null,\n model: null,\n mode: null,\n createdAt: now,\n }).run();\n return db.select().from(sessions).where(eq(sessions.id, id)).get()!;\n }),\n\n /**\n * Update a session.\n * Note: No title/hasCustomTitle/lastAccessedAt - those are UI concerns (Shella).\n */\n update: publicProcedure\n .input(z.object({\n id: z.string(),\n directory: z.string().optional(),\n agentConfigId: z.string().optional(),\n acpSessionId: z.string().nullable().optional(),\n model: z.string().nullable().optional(),\n mode: z.string().nullable().optional(),\n }))\n .mutation(async ({ input }) => {\n const { id, ...updates } = input;\n db.update(sessions)\n .set(updates)\n .where(eq(sessions.id, id))\n .run();\n // Save as last used when agent is changed (only for acp agents)\n if (input.agentConfigId) {\n const config = db.select().from(agentConfigs).where(eq(agentConfigs.id, input.agentConfigId)).get();\n if (config?.streamType === 'acp') {\n db.insert(appState)\n .values({ key: 'last_used_agent_config_id', value: input.agentConfigId })\n .onConflictDoUpdate({ target: appState.key, set: { value: input.agentConfigId } })\n .run();\n }\n }\n return db.select().from(sessions).where(eq(sessions.id, id)).get()!;\n }),\n\n /**\n * Delete a session.\n * Note: No active session management - that's a UI concern (Shella).\n */\n delete: publicProcedure\n .input(z.object({ id: z.string() }))\n .mutation(async ({ input }) => {\n // Stop agent and clear events\n await agentManager.stopForSession(input.id);\n clearEventsForSession(input.id);\n\n // Delete session\n db.delete(sessions).where(eq(sessions.id, input.id)).run();\n\n return { ok: true };\n }),\n});\n","import { z } from 'zod';\nimport { observable } from '@trpc/server/observable';\nimport { eq } from 'drizzle-orm';\nimport { router, publicProcedure } from './trpc.js';\nimport { agentManager, type SessionEvent } from '../agents/manager.js';\nimport { clearEventsForSession } from '../agents/eventStore.js';\nimport { db } from '../db/index.js';\nimport { sessions, appState } from '../db/schema.js';\nimport type { WindowUpdate } from '../types.js';\nimport { getStressRuntimeConfig, setStressRuntimeConfig } from '../stress/config.js';\nimport { isShuttingDown } from '../lib/shutdown.js';\n\nconst sessionIdInput = z.object({\n sessionId: z.string(),\n});\n\nexport const agentsRouter = router({\n start: publicProcedure\n .input(sessionIdInput)\n .mutation(async ({ input }) => {\n if (isShuttingDown) {\n throw new Error('Server is shutting down');\n }\n return agentManager.startForSession(input.sessionId);\n }),\n\n stop: publicProcedure\n .input(sessionIdInput)\n .mutation(async ({ input }) => {\n await agentManager.stopForSession(input.sessionId);\n return { ok: true };\n }),\n\n switchAgent: publicProcedure\n .input(z.object({\n sessionId: z.string(),\n agentConfigId: z.string(),\n }))\n .mutation(async ({ input }) => {\n const { sessionId, agentConfigId } = input;\n\n // Stop old agent if running\n await agentManager.stopForSession(sessionId);\n\n // Clear stored events (new agent = fresh history)\n clearEventsForSession(sessionId);\n\n // Update session config\n db.update(sessions)\n .set({\n agentConfigId,\n acpSessionId: null,\n model: null,\n mode: null,\n })\n .where(eq(sessions.id, sessionId))\n .run();\n\n // Save as last used\n db.insert(appState)\n .values({ key: 'last_used_agent_config_id', value: agentConfigId })\n .onConflictDoUpdate({ target: appState.key, set: { value: agentConfigId } })\n .run();\n\n return { ok: true };\n }),\n\n prompt: publicProcedure\n .input(z.object({\n sessionId: z.string(),\n message: z.string(),\n }))\n .mutation(async ({ input }) => {\n await agentManager.prompt(input.sessionId, input.message);\n return { ok: true };\n }),\n\n cancel: publicProcedure\n .input(sessionIdInput)\n .mutation(async ({ input }) => {\n await agentManager.cancel(input.sessionId);\n return { ok: true };\n }),\n\n setMode: publicProcedure\n .input(z.object({\n sessionId: z.string(),\n modeId: z.string(),\n }))\n .mutation(async ({ input }) => {\n await agentManager.setMode(input.sessionId, input.modeId);\n return { ok: true };\n }),\n\n setModel: publicProcedure\n .input(z.object({\n sessionId: z.string(),\n modelId: z.string(),\n }))\n .mutation(async ({ input }) => {\n await agentManager.setModel(input.sessionId, input.modelId);\n return { ok: true };\n }),\n\n respondPermission: publicProcedure\n .input(z.object({\n sessionId: z.string(),\n requestId: z.string(),\n optionId: z.string(),\n }))\n .mutation(({ input }) => {\n agentManager.respondPermission(input.sessionId, input.requestId, input.optionId);\n return { ok: true };\n }),\n\n subscribe: publicProcedure\n .input(sessionIdInput)\n .subscription(({ input }) => {\n return observable<WindowUpdate>((emit) => {\n // Just forward events - replay happens in startForSession\n const handler = (event: SessionEvent) => {\n if (event.sessionId === input.sessionId) {\n emit.next(event.update);\n }\n };\n agentManager.on('update', handler);\n return () => agentManager.off('update', handler);\n });\n }),\n\n subscribeAll: publicProcedure.subscription(() => {\n return observable<SessionEvent>((emit) => {\n const handler = (event: SessionEvent) => emit.next(event);\n agentManager.on('update', handler);\n return () => agentManager.off('update', handler);\n });\n }),\n\n // Stress testing config\n setStressConfig: publicProcedure\n .input(z.object({\n turnDelay: z.number().min(0).max(60).optional(),\n eventsPerTurn: z.number().min(10).max(500).optional(),\n eventDelay: z.number().min(0).max(200).optional(),\n chunkSize: z.number().min(5).max(100).optional(),\n }))\n .mutation(({ input }) => {\n setStressRuntimeConfig(input);\n return { ok: true };\n }),\n\n getStressConfig: publicProcedure.query(() => {\n return getStressRuntimeConfig();\n }),\n});\n","// Shutdown state - separate module to avoid circular dependencies\nexport let isShuttingDown = false;\n\nexport function setShuttingDown(value: boolean) {\n isShuttingDown = value;\n}\n","import { z } from 'zod';\nimport { router, publicProcedure } from './trpc.js';\nimport { db } from '../db/index.js';\nimport { agentConfigs } from '../db/schema.js';\nimport { eq } from 'drizzle-orm';\nimport { randomUUID } from 'crypto';\n\nexport const agentConfigsRouter = router({\n list: publicProcedure.query(async () => {\n return db.select().from(agentConfigs).all();\n }),\n\n get: publicProcedure\n .input(z.object({ id: z.string() }))\n .query(async ({ input }) => {\n return db.select().from(agentConfigs).where(eq(agentConfigs.id, input.id)).get();\n }),\n\n create: publicProcedure\n .input(z.object({\n name: z.string(),\n command: z.string(),\n args: z.array(z.string()).default([]),\n env: z.record(z.string()).default({}),\n }))\n .mutation(async ({ input }) => {\n const id = randomUUID();\n db.insert(agentConfigs).values({ id, ...input }).run();\n return db.select().from(agentConfigs).where(eq(agentConfigs.id, id)).get()!;\n }),\n\n update: publicProcedure\n .input(z.object({\n id: z.string(),\n name: z.string().optional(),\n command: z.string().optional(),\n args: z.array(z.string()).optional(),\n env: z.record(z.string()).optional(),\n }))\n .mutation(async ({ input }) => {\n const { id, ...updates } = input;\n db.update(agentConfigs).set(updates).where(eq(agentConfigs.id, id)).run();\n return db.select().from(agentConfigs).where(eq(agentConfigs.id, id)).get()!;\n }),\n\n delete: publicProcedure\n .input(z.object({ id: z.string() }))\n .mutation(async ({ input }) => {\n db.delete(agentConfigs).where(eq(agentConfigs.id, input.id)).run();\n return { ok: true };\n }),\n});\n","import { router } from './trpc.js';\nimport { sessionsRouter } from './sessions.js';\nimport { agentsRouter } from './agents.js';\nimport { agentConfigsRouter } from './agentConfigs.js';\nimport { filesRouter } from './files.js';\n\nexport const appRouter = router({\n sessions: sessionsRouter,\n agents: agentsRouter,\n agentConfigs: agentConfigsRouter,\n files: filesRouter,\n});\n\nexport type AppRouter = typeof appRouter;\n"],"mappings":"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","names":["z","eq","z","eq","z","eq","randomUUID","z"]}
@@ -0,0 +1,122 @@
1
+ import {
2
+ db,
3
+ sessions
4
+ } from "./chunk-OQ5K5ON2.js";
5
+
6
+ // src/trpc/files.ts
7
+ import { z } from "zod";
8
+
9
+ // src/trpc/trpc.ts
10
+ import { initTRPC } from "@trpc/server";
11
+ var t = initTRPC.create();
12
+ var router = t.router;
13
+ var publicProcedure = t.procedure;
14
+
15
+ // src/trpc/files.ts
16
+ import { eq } from "drizzle-orm";
17
+ import * as fs from "fs/promises";
18
+ import * as path from "path";
19
+ import { glob } from "glob";
20
+ async function listFilesForAutocomplete(workingDir, partialPath, limit) {
21
+ const normalized = partialPath.startsWith("./") ? partialPath.slice(2) : partialPath;
22
+ if (normalized.includes("/")) {
23
+ return listFilesPrefix(workingDir, normalized, limit);
24
+ }
25
+ return listFilesFuzzy(workingDir, normalized, limit);
26
+ }
27
+ async function listFilesPrefix(workingDir, partialPath, limit) {
28
+ const lastSlash = partialPath.lastIndexOf("/");
29
+ const dirPart = lastSlash >= 0 ? partialPath.slice(0, lastSlash) : "";
30
+ const prefix = lastSlash >= 0 ? partialPath.slice(lastSlash + 1) : partialPath;
31
+ const targetDir = path.resolve(workingDir, dirPart);
32
+ const normalizedWorkingDir = path.resolve(workingDir);
33
+ const normalizedTargetDir = path.resolve(targetDir);
34
+ if (!normalizedTargetDir.startsWith(normalizedWorkingDir)) {
35
+ return [];
36
+ }
37
+ try {
38
+ const entries = await fs.readdir(targetDir, { withFileTypes: true });
39
+ const matching = entries.filter((e) => !e.name.startsWith(".")).filter((e) => prefix === "" || e.name.toLowerCase().startsWith(prefix.toLowerCase())).slice(0, limit * 2).map((e) => ({
40
+ name: e.name,
41
+ path: dirPart ? `${dirPart}/${e.name}` : e.name,
42
+ isDirectory: e.isDirectory()
43
+ }));
44
+ matching.sort((a, b) => {
45
+ if (a.isDirectory !== b.isDirectory) {
46
+ return a.isDirectory ? -1 : 1;
47
+ }
48
+ return a.name.localeCompare(b.name);
49
+ });
50
+ return matching.slice(0, limit);
51
+ } catch {
52
+ return [];
53
+ }
54
+ }
55
+ async function listFilesFuzzy(workingDir, query, limit) {
56
+ if (!query.trim()) return [];
57
+ try {
58
+ const pattern = `{*${query}*,**/*${query}*}`;
59
+ const matches = await glob(pattern, {
60
+ cwd: workingDir,
61
+ nodir: false,
62
+ dot: false,
63
+ // Skip hidden files
64
+ ignore: ["**/node_modules/**", "**/.git/**"],
65
+ maxDepth: 10
66
+ });
67
+ const results = [];
68
+ for (const match of matches.slice(0, limit * 2)) {
69
+ try {
70
+ const fullPath = path.join(workingDir, match);
71
+ const stat2 = await fs.stat(fullPath);
72
+ results.push({
73
+ name: path.basename(match),
74
+ path: match,
75
+ isDirectory: stat2.isDirectory()
76
+ });
77
+ } catch {
78
+ }
79
+ }
80
+ results.sort((a, b) => {
81
+ if (a.isDirectory !== b.isDirectory) {
82
+ return a.isDirectory ? -1 : 1;
83
+ }
84
+ const aDepth = a.path.split("/").length;
85
+ const bDepth = b.path.split("/").length;
86
+ if (aDepth !== bDepth) {
87
+ return aDepth - bDepth;
88
+ }
89
+ return a.path.localeCompare(b.path);
90
+ });
91
+ return results.slice(0, limit);
92
+ } catch {
93
+ return [];
94
+ }
95
+ }
96
+ var filesRouter = router({
97
+ /**
98
+ * List files/directories matching a partial path for autocomplete.
99
+ * Returns files in the session's working directory.
100
+ */
101
+ listForAutocomplete: publicProcedure.input(z.object({
102
+ sessionId: z.string(),
103
+ partialPath: z.string(),
104
+ limit: z.number().default(20)
105
+ })).query(async ({ input }) => {
106
+ const session = db.select().from(sessions).where(eq(sessions.id, input.sessionId)).get();
107
+ if (!session) throw new Error(`Session ${input.sessionId} not found`);
108
+ return listFilesForAutocomplete(
109
+ session.directory,
110
+ input.partialPath,
111
+ input.limit
112
+ );
113
+ })
114
+ });
115
+
116
+ export {
117
+ router,
118
+ publicProcedure,
119
+ listFilesForAutocomplete,
120
+ filesRouter
121
+ };
122
+ //# sourceMappingURL=chunk-L4DBPVMA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/trpc/files.ts","../src/trpc/trpc.ts"],"sourcesContent":["import { z } from 'zod';\nimport { router, publicProcedure } from './trpc.js';\nimport { db } from '../db/index.js';\nimport { sessions } from '../db/schema.js';\nimport { eq } from 'drizzle-orm';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport { glob } from 'glob';\n\nexport interface FileEntry {\n name: string; // File/directory name\n path: string; // Full relative path for insertion\n isDirectory: boolean;\n}\n\n/**\n * List files/directories matching a partial path for autocomplete.\n * Uses glob for fuzzy full-text search across the entire project.\n * Exported for direct use by AmuxBridge.\n */\nexport async function listFilesForAutocomplete(\n workingDir: string,\n partialPath: string,\n limit: number\n): Promise<FileEntry[]> {\n // Normalize partial path (strip leading ./)\n const normalized = partialPath.startsWith('./')\n ? partialPath.slice(2)\n : partialPath;\n\n // If query contains a slash, user is navigating into a directory - use prefix match\n if (normalized.includes('/')) {\n return listFilesPrefix(workingDir, normalized, limit);\n }\n\n // Otherwise, do a fuzzy search across the entire project\n return listFilesFuzzy(workingDir, normalized, limit);\n}\n\n/**\n * Prefix-based listing for directory navigation (e.g., \"src/comp\")\n */\nasync function listFilesPrefix(\n workingDir: string,\n partialPath: string,\n limit: number\n): Promise<FileEntry[]> {\n const lastSlash = partialPath.lastIndexOf('/');\n const dirPart = lastSlash >= 0 ? partialPath.slice(0, lastSlash) : '';\n const prefix = lastSlash >= 0 ? partialPath.slice(lastSlash + 1) : partialPath;\n\n const targetDir = path.resolve(workingDir, dirPart);\n\n // Security: ensure we're within working directory\n const normalizedWorkingDir = path.resolve(workingDir);\n const normalizedTargetDir = path.resolve(targetDir);\n if (!normalizedTargetDir.startsWith(normalizedWorkingDir)) {\n return [];\n }\n\n try {\n const entries = await fs.readdir(targetDir, { withFileTypes: true });\n\n const matching = entries\n .filter(e => !e.name.startsWith('.'))\n .filter(e => prefix === '' || e.name.toLowerCase().startsWith(prefix.toLowerCase()))\n .slice(0, limit * 2)\n .map(e => ({\n name: e.name,\n path: dirPart ? `${dirPart}/${e.name}` : e.name,\n isDirectory: e.isDirectory(),\n }));\n\n matching.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) {\n return a.isDirectory ? -1 : 1;\n }\n return a.name.localeCompare(b.name);\n });\n\n return matching.slice(0, limit);\n } catch {\n return [];\n }\n}\n\n/**\n * Fuzzy search across entire project using glob\n */\nasync function listFilesFuzzy(\n workingDir: string,\n query: string,\n limit: number\n): Promise<FileEntry[]> {\n if (!query.trim()) return [];\n\n try {\n // Use glob to find files matching *query* pattern anywhere in the project\n // Use brace expansion to match both root-level files and nested files\n const pattern = `{*${query}*,**/*${query}*}`;\n const matches = await glob(pattern, {\n cwd: workingDir,\n nodir: false,\n dot: false, // Skip hidden files\n ignore: ['**/node_modules/**', '**/.git/**'],\n maxDepth: 10,\n });\n\n // Get file stats to determine if directory\n const results: FileEntry[] = [];\n for (const match of matches.slice(0, limit * 2)) {\n try {\n const fullPath = path.join(workingDir, match);\n const stat = await fs.stat(fullPath);\n results.push({\n name: path.basename(match),\n path: match,\n isDirectory: stat.isDirectory(),\n });\n } catch {\n // Skip files we can't stat\n }\n }\n\n // Sort: directories first, shorter paths first, then alphabetically\n results.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) {\n return a.isDirectory ? -1 : 1;\n }\n // Prefer shorter paths (closer to root)\n const aDepth = a.path.split('/').length;\n const bDepth = b.path.split('/').length;\n if (aDepth !== bDepth) {\n return aDepth - bDepth;\n }\n return a.path.localeCompare(b.path);\n });\n\n return results.slice(0, limit);\n } catch {\n return [];\n }\n}\n\nexport const filesRouter = router({\n /**\n * List files/directories matching a partial path for autocomplete.\n * Returns files in the session's working directory.\n */\n listForAutocomplete: publicProcedure\n .input(z.object({\n sessionId: z.string(),\n partialPath: z.string(),\n limit: z.number().default(20),\n }))\n .query(async ({ input }) => {\n const session = db.select().from(sessions).where(eq(sessions.id, input.sessionId)).get();\n if (!session) throw new Error(`Session ${input.sessionId} not found`);\n\n return listFilesForAutocomplete(\n session.directory,\n input.partialPath,\n input.limit\n );\n }),\n});\n","import { initTRPC } from '@trpc/server';\n\nconst t = initTRPC.create();\n\nexport const router = t.router;\nexport const publicProcedure = t.procedure;\n"],"mappings":"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","names":["stat"]}