@wingman-ai/gateway 0.2.1 → 0.2.3
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/.wingman/agents/README.md +1 -0
- package/.wingman/agents/coding/agent.md +179 -112
- package/.wingman/agents/coding/implementor.md +50 -3
- package/.wingman/agents/main/agent.md +4 -0
- package/README.md +1 -0
- package/dist/agent/config/agentConfig.cjs +30 -1
- package/dist/agent/config/agentConfig.js +30 -1
- package/dist/agent/config/modelFactory.cjs +22 -2
- package/dist/agent/config/modelFactory.d.ts +2 -0
- package/dist/agent/config/modelFactory.js +22 -2
- package/dist/agent/tests/agentConfig.test.cjs +39 -0
- package/dist/agent/tests/agentConfig.test.js +39 -0
- package/dist/agent/tests/modelFactory.test.cjs +12 -5
- package/dist/agent/tests/modelFactory.test.js +12 -5
- package/dist/cli/commands/init.cjs +7 -6
- package/dist/cli/commands/init.js +7 -6
- package/dist/cli/commands/provider.cjs +17 -3
- package/dist/cli/commands/provider.js +17 -3
- package/dist/cli/config/loader.cjs +27 -0
- package/dist/cli/config/loader.js +27 -0
- package/dist/cli/config/schema.cjs +80 -2
- package/dist/cli/config/schema.d.ts +88 -0
- package/dist/cli/config/schema.js +67 -1
- package/dist/cli/core/agentInvoker.cjs +242 -17
- package/dist/cli/core/agentInvoker.d.ts +46 -4
- package/dist/cli/core/agentInvoker.js +214 -13
- package/dist/cli/core/sessionManager.cjs +32 -5
- package/dist/cli/core/sessionManager.js +32 -5
- package/dist/cli/index.cjs +6 -5
- package/dist/cli/index.js +6 -5
- package/dist/cli/types.d.ts +32 -0
- package/dist/gateway/http/sessions.cjs +7 -7
- package/dist/gateway/http/sessions.js +7 -7
- package/dist/gateway/server.cjs +230 -28
- package/dist/gateway/server.d.ts +11 -1
- package/dist/gateway/server.js +230 -28
- package/dist/gateway/types.d.ts +5 -1
- package/dist/gateway/validation.cjs +1 -0
- package/dist/gateway/validation.d.ts +2 -0
- package/dist/gateway/validation.js +1 -0
- package/dist/providers/codex.cjs +167 -0
- package/dist/providers/codex.d.ts +15 -0
- package/dist/providers/codex.js +127 -0
- package/dist/providers/credentials.cjs +8 -0
- package/dist/providers/credentials.js +8 -0
- package/dist/providers/registry.cjs +11 -0
- package/dist/providers/registry.d.ts +1 -1
- package/dist/providers/registry.js +11 -0
- package/dist/tests/agentInvokerSummarization.test.cjs +296 -0
- package/dist/tests/agentInvokerSummarization.test.d.ts +1 -0
- package/dist/tests/agentInvokerSummarization.test.js +290 -0
- package/dist/tests/cli-config-loader.test.cjs +88 -0
- package/dist/tests/cli-config-loader.test.js +88 -0
- package/dist/tests/codex-credentials-precedence.test.cjs +94 -0
- package/dist/tests/codex-credentials-precedence.test.d.ts +1 -0
- package/dist/tests/codex-credentials-precedence.test.js +88 -0
- package/dist/tests/codex-provider.test.cjs +186 -0
- package/dist/tests/codex-provider.test.d.ts +1 -0
- package/dist/tests/codex-provider.test.js +180 -0
- package/dist/tests/gateway.test.cjs +173 -1
- package/dist/tests/gateway.test.js +173 -1
- package/dist/tests/provider-command-codex.test.cjs +57 -0
- package/dist/tests/provider-command-codex.test.d.ts +1 -0
- package/dist/tests/provider-command-codex.test.js +51 -0
- package/dist/tests/sessionStateMessages.test.cjs +38 -0
- package/dist/tests/sessionStateMessages.test.js +38 -0
- package/dist/webui/assets/index-BVMavpud.css +11 -0
- package/dist/webui/assets/index-DCB2aVVf.js +182 -0
- package/dist/webui/index.html +2 -2
- package/package.json +3 -1
- package/dist/webui/assets/index-BytPznA_.css +0 -1
- package/dist/webui/assets/index-u_5qlVip.js +0 -176
package/dist/gateway/server.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { getGatewayTokenFromEnv } from "./env.js";
|
|
6
|
-
import { MDNSDiscoveryService, TailscaleDiscoveryService } from "./discovery/index.js";
|
|
7
|
-
import { createLogger } from "../logger.js";
|
|
1
|
+
import { existsSync, mkdirSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, isAbsolute, join, normalize, sep } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
8
5
|
import { WingmanConfigLoader } from "../cli/config/loader.js";
|
|
9
|
-
import { GatewayRouter } from "./router.js";
|
|
10
|
-
import { OutputManager } from "../cli/core/outputManager.js";
|
|
11
6
|
import { AgentInvoker } from "../cli/core/agentInvoker.js";
|
|
7
|
+
import { OutputManager } from "../cli/core/outputManager.js";
|
|
12
8
|
import { SessionManager } from "../cli/core/sessionManager.js";
|
|
9
|
+
import { createLogger } from "../logger.js";
|
|
10
|
+
import { DiscordGatewayAdapter } from "./adapters/discord.js";
|
|
11
|
+
import { GatewayAuth } from "./auth.js";
|
|
12
|
+
import { BroadcastGroupManager } from "./broadcast.js";
|
|
13
|
+
import { MDNSDiscoveryService, TailscaleDiscoveryService } from "./discovery/index.js";
|
|
14
|
+
import { getGatewayTokenFromEnv } from "./env.js";
|
|
15
|
+
import { InternalHookRegistry } from "./hooks/registry.js";
|
|
13
16
|
import { handleAgentsApi } from "./http/agents.js";
|
|
14
17
|
import { handleFsApi } from "./http/fs.js";
|
|
15
18
|
import { handleProvidersApi } from "./http/providers.js";
|
|
16
|
-
import {
|
|
19
|
+
import { createRoutineStore, handleRoutinesApi } from "./http/routines.js";
|
|
17
20
|
import { handleSessionsApi } from "./http/sessions.js";
|
|
21
|
+
import { handleVoiceApi } from "./http/voice.js";
|
|
18
22
|
import { createWebhookStore, handleWebhookInvoke, handleWebhooksApi } from "./http/webhooks.js";
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import { homedir } from "node:os";
|
|
23
|
-
import { dirname, isAbsolute, join, normalize, sep } from "node:path";
|
|
24
|
-
import { existsSync, mkdirSync, statSync, writeFileSync } from "node:fs";
|
|
25
|
-
import { fileURLToPath } from "node:url";
|
|
23
|
+
import { NodeManager } from "./node.js";
|
|
24
|
+
import { GatewayRouter } from "./router.js";
|
|
25
|
+
import { validateGatewayMessage } from "./validation.js";
|
|
26
26
|
function _define_property(obj, key, value) {
|
|
27
27
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
28
28
|
value: value,
|
|
@@ -218,6 +218,7 @@ class GatewayServer {
|
|
|
218
218
|
const nodeId = ws.data.nodeId;
|
|
219
219
|
if ("connect" === msg.type) return void this.handleConnect(ws, msg);
|
|
220
220
|
if ("req:agent" === msg.type) return void this.handleAgentRequest(ws, msg);
|
|
221
|
+
if ("req:agent:cancel" === msg.type) return void this.handleAgentCancel(ws, msg);
|
|
221
222
|
if (nodeId && "register" !== msg.type && "ping" !== msg.type && "pong" !== msg.type) {
|
|
222
223
|
if (this.nodeManager.isRateLimited(nodeId)) return void this.sendError(ws, "RATE_LIMITED", "Too many messages. Please slow down.");
|
|
223
224
|
this.nodeManager.recordMessage(nodeId);
|
|
@@ -272,6 +273,7 @@ class GatewayServer {
|
|
|
272
273
|
}
|
|
273
274
|
this.connectedClients.delete(ws);
|
|
274
275
|
this.clearSessionSubscriptions(ws);
|
|
276
|
+
this.cancelSocketAgentRequests(ws);
|
|
275
277
|
}
|
|
276
278
|
handleDrain(ws) {
|
|
277
279
|
this.log("debug", "WebSocket drained");
|
|
@@ -312,6 +314,12 @@ class GatewayServer {
|
|
|
312
314
|
async handleAgentRequest(ws, msg) {
|
|
313
315
|
if (!msg.id) return void this.sendError(ws, "INVALID_REQUEST", "Missing request id");
|
|
314
316
|
if (!ws.data.authenticated) return void this.sendAgentError(ws, msg.id, "Client is not authenticated");
|
|
317
|
+
if (this.activeAgentRequests.has(msg.id)) {
|
|
318
|
+
const existing = this.activeAgentRequests.get(msg.id);
|
|
319
|
+
existing?.abortController.abort();
|
|
320
|
+
this.activeAgentRequests.delete(msg.id);
|
|
321
|
+
}
|
|
322
|
+
this.removeQueuedRequestById(msg.id);
|
|
315
323
|
const payload = msg.payload;
|
|
316
324
|
const content = "string" == typeof payload?.content ? payload.content : "";
|
|
317
325
|
const attachments = Array.isArray(payload?.attachments) ? payload.attachments : [];
|
|
@@ -321,6 +329,7 @@ class GatewayServer {
|
|
|
321
329
|
const agentId = this.router.selectAgent(payload.agentId, payload.routing);
|
|
322
330
|
if (!agentId) return void this.sendAgentError(ws, msg.id, "No agent matched the request");
|
|
323
331
|
const sessionKey = payload.sessionKey || this.router.buildSessionKey(agentId, payload.routing);
|
|
332
|
+
const sessionQueueKey = this.buildSessionQueueKey(agentId, sessionKey);
|
|
324
333
|
const sessionManager = await this.getSessionManager(agentId);
|
|
325
334
|
const existingSession = sessionManager.getSession(sessionKey);
|
|
326
335
|
const session = existingSession || sessionManager.getOrCreateSession(sessionKey, agentId);
|
|
@@ -371,9 +380,70 @@ class GatewayServer {
|
|
|
371
380
|
],
|
|
372
381
|
skipSessionId: sessionKey
|
|
373
382
|
});
|
|
383
|
+
const request = {
|
|
384
|
+
ws,
|
|
385
|
+
msg,
|
|
386
|
+
payload,
|
|
387
|
+
agentId,
|
|
388
|
+
sessionKey,
|
|
389
|
+
sessionQueueKey,
|
|
390
|
+
content,
|
|
391
|
+
attachments,
|
|
392
|
+
sessionManager,
|
|
393
|
+
workdir,
|
|
394
|
+
defaultOutputDir
|
|
395
|
+
};
|
|
396
|
+
this.requestSessionKeys.set(msg.id, sessionQueueKey);
|
|
397
|
+
const queueIfBusy = false !== payload.queueIfBusy;
|
|
398
|
+
const activeRequestId = this.activeSessionRequests.get(sessionQueueKey);
|
|
399
|
+
if (activeRequestId && queueIfBusy) {
|
|
400
|
+
const queued = this.queuedSessionRequests.get(sessionQueueKey) || [];
|
|
401
|
+
queued.push(request);
|
|
402
|
+
this.queuedSessionRequests.set(sessionQueueKey, queued);
|
|
403
|
+
const position = queued.length;
|
|
404
|
+
this.sendMessage(ws, {
|
|
405
|
+
type: "ack",
|
|
406
|
+
id: msg.id,
|
|
407
|
+
payload: {
|
|
408
|
+
action: "req:agent",
|
|
409
|
+
status: "queued",
|
|
410
|
+
requestId: msg.id,
|
|
411
|
+
sessionId: sessionKey,
|
|
412
|
+
agentId,
|
|
413
|
+
position
|
|
414
|
+
},
|
|
415
|
+
timestamp: Date.now()
|
|
416
|
+
});
|
|
417
|
+
this.sendMessage(ws, {
|
|
418
|
+
type: "event:agent",
|
|
419
|
+
id: msg.id,
|
|
420
|
+
clientId: ws.data.clientId,
|
|
421
|
+
payload: this.attachSessionContext({
|
|
422
|
+
type: "request-queued",
|
|
423
|
+
position
|
|
424
|
+
}, sessionKey, agentId),
|
|
425
|
+
timestamp: Date.now()
|
|
426
|
+
});
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (activeRequestId && !queueIfBusy) {
|
|
430
|
+
this.requestSessionKeys.delete(msg.id);
|
|
431
|
+
this.sendAgentError(ws, msg.id, "Session already has an in-flight request. Set queueIfBusy=true to enqueue.", {
|
|
432
|
+
sessionId: sessionKey,
|
|
433
|
+
agentId
|
|
434
|
+
});
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
this.executeAgentRequest(request);
|
|
438
|
+
}
|
|
439
|
+
async executeAgentRequest(request) {
|
|
440
|
+
const { ws, msg, agentId, sessionKey, sessionQueueKey, content, attachments, sessionManager, workdir, defaultOutputDir } = request;
|
|
441
|
+
this.activeSessionRequests.set(sessionQueueKey, msg.id);
|
|
374
442
|
const outputManager = new OutputManager("interactive");
|
|
443
|
+
let emittedAgentError = false;
|
|
375
444
|
const outputHandler = (event)=>{
|
|
376
445
|
const payloadWithSession = this.attachSessionContext(event, sessionKey, agentId);
|
|
446
|
+
if (payloadWithSession && "object" == typeof payloadWithSession && !Array.isArray(payloadWithSession) && "agent-error" === payloadWithSession.type) emittedAgentError = true;
|
|
377
447
|
const baseMessage = {
|
|
378
448
|
type: "event:agent",
|
|
379
449
|
id: msg.id,
|
|
@@ -397,18 +467,126 @@ class GatewayServer {
|
|
|
397
467
|
workdir,
|
|
398
468
|
defaultOutputDir
|
|
399
469
|
});
|
|
470
|
+
const abortController = new AbortController();
|
|
471
|
+
this.activeAgentRequests.set(msg.id, {
|
|
472
|
+
socket: ws,
|
|
473
|
+
abortController
|
|
474
|
+
});
|
|
400
475
|
try {
|
|
401
|
-
await invoker.invokeAgent(agentId, content, sessionKey, attachments
|
|
476
|
+
await invoker.invokeAgent(agentId, content, sessionKey, attachments, {
|
|
477
|
+
signal: abortController.signal
|
|
478
|
+
});
|
|
402
479
|
const updated = sessionManager.getSession(sessionKey);
|
|
403
480
|
if (updated) sessionManager.updateSession(sessionKey, {
|
|
404
481
|
messageCount: updated.messageCount + 1
|
|
405
482
|
});
|
|
406
483
|
} catch (error) {
|
|
407
484
|
this.logger.error("Agent invocation failed", error);
|
|
485
|
+
if (!emittedAgentError) {
|
|
486
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
487
|
+
const stack = error instanceof Error ? error.stack : void 0;
|
|
488
|
+
this.sendAgentError(ws, msg.id, message, {
|
|
489
|
+
sessionId: sessionKey,
|
|
490
|
+
agentId,
|
|
491
|
+
stack,
|
|
492
|
+
broadcastToSession: true,
|
|
493
|
+
exclude: ws
|
|
494
|
+
});
|
|
495
|
+
}
|
|
408
496
|
} finally{
|
|
497
|
+
this.activeAgentRequests.delete(msg.id);
|
|
498
|
+
this.activeSessionRequests.delete(sessionQueueKey);
|
|
499
|
+
this.requestSessionKeys.delete(msg.id);
|
|
409
500
|
outputManager.off("output-event", outputHandler);
|
|
501
|
+
this.processNextQueuedAgentRequest(sessionQueueKey);
|
|
410
502
|
}
|
|
411
503
|
}
|
|
504
|
+
processNextQueuedAgentRequest(sessionQueueKey) {
|
|
505
|
+
if (this.activeSessionRequests.has(sessionQueueKey)) return;
|
|
506
|
+
const queue = this.queuedSessionRequests.get(sessionQueueKey);
|
|
507
|
+
if (!queue || 0 === queue.length) return void this.queuedSessionRequests.delete(sessionQueueKey);
|
|
508
|
+
const next = queue.shift();
|
|
509
|
+
if (!next) return;
|
|
510
|
+
if (0 === queue.length) this.queuedSessionRequests.delete(sessionQueueKey);
|
|
511
|
+
else this.queuedSessionRequests.set(sessionQueueKey, queue);
|
|
512
|
+
this.sendMessage(next.ws, {
|
|
513
|
+
type: "ack",
|
|
514
|
+
id: next.msg.id,
|
|
515
|
+
payload: {
|
|
516
|
+
action: "req:agent",
|
|
517
|
+
status: "dequeued",
|
|
518
|
+
requestId: next.msg.id,
|
|
519
|
+
sessionId: next.sessionKey,
|
|
520
|
+
agentId: next.agentId,
|
|
521
|
+
remaining: queue.length
|
|
522
|
+
},
|
|
523
|
+
timestamp: Date.now()
|
|
524
|
+
});
|
|
525
|
+
this.executeAgentRequest(next);
|
|
526
|
+
}
|
|
527
|
+
handleAgentCancel(ws, msg) {
|
|
528
|
+
if (!ws.data.authenticated) return void this.sendError(ws, "AUTH_FAILED", "Client is not authenticated");
|
|
529
|
+
const payload = msg.payload;
|
|
530
|
+
const requestId = "string" == typeof payload?.requestId && payload.requestId || void 0;
|
|
531
|
+
if (!requestId) return void this.sendError(ws, "INVALID_REQUEST", "Missing requestId for cancellation");
|
|
532
|
+
const active = this.activeAgentRequests.get(requestId);
|
|
533
|
+
if (active) {
|
|
534
|
+
if (active.socket !== ws) return void this.sendError(ws, "FORBIDDEN", "Cannot cancel a request started by another client");
|
|
535
|
+
active.abortController.abort();
|
|
536
|
+
this.activeAgentRequests.delete(requestId);
|
|
537
|
+
this.sendMessage(ws, {
|
|
538
|
+
type: "ack",
|
|
539
|
+
id: msg.id,
|
|
540
|
+
payload: {
|
|
541
|
+
action: "req:agent:cancel",
|
|
542
|
+
requestId,
|
|
543
|
+
status: "cancelled"
|
|
544
|
+
},
|
|
545
|
+
timestamp: Date.now()
|
|
546
|
+
});
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const queued = this.removeQueuedRequestById(requestId);
|
|
550
|
+
if (queued) {
|
|
551
|
+
if (queued.ws !== ws) return void this.sendError(ws, "FORBIDDEN", "Cannot cancel a request started by another client");
|
|
552
|
+
this.sendMessage(ws, {
|
|
553
|
+
type: "ack",
|
|
554
|
+
id: msg.id,
|
|
555
|
+
payload: {
|
|
556
|
+
action: "req:agent:cancel",
|
|
557
|
+
requestId,
|
|
558
|
+
status: "cancelled_queued"
|
|
559
|
+
},
|
|
560
|
+
timestamp: Date.now()
|
|
561
|
+
});
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
this.sendMessage(ws, {
|
|
565
|
+
type: "ack",
|
|
566
|
+
id: msg.id,
|
|
567
|
+
payload: {
|
|
568
|
+
action: "req:agent:cancel",
|
|
569
|
+
requestId,
|
|
570
|
+
status: "not_found"
|
|
571
|
+
},
|
|
572
|
+
timestamp: Date.now()
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
buildSessionQueueKey(agentId, sessionKey) {
|
|
576
|
+
return `${agentId}:${sessionKey}`;
|
|
577
|
+
}
|
|
578
|
+
removeQueuedRequestById(requestId) {
|
|
579
|
+
for (const [queueKey, queue] of this.queuedSessionRequests){
|
|
580
|
+
const index = queue.findIndex((item)=>item.msg.id === requestId);
|
|
581
|
+
if (-1 === index) continue;
|
|
582
|
+
const [removed] = queue.splice(index, 1);
|
|
583
|
+
if (0 === queue.length) this.queuedSessionRequests.delete(queueKey);
|
|
584
|
+
else this.queuedSessionRequests.set(queueKey, queue);
|
|
585
|
+
this.requestSessionKeys.delete(requestId);
|
|
586
|
+
return removed || null;
|
|
587
|
+
}
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
412
590
|
handleRegister(ws, msg) {
|
|
413
591
|
const payload = msg.payload;
|
|
414
592
|
if (!this.auth.validate({
|
|
@@ -571,17 +749,39 @@ class GatewayServer {
|
|
|
571
749
|
timestamp: Date.now()
|
|
572
750
|
});
|
|
573
751
|
}
|
|
574
|
-
sendAgentError(ws, requestId, message) {
|
|
575
|
-
|
|
752
|
+
sendAgentError(ws, requestId, message, options) {
|
|
753
|
+
let payload = {
|
|
754
|
+
type: "agent-error",
|
|
755
|
+
error: message,
|
|
756
|
+
timestamp: new Date().toISOString()
|
|
757
|
+
};
|
|
758
|
+
if (options?.stack) payload.stack = options.stack;
|
|
759
|
+
if (options?.sessionId && options?.agentId) payload = this.attachSessionContext(payload, options.sessionId, options.agentId);
|
|
760
|
+
const baseMessage = {
|
|
576
761
|
type: "event:agent",
|
|
577
762
|
id: requestId,
|
|
578
|
-
payload
|
|
579
|
-
type: "agent-error",
|
|
580
|
-
error: message,
|
|
581
|
-
timestamp: new Date().toISOString()
|
|
582
|
-
},
|
|
763
|
+
payload,
|
|
583
764
|
timestamp: Date.now()
|
|
765
|
+
};
|
|
766
|
+
this.sendMessage(ws, {
|
|
767
|
+
...baseMessage,
|
|
768
|
+
clientId: ws.data.clientId
|
|
584
769
|
});
|
|
770
|
+
if (options?.broadcastToSession && options.sessionId) this.broadcastSessionEvent(options.sessionId, baseMessage, options.exclude);
|
|
771
|
+
}
|
|
772
|
+
cancelSocketAgentRequests(ws) {
|
|
773
|
+
for (const [requestId, active] of this.activeAgentRequests)if (active.socket === ws) {
|
|
774
|
+
active.abortController.abort();
|
|
775
|
+
this.activeAgentRequests.delete(requestId);
|
|
776
|
+
}
|
|
777
|
+
for (const [queueKey, queue] of this.queuedSessionRequests){
|
|
778
|
+
const nextQueue = queue.filter((request)=>request.ws !== ws);
|
|
779
|
+
if (nextQueue.length !== queue.length) {
|
|
780
|
+
for (const request of queue)if (request.ws === ws && request.msg.id) this.requestSessionKeys.delete(request.msg.id);
|
|
781
|
+
if (0 === nextQueue.length) this.queuedSessionRequests.delete(queueKey);
|
|
782
|
+
else this.queuedSessionRequests.set(queueKey, nextQueue);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
585
785
|
}
|
|
586
786
|
attachSessionContext(event, sessionId, agentId) {
|
|
587
787
|
if (event && "object" == typeof event && !Array.isArray(event)) return {
|
|
@@ -773,9 +973,7 @@ class GatewayServer {
|
|
|
773
973
|
];
|
|
774
974
|
for (const candidate of candidates)try {
|
|
775
975
|
if (existsSync(candidate) && statSync(candidate).isDirectory() && existsSync(join(candidate, "index.html"))) return candidate;
|
|
776
|
-
} catch {
|
|
777
|
-
continue;
|
|
778
|
-
}
|
|
976
|
+
} catch {}
|
|
779
977
|
return null;
|
|
780
978
|
}
|
|
781
979
|
async getSessionManager(agentId) {
|
|
@@ -1098,6 +1296,10 @@ class GatewayServer {
|
|
|
1098
1296
|
_define_property(this, "sessionSubscriptions", new Map());
|
|
1099
1297
|
_define_property(this, "socketSubscriptions", new Map());
|
|
1100
1298
|
_define_property(this, "connectedClients", new Set());
|
|
1299
|
+
_define_property(this, "activeAgentRequests", new Map());
|
|
1300
|
+
_define_property(this, "activeSessionRequests", new Map());
|
|
1301
|
+
_define_property(this, "queuedSessionRequests", new Map());
|
|
1302
|
+
_define_property(this, "requestSessionKeys", new Map());
|
|
1101
1303
|
_define_property(this, "bridgeQueues", new Map());
|
|
1102
1304
|
_define_property(this, "bridgePollWaiters", new Map());
|
|
1103
1305
|
this.workspace = config.workspace || process.cwd();
|
package/dist/gateway/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ServerWebSocket } from "bun";
|
|
|
2
2
|
/**
|
|
3
3
|
* Message types for gateway communication
|
|
4
4
|
*/
|
|
5
|
-
export type MessageType = "connect" | "res" | "req:agent" | "event:agent" | "session_subscribe" | "session_unsubscribe" | "register" | "registered" | "unregister" | "join_group" | "leave_group" | "broadcast" | "direct" | "ping" | "pong" | "error" | "ack" | "upgrade";
|
|
5
|
+
export type MessageType = "connect" | "res" | "req:agent" | "req:agent:cancel" | "event:agent" | "session_subscribe" | "session_unsubscribe" | "register" | "registered" | "unregister" | "join_group" | "leave_group" | "broadcast" | "direct" | "ping" | "pong" | "error" | "ack" | "upgrade";
|
|
6
6
|
/**
|
|
7
7
|
* Gateway message structure
|
|
8
8
|
*/
|
|
@@ -60,6 +60,10 @@ export interface AgentRequestPayload {
|
|
|
60
60
|
attachments?: MediaAttachment[];
|
|
61
61
|
routing?: RoutingInfo;
|
|
62
62
|
sessionKey?: string;
|
|
63
|
+
queueIfBusy?: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface AgentCancelPayload {
|
|
66
|
+
requestId: string;
|
|
63
67
|
}
|
|
64
68
|
export interface RoutingPeer {
|
|
65
69
|
kind: "dm" | "group" | "channel";
|
|
@@ -8,6 +8,7 @@ export declare const MessageTypeSchema: z.ZodEnum<{
|
|
|
8
8
|
connect: "connect";
|
|
9
9
|
res: "res";
|
|
10
10
|
"req:agent": "req:agent";
|
|
11
|
+
"req:agent:cancel": "req:agent:cancel";
|
|
11
12
|
"event:agent": "event:agent";
|
|
12
13
|
session_subscribe: "session_subscribe";
|
|
13
14
|
session_unsubscribe: "session_unsubscribe";
|
|
@@ -31,6 +32,7 @@ export declare const GatewayMessageSchema: z.ZodObject<{
|
|
|
31
32
|
connect: "connect";
|
|
32
33
|
res: "res";
|
|
33
34
|
"req:agent": "req:agent";
|
|
35
|
+
"req:agent:cancel": "req:agent:cancel";
|
|
34
36
|
"event:agent": "event:agent";
|
|
35
37
|
session_subscribe: "session_subscribe";
|
|
36
38
|
session_unsubscribe: "session_unsubscribe";
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
createCodexFetch: ()=>createCodexFetch,
|
|
28
|
+
getCodexAuthPath: ()=>getCodexAuthPath,
|
|
29
|
+
resolveCodexAuthFromFile: ()=>resolveCodexAuthFromFile
|
|
30
|
+
});
|
|
31
|
+
const external_node_fs_namespaceObject = require("node:fs");
|
|
32
|
+
const external_node_os_namespaceObject = require("node:os");
|
|
33
|
+
const external_node_path_namespaceObject = require("node:path");
|
|
34
|
+
const external_logger_cjs_namespaceObject = require("../logger.cjs");
|
|
35
|
+
const CODEX_HOME_ENV = "CODEX_HOME";
|
|
36
|
+
const CODEX_AUTH_FILE = "auth.json";
|
|
37
|
+
const DEFAULT_CODEX_INSTRUCTIONS = "You are Wingman, a coding assistant. Follow the user's request exactly and keep tool usage focused.";
|
|
38
|
+
const logger = (0, external_logger_cjs_namespaceObject.createLogger)();
|
|
39
|
+
function getCodexAuthPath() {
|
|
40
|
+
const codexHome = process.env[CODEX_HOME_ENV]?.trim();
|
|
41
|
+
if (codexHome) return (0, external_node_path_namespaceObject.join)(codexHome, CODEX_AUTH_FILE);
|
|
42
|
+
return (0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.homedir)(), ".codex", CODEX_AUTH_FILE);
|
|
43
|
+
}
|
|
44
|
+
function resolveCodexAuthFromFile() {
|
|
45
|
+
const authPath = getCodexAuthPath();
|
|
46
|
+
if (!(0, external_node_fs_namespaceObject.existsSync)(authPath)) return {
|
|
47
|
+
authPath
|
|
48
|
+
};
|
|
49
|
+
try {
|
|
50
|
+
const parsed = JSON.parse((0, external_node_fs_namespaceObject.readFileSync)(authPath, "utf-8"));
|
|
51
|
+
if (!parsed || "object" != typeof parsed) return {
|
|
52
|
+
authPath
|
|
53
|
+
};
|
|
54
|
+
const root = parsed;
|
|
55
|
+
const tokens = root.tokens && "object" == typeof root.tokens ? root.tokens : void 0;
|
|
56
|
+
const accessToken = firstNonEmptyString([
|
|
57
|
+
tokens?.access_token,
|
|
58
|
+
root.access_token
|
|
59
|
+
]);
|
|
60
|
+
const accountId = firstNonEmptyString([
|
|
61
|
+
tokens?.account_id,
|
|
62
|
+
root.account_id
|
|
63
|
+
]);
|
|
64
|
+
return {
|
|
65
|
+
accessToken,
|
|
66
|
+
accountId,
|
|
67
|
+
authPath
|
|
68
|
+
};
|
|
69
|
+
} catch {
|
|
70
|
+
return {
|
|
71
|
+
authPath
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function createCodexFetch(options = {}) {
|
|
76
|
+
const baseFetch = options.baseFetch || globalThis.fetch.bind(globalThis);
|
|
77
|
+
return async (input, init)=>{
|
|
78
|
+
const codexAuth = resolveCodexAuthFromFile();
|
|
79
|
+
const accessToken = codexAuth.accessToken || options.fallbackToken;
|
|
80
|
+
const accountId = codexAuth.accountId || options.fallbackAccountId;
|
|
81
|
+
if (!accessToken) throw new Error("Codex credentials missing. Run `codex login` or set CODEX_ACCESS_TOKEN.");
|
|
82
|
+
const headers = new Headers(init?.headers || {});
|
|
83
|
+
headers.delete("authorization");
|
|
84
|
+
headers.delete("x-api-key");
|
|
85
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
86
|
+
if (accountId) headers.set("ChatGPT-Account-ID", accountId);
|
|
87
|
+
const body = withCodexRequestDefaults(init?.body);
|
|
88
|
+
const response = await baseFetch(input, {
|
|
89
|
+
...init,
|
|
90
|
+
headers,
|
|
91
|
+
body
|
|
92
|
+
});
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
let responseBody = "";
|
|
95
|
+
try {
|
|
96
|
+
responseBody = await response.clone().text();
|
|
97
|
+
} catch {}
|
|
98
|
+
const preview = responseBody.trim().slice(0, 1200);
|
|
99
|
+
logger.warn(`Codex request failed (${response.status} ${response.statusText || ""})`, {
|
|
100
|
+
url: "string" == typeof input ? input : input instanceof URL ? input.toString() : input.url,
|
|
101
|
+
bodyPresent: preview.length > 0,
|
|
102
|
+
bodyPreview: preview || null
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return response;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function withCodexRequestDefaults(body) {
|
|
109
|
+
if ("string" != typeof body || !body.trim()) return body;
|
|
110
|
+
try {
|
|
111
|
+
const parsed = JSON.parse(body);
|
|
112
|
+
if (!parsed || "object" != typeof parsed || Array.isArray(parsed)) return body;
|
|
113
|
+
const root = stripUnsupportedCodexParams(parsed);
|
|
114
|
+
const instructions = "string" == typeof root.instructions && root.instructions.trim() ? root.instructions.trim() : extractInstructionsFromInput(root.input) || DEFAULT_CODEX_INSTRUCTIONS;
|
|
115
|
+
return JSON.stringify({
|
|
116
|
+
...root,
|
|
117
|
+
store: false,
|
|
118
|
+
instructions
|
|
119
|
+
});
|
|
120
|
+
} catch {
|
|
121
|
+
return body;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function stripUnsupportedCodexParams(payload) {
|
|
125
|
+
const next = {
|
|
126
|
+
...payload
|
|
127
|
+
};
|
|
128
|
+
delete next.temperature;
|
|
129
|
+
return next;
|
|
130
|
+
}
|
|
131
|
+
function extractInstructionsFromInput(input) {
|
|
132
|
+
if (!Array.isArray(input)) return;
|
|
133
|
+
for (const item of input){
|
|
134
|
+
if (!item || "object" != typeof item || Array.isArray(item)) continue;
|
|
135
|
+
const message = item;
|
|
136
|
+
const role = "string" == typeof message.role ? message.role : "";
|
|
137
|
+
if ("system" !== role && "developer" !== role) continue;
|
|
138
|
+
const text = extractTextContent(message.content);
|
|
139
|
+
if (text) return text;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function extractTextContent(content) {
|
|
143
|
+
if ("string" == typeof content && content.trim()) return content.trim();
|
|
144
|
+
if (!Array.isArray(content)) return;
|
|
145
|
+
for (const part of content){
|
|
146
|
+
if (!part || "object" != typeof part || Array.isArray(part)) continue;
|
|
147
|
+
const block = part;
|
|
148
|
+
const text = "string" == typeof block.text ? block.text : "string" == typeof block.content ? block.content : void 0;
|
|
149
|
+
if (!text || !text.trim()) continue;
|
|
150
|
+
const type = "string" == typeof block.type ? block.type : "";
|
|
151
|
+
if (!type || type.includes("text")) return text.trim();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function firstNonEmptyString(values) {
|
|
155
|
+
for (const value of values)if ("string" == typeof value && value.trim()) return value.trim();
|
|
156
|
+
}
|
|
157
|
+
exports.createCodexFetch = __webpack_exports__.createCodexFetch;
|
|
158
|
+
exports.getCodexAuthPath = __webpack_exports__.getCodexAuthPath;
|
|
159
|
+
exports.resolveCodexAuthFromFile = __webpack_exports__.resolveCodexAuthFromFile;
|
|
160
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
161
|
+
"createCodexFetch",
|
|
162
|
+
"getCodexAuthPath",
|
|
163
|
+
"resolveCodexAuthFromFile"
|
|
164
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
165
|
+
Object.defineProperty(exports, '__esModule', {
|
|
166
|
+
value: true
|
|
167
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type FetchLike = (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]) => ReturnType<typeof fetch>;
|
|
2
|
+
export interface CodexAuthState {
|
|
3
|
+
accessToken?: string;
|
|
4
|
+
accountId?: string;
|
|
5
|
+
authPath: string;
|
|
6
|
+
}
|
|
7
|
+
export interface CodexFetchOptions {
|
|
8
|
+
baseFetch?: FetchLike;
|
|
9
|
+
fallbackToken?: string;
|
|
10
|
+
fallbackAccountId?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function getCodexAuthPath(): string;
|
|
13
|
+
export declare function resolveCodexAuthFromFile(): CodexAuthState;
|
|
14
|
+
export declare function createCodexFetch(options?: CodexFetchOptions): FetchLike;
|
|
15
|
+
export {};
|