@wingman-ai/gateway 0.2.2 → 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/coding/agent.md +174 -169
- package/.wingman/agents/coding/implementor.md +25 -1
- package/.wingman/agents/main/agent.md +4 -0
- package/README.md +1 -0
- package/dist/agent/config/agentConfig.cjs +1 -1
- package/dist/agent/config/agentConfig.js +1 -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/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 +191 -13
- package/dist/cli/core/agentInvoker.d.ts +42 -3
- package/dist/cli/core/agentInvoker.js +163 -9
- 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 +191 -41
- package/dist/gateway/server.d.ts +8 -1
- package/dist/gateway/server.js +191 -41
- package/dist/gateway/types.d.ts +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 +108 -1
- package/dist/tests/gateway.test.js +108 -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-DDsMIOTX.css → index-BVMavpud.css} +1 -1
- package/dist/webui/assets/index-DCB2aVVf.js +182 -0
- package/dist/webui/index.html +2 -2
- package/package.json +1 -1
- package/dist/webui/assets/index-CPhfGPHc.js +0 -182
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,
|
|
@@ -319,6 +319,7 @@ class GatewayServer {
|
|
|
319
319
|
existing?.abortController.abort();
|
|
320
320
|
this.activeAgentRequests.delete(msg.id);
|
|
321
321
|
}
|
|
322
|
+
this.removeQueuedRequestById(msg.id);
|
|
322
323
|
const payload = msg.payload;
|
|
323
324
|
const content = "string" == typeof payload?.content ? payload.content : "";
|
|
324
325
|
const attachments = Array.isArray(payload?.attachments) ? payload.attachments : [];
|
|
@@ -328,6 +329,7 @@ class GatewayServer {
|
|
|
328
329
|
const agentId = this.router.selectAgent(payload.agentId, payload.routing);
|
|
329
330
|
if (!agentId) return void this.sendAgentError(ws, msg.id, "No agent matched the request");
|
|
330
331
|
const sessionKey = payload.sessionKey || this.router.buildSessionKey(agentId, payload.routing);
|
|
332
|
+
const sessionQueueKey = this.buildSessionQueueKey(agentId, sessionKey);
|
|
331
333
|
const sessionManager = await this.getSessionManager(agentId);
|
|
332
334
|
const existingSession = sessionManager.getSession(sessionKey);
|
|
333
335
|
const session = existingSession || sessionManager.getOrCreateSession(sessionKey, agentId);
|
|
@@ -378,9 +380,70 @@ class GatewayServer {
|
|
|
378
380
|
],
|
|
379
381
|
skipSessionId: sessionKey
|
|
380
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);
|
|
381
442
|
const outputManager = new OutputManager("interactive");
|
|
443
|
+
let emittedAgentError = false;
|
|
382
444
|
const outputHandler = (event)=>{
|
|
383
445
|
const payloadWithSession = this.attachSessionContext(event, sessionKey, agentId);
|
|
446
|
+
if (payloadWithSession && "object" == typeof payloadWithSession && !Array.isArray(payloadWithSession) && "agent-error" === payloadWithSession.type) emittedAgentError = true;
|
|
384
447
|
const baseMessage = {
|
|
385
448
|
type: "event:agent",
|
|
386
449
|
id: msg.id,
|
|
@@ -419,41 +482,111 @@ class GatewayServer {
|
|
|
419
482
|
});
|
|
420
483
|
} catch (error) {
|
|
421
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
|
+
}
|
|
422
496
|
} finally{
|
|
423
497
|
this.activeAgentRequests.delete(msg.id);
|
|
498
|
+
this.activeSessionRequests.delete(sessionQueueKey);
|
|
499
|
+
this.requestSessionKeys.delete(msg.id);
|
|
424
500
|
outputManager.off("output-event", outputHandler);
|
|
501
|
+
this.processNextQueuedAgentRequest(sessionQueueKey);
|
|
425
502
|
}
|
|
426
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
|
+
}
|
|
427
527
|
handleAgentCancel(ws, msg) {
|
|
428
528
|
if (!ws.data.authenticated) return void this.sendError(ws, "AUTH_FAILED", "Client is not authenticated");
|
|
429
529
|
const payload = msg.payload;
|
|
430
530
|
const requestId = "string" == typeof payload?.requestId && payload.requestId || void 0;
|
|
431
531
|
if (!requestId) return void this.sendError(ws, "INVALID_REQUEST", "Missing requestId for cancellation");
|
|
432
532
|
const active = this.activeAgentRequests.get(requestId);
|
|
433
|
-
if (
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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
|
+
}
|
|
446
564
|
this.sendMessage(ws, {
|
|
447
565
|
type: "ack",
|
|
448
566
|
id: msg.id,
|
|
449
567
|
payload: {
|
|
450
568
|
action: "req:agent:cancel",
|
|
451
569
|
requestId,
|
|
452
|
-
status: "
|
|
570
|
+
status: "not_found"
|
|
453
571
|
},
|
|
454
572
|
timestamp: Date.now()
|
|
455
573
|
});
|
|
456
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
|
+
}
|
|
457
590
|
handleRegister(ws, msg) {
|
|
458
591
|
const payload = msg.payload;
|
|
459
592
|
if (!this.auth.validate({
|
|
@@ -616,23 +749,39 @@ class GatewayServer {
|
|
|
616
749
|
timestamp: Date.now()
|
|
617
750
|
});
|
|
618
751
|
}
|
|
619
|
-
sendAgentError(ws, requestId, message) {
|
|
620
|
-
|
|
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 = {
|
|
621
761
|
type: "event:agent",
|
|
622
762
|
id: requestId,
|
|
623
|
-
payload
|
|
624
|
-
type: "agent-error",
|
|
625
|
-
error: message,
|
|
626
|
-
timestamp: new Date().toISOString()
|
|
627
|
-
},
|
|
763
|
+
payload,
|
|
628
764
|
timestamp: Date.now()
|
|
765
|
+
};
|
|
766
|
+
this.sendMessage(ws, {
|
|
767
|
+
...baseMessage,
|
|
768
|
+
clientId: ws.data.clientId
|
|
629
769
|
});
|
|
770
|
+
if (options?.broadcastToSession && options.sessionId) this.broadcastSessionEvent(options.sessionId, baseMessage, options.exclude);
|
|
630
771
|
}
|
|
631
772
|
cancelSocketAgentRequests(ws) {
|
|
632
773
|
for (const [requestId, active] of this.activeAgentRequests)if (active.socket === ws) {
|
|
633
774
|
active.abortController.abort();
|
|
634
775
|
this.activeAgentRequests.delete(requestId);
|
|
635
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
|
+
}
|
|
636
785
|
}
|
|
637
786
|
attachSessionContext(event, sessionId, agentId) {
|
|
638
787
|
if (event && "object" == typeof event && !Array.isArray(event)) return {
|
|
@@ -824,9 +973,7 @@ class GatewayServer {
|
|
|
824
973
|
];
|
|
825
974
|
for (const candidate of candidates)try {
|
|
826
975
|
if (existsSync(candidate) && statSync(candidate).isDirectory() && existsSync(join(candidate, "index.html"))) return candidate;
|
|
827
|
-
} catch {
|
|
828
|
-
continue;
|
|
829
|
-
}
|
|
976
|
+
} catch {}
|
|
830
977
|
return null;
|
|
831
978
|
}
|
|
832
979
|
async getSessionManager(agentId) {
|
|
@@ -1150,6 +1297,9 @@ class GatewayServer {
|
|
|
1150
1297
|
_define_property(this, "socketSubscriptions", new Map());
|
|
1151
1298
|
_define_property(this, "connectedClients", new Set());
|
|
1152
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());
|
|
1153
1303
|
_define_property(this, "bridgeQueues", new Map());
|
|
1154
1304
|
_define_property(this, "bridgePollWaiters", new Map());
|
|
1155
1305
|
this.workspace = config.workspace || process.cwd();
|
package/dist/gateway/types.d.ts
CHANGED
|
@@ -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 {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { createLogger } from "../logger.js";
|
|
5
|
+
const CODEX_HOME_ENV = "CODEX_HOME";
|
|
6
|
+
const CODEX_AUTH_FILE = "auth.json";
|
|
7
|
+
const DEFAULT_CODEX_INSTRUCTIONS = "You are Wingman, a coding assistant. Follow the user's request exactly and keep tool usage focused.";
|
|
8
|
+
const logger = createLogger();
|
|
9
|
+
function getCodexAuthPath() {
|
|
10
|
+
const codexHome = process.env[CODEX_HOME_ENV]?.trim();
|
|
11
|
+
if (codexHome) return join(codexHome, CODEX_AUTH_FILE);
|
|
12
|
+
return join(homedir(), ".codex", CODEX_AUTH_FILE);
|
|
13
|
+
}
|
|
14
|
+
function resolveCodexAuthFromFile() {
|
|
15
|
+
const authPath = getCodexAuthPath();
|
|
16
|
+
if (!existsSync(authPath)) return {
|
|
17
|
+
authPath
|
|
18
|
+
};
|
|
19
|
+
try {
|
|
20
|
+
const parsed = JSON.parse(readFileSync(authPath, "utf-8"));
|
|
21
|
+
if (!parsed || "object" != typeof parsed) return {
|
|
22
|
+
authPath
|
|
23
|
+
};
|
|
24
|
+
const root = parsed;
|
|
25
|
+
const tokens = root.tokens && "object" == typeof root.tokens ? root.tokens : void 0;
|
|
26
|
+
const accessToken = firstNonEmptyString([
|
|
27
|
+
tokens?.access_token,
|
|
28
|
+
root.access_token
|
|
29
|
+
]);
|
|
30
|
+
const accountId = firstNonEmptyString([
|
|
31
|
+
tokens?.account_id,
|
|
32
|
+
root.account_id
|
|
33
|
+
]);
|
|
34
|
+
return {
|
|
35
|
+
accessToken,
|
|
36
|
+
accountId,
|
|
37
|
+
authPath
|
|
38
|
+
};
|
|
39
|
+
} catch {
|
|
40
|
+
return {
|
|
41
|
+
authPath
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function createCodexFetch(options = {}) {
|
|
46
|
+
const baseFetch = options.baseFetch || globalThis.fetch.bind(globalThis);
|
|
47
|
+
return async (input, init)=>{
|
|
48
|
+
const codexAuth = resolveCodexAuthFromFile();
|
|
49
|
+
const accessToken = codexAuth.accessToken || options.fallbackToken;
|
|
50
|
+
const accountId = codexAuth.accountId || options.fallbackAccountId;
|
|
51
|
+
if (!accessToken) throw new Error("Codex credentials missing. Run `codex login` or set CODEX_ACCESS_TOKEN.");
|
|
52
|
+
const headers = new Headers(init?.headers || {});
|
|
53
|
+
headers.delete("authorization");
|
|
54
|
+
headers.delete("x-api-key");
|
|
55
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
56
|
+
if (accountId) headers.set("ChatGPT-Account-ID", accountId);
|
|
57
|
+
const body = withCodexRequestDefaults(init?.body);
|
|
58
|
+
const response = await baseFetch(input, {
|
|
59
|
+
...init,
|
|
60
|
+
headers,
|
|
61
|
+
body
|
|
62
|
+
});
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
let responseBody = "";
|
|
65
|
+
try {
|
|
66
|
+
responseBody = await response.clone().text();
|
|
67
|
+
} catch {}
|
|
68
|
+
const preview = responseBody.trim().slice(0, 1200);
|
|
69
|
+
logger.warn(`Codex request failed (${response.status} ${response.statusText || ""})`, {
|
|
70
|
+
url: "string" == typeof input ? input : input instanceof URL ? input.toString() : input.url,
|
|
71
|
+
bodyPresent: preview.length > 0,
|
|
72
|
+
bodyPreview: preview || null
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return response;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function withCodexRequestDefaults(body) {
|
|
79
|
+
if ("string" != typeof body || !body.trim()) return body;
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(body);
|
|
82
|
+
if (!parsed || "object" != typeof parsed || Array.isArray(parsed)) return body;
|
|
83
|
+
const root = stripUnsupportedCodexParams(parsed);
|
|
84
|
+
const instructions = "string" == typeof root.instructions && root.instructions.trim() ? root.instructions.trim() : extractInstructionsFromInput(root.input) || DEFAULT_CODEX_INSTRUCTIONS;
|
|
85
|
+
return JSON.stringify({
|
|
86
|
+
...root,
|
|
87
|
+
store: false,
|
|
88
|
+
instructions
|
|
89
|
+
});
|
|
90
|
+
} catch {
|
|
91
|
+
return body;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function stripUnsupportedCodexParams(payload) {
|
|
95
|
+
const next = {
|
|
96
|
+
...payload
|
|
97
|
+
};
|
|
98
|
+
delete next.temperature;
|
|
99
|
+
return next;
|
|
100
|
+
}
|
|
101
|
+
function extractInstructionsFromInput(input) {
|
|
102
|
+
if (!Array.isArray(input)) return;
|
|
103
|
+
for (const item of input){
|
|
104
|
+
if (!item || "object" != typeof item || Array.isArray(item)) continue;
|
|
105
|
+
const message = item;
|
|
106
|
+
const role = "string" == typeof message.role ? message.role : "";
|
|
107
|
+
if ("system" !== role && "developer" !== role) continue;
|
|
108
|
+
const text = extractTextContent(message.content);
|
|
109
|
+
if (text) return text;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function extractTextContent(content) {
|
|
113
|
+
if ("string" == typeof content && content.trim()) return content.trim();
|
|
114
|
+
if (!Array.isArray(content)) return;
|
|
115
|
+
for (const part of content){
|
|
116
|
+
if (!part || "object" != typeof part || Array.isArray(part)) continue;
|
|
117
|
+
const block = part;
|
|
118
|
+
const text = "string" == typeof block.text ? block.text : "string" == typeof block.content ? block.content : void 0;
|
|
119
|
+
if (!text || !text.trim()) continue;
|
|
120
|
+
const type = "string" == typeof block.type ? block.type : "";
|
|
121
|
+
if (!type || type.includes("text")) return text.trim();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function firstNonEmptyString(values) {
|
|
125
|
+
for (const value of values)if ("string" == typeof value && value.trim()) return value.trim();
|
|
126
|
+
}
|
|
127
|
+
export { createCodexFetch, getCodexAuthPath, resolveCodexAuthFromFile };
|
|
@@ -36,6 +36,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
36
36
|
const external_node_fs_namespaceObject = require("node:fs");
|
|
37
37
|
const external_node_os_namespaceObject = require("node:os");
|
|
38
38
|
const external_node_path_namespaceObject = require("node:path");
|
|
39
|
+
const external_codex_cjs_namespaceObject = require("./codex.cjs");
|
|
39
40
|
const external_registry_cjs_namespaceObject = require("./registry.cjs");
|
|
40
41
|
const CREDENTIALS_VERSION = 1;
|
|
41
42
|
const CREDENTIALS_DIR = (0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.homedir)(), ".wingman");
|
|
@@ -121,6 +122,13 @@ function resolveProviderToken(providerName) {
|
|
|
121
122
|
envVar
|
|
122
123
|
};
|
|
123
124
|
}
|
|
125
|
+
if ("codex" === provider.name) {
|
|
126
|
+
const codexAuth = (0, external_codex_cjs_namespaceObject.resolveCodexAuthFromFile)();
|
|
127
|
+
if (codexAuth.accessToken) return {
|
|
128
|
+
token: codexAuth.accessToken,
|
|
129
|
+
source: "credentials"
|
|
130
|
+
};
|
|
131
|
+
}
|
|
124
132
|
const credentials = getProviderCredentials(provider.name);
|
|
125
133
|
const token = credentials?.accessToken ?? credentials?.apiKey ?? credentials?.refreshToken;
|
|
126
134
|
if (token && token.trim()) return {
|