@love-moon/ai-sdk 0.2.30 → 0.2.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +47 -7
- package/dist/client.js +214 -14
- package/dist/external-provider-registry.d.ts +4 -0
- package/dist/external-provider-registry.js +143 -0
- package/dist/providers/claude-agent-sdk-session.d.ts +1 -0
- package/dist/providers/claude-agent-sdk-session.js +17 -4
- package/dist/providers/codex-app-server-session.d.ts +3 -2
- package/dist/providers/codex-app-server-session.js +24 -5
- package/dist/providers/opencode-sdk-session.d.ts +1 -0
- package/dist/providers/opencode-sdk-session.js +22 -4
- package/dist/session-factory.d.ts +5 -5
- package/dist/session-factory.js +42 -12
- package/dist/worker.js +1 -1
- package/package.json +2 -2
package/dist/client.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
export function createAiSession(backend: any, options?: {}):
|
|
1
|
+
export function createAiSession(backend: any, options?: {}): RemoteAiSession | LocalAiSessionProxy;
|
|
2
2
|
export class RemoteAiSession extends EventEmitter<[never]> {
|
|
3
3
|
constructor(backend: any, options?: {});
|
|
4
|
-
backend: string;
|
|
5
4
|
options: {};
|
|
6
5
|
logger: any;
|
|
7
|
-
variant: string;
|
|
8
6
|
threadIdValue: any;
|
|
9
7
|
threadOptionsValue: {
|
|
10
8
|
model: any;
|
|
@@ -12,8 +10,8 @@ export class RemoteAiSession extends EventEmitter<[never]> {
|
|
|
12
10
|
useSessionFileReplyStreamValue: boolean;
|
|
13
11
|
sessionInfo: any;
|
|
14
12
|
snapshot: {
|
|
15
|
-
backend:
|
|
16
|
-
provider:
|
|
13
|
+
backend: undefined;
|
|
14
|
+
provider: undefined;
|
|
17
15
|
sessionId: any;
|
|
18
16
|
useSessionFileReplyStream: boolean;
|
|
19
17
|
workerReady: boolean;
|
|
@@ -31,14 +29,18 @@ export class RemoteAiSession extends EventEmitter<[never]> {
|
|
|
31
29
|
resolveReady: ((value: any) => void) | null;
|
|
32
30
|
rejectReady: ((reason?: any) => void) | null;
|
|
33
31
|
readyPromise: Promise<any>;
|
|
32
|
+
initializeSession(backend: any, options: any): Promise<void>;
|
|
33
|
+
backend: any;
|
|
34
|
+
variant: any;
|
|
34
35
|
get threadId(): any;
|
|
35
36
|
get threadOptions(): {
|
|
37
|
+
modelProvider?: any;
|
|
36
38
|
model: any;
|
|
37
39
|
};
|
|
38
40
|
getSnapshot(): {
|
|
39
41
|
sessionInfo: any;
|
|
40
|
-
backend:
|
|
41
|
-
provider:
|
|
42
|
+
backend: undefined;
|
|
43
|
+
provider: undefined;
|
|
42
44
|
sessionId: any;
|
|
43
45
|
useSessionFileReplyStream: boolean;
|
|
44
46
|
workerReady: boolean;
|
|
@@ -63,5 +65,43 @@ export class RemoteAiSession extends EventEmitter<[never]> {
|
|
|
63
65
|
handleWorkerEvent(payload: any): Promise<void>;
|
|
64
66
|
rejectPendingRequests(error: any): void;
|
|
65
67
|
}
|
|
68
|
+
declare class LocalAiSessionProxy extends EventEmitter<[never]> {
|
|
69
|
+
constructor(backend: any, options?: {});
|
|
70
|
+
backend: any;
|
|
71
|
+
options: {};
|
|
72
|
+
threadIdValue: any;
|
|
73
|
+
threadOptionsValue: {
|
|
74
|
+
model: any;
|
|
75
|
+
};
|
|
76
|
+
useSessionFileReplyStreamValue: boolean;
|
|
77
|
+
sessionInfo: any;
|
|
78
|
+
session: any;
|
|
79
|
+
closed: boolean;
|
|
80
|
+
sessionMessageHandler: any;
|
|
81
|
+
workingStatusHandler: any;
|
|
82
|
+
replyTarget: any;
|
|
83
|
+
snapshot: {
|
|
84
|
+
backend: undefined;
|
|
85
|
+
provider: undefined;
|
|
86
|
+
sessionId: any;
|
|
87
|
+
useSessionFileReplyStream: boolean;
|
|
88
|
+
workerReady: boolean;
|
|
89
|
+
};
|
|
90
|
+
readyPromise: Promise<any>;
|
|
91
|
+
initializeSession(backend: any, options: any): Promise<any>;
|
|
92
|
+
get threadId(): any;
|
|
93
|
+
get threadOptions(): any;
|
|
94
|
+
getSnapshot(): any;
|
|
95
|
+
usesSessionFileReplyStream(): boolean;
|
|
96
|
+
getSessionInfo(): any;
|
|
97
|
+
setSessionMessageHandler(handler: any): void;
|
|
98
|
+
setWorkingStatusHandler(handler: any): void;
|
|
99
|
+
setSessionReplyTarget(replyTarget: any): void;
|
|
100
|
+
ensureSessionInfo(): Promise<any>;
|
|
101
|
+
getSessionUsageSummary(): Promise<any>;
|
|
102
|
+
runTurn(promptText: any, options?: {}): Promise<any>;
|
|
103
|
+
close(): Promise<void>;
|
|
104
|
+
}
|
|
66
105
|
import { EventEmitter } from "node:events";
|
|
67
106
|
import readline from "node:readline";
|
|
107
|
+
export {};
|
package/dist/client.js
CHANGED
|
@@ -26,10 +26,8 @@ function toSerializablePayload(payload) {
|
|
|
26
26
|
export class RemoteAiSession extends EventEmitter {
|
|
27
27
|
constructor(backend, options = {}) {
|
|
28
28
|
super();
|
|
29
|
-
this.backend = assertSupportedBackend(backend);
|
|
30
29
|
this.options = options;
|
|
31
30
|
this.logger = normalizeLogger(options.logger);
|
|
32
|
-
this.variant = providerVariantForBackend(this.backend);
|
|
33
31
|
this.threadIdValue =
|
|
34
32
|
typeof options.resumeSessionId === "string" && options.resumeSessionId.trim()
|
|
35
33
|
? options.resumeSessionId.trim()
|
|
@@ -37,13 +35,13 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
37
35
|
this.threadOptionsValue = {
|
|
38
36
|
model: typeof options.model === "string" && options.model.trim()
|
|
39
37
|
? options.model.trim()
|
|
40
|
-
:
|
|
38
|
+
: String(backend || "unknown").trim() || "unknown",
|
|
41
39
|
};
|
|
42
40
|
this.useSessionFileReplyStreamValue = true;
|
|
43
41
|
this.sessionInfo = null;
|
|
44
42
|
this.snapshot = {
|
|
45
|
-
backend:
|
|
46
|
-
provider:
|
|
43
|
+
backend: undefined,
|
|
44
|
+
provider: undefined,
|
|
47
45
|
sessionId: this.threadIdValue || undefined,
|
|
48
46
|
useSessionFileReplyStream: this.useSessionFileReplyStreamValue,
|
|
49
47
|
workerReady: false,
|
|
@@ -94,17 +92,45 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
94
92
|
this.rejectReady?.(error);
|
|
95
93
|
this.rejectPendingRequests(error);
|
|
96
94
|
});
|
|
97
|
-
this.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
95
|
+
void this.initializeSession(backend, options);
|
|
96
|
+
}
|
|
97
|
+
async initializeSession(backend, options) {
|
|
98
|
+
try {
|
|
99
|
+
this.backend = await assertSupportedBackend(backend, options);
|
|
100
|
+
this.variant = await providerVariantForBackend(backend, options);
|
|
101
|
+
this.snapshot.backend = this.backend;
|
|
102
|
+
this.snapshot.provider = this.variant;
|
|
103
|
+
this.threadOptionsValue.model =
|
|
104
|
+
typeof options.model === "string" && options.model.trim() ? options.model.trim() : this.backend || "unknown";
|
|
105
|
+
this.child.stdin.write(toSerializablePayload({
|
|
106
|
+
type: "create",
|
|
107
|
+
backend: this.backend,
|
|
108
|
+
options: sanitizeOptionsForWorker(options),
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
this.rejectReady?.(error);
|
|
113
|
+
this.resolveReady = null;
|
|
114
|
+
this.rejectReady = null;
|
|
115
|
+
this.rejectPendingRequests(error);
|
|
116
|
+
try {
|
|
117
|
+
this.child.kill("SIGTERM");
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// ignore
|
|
121
|
+
}
|
|
122
|
+
}
|
|
102
123
|
}
|
|
103
124
|
get threadId() {
|
|
104
125
|
return this.threadIdValue;
|
|
105
126
|
}
|
|
106
127
|
get threadOptions() {
|
|
107
|
-
|
|
128
|
+
const sessionInfo = this.sessionInfo && typeof this.sessionInfo === "object" ? this.sessionInfo : null;
|
|
129
|
+
return {
|
|
130
|
+
...this.threadOptionsValue,
|
|
131
|
+
...(sessionInfo?.model ? { model: sessionInfo.model } : {}),
|
|
132
|
+
...(sessionInfo?.modelProvider ? { modelProvider: sessionInfo.modelProvider } : {}),
|
|
133
|
+
};
|
|
108
134
|
}
|
|
109
135
|
getSnapshot() {
|
|
110
136
|
return {
|
|
@@ -352,10 +378,184 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
352
378
|
this.pendingRequests.clear();
|
|
353
379
|
}
|
|
354
380
|
}
|
|
381
|
+
const LOCAL_SESSION_EVENT_NAMES = ["session", "assistant_message", "working_status", "auth_required", "process.exited"];
|
|
382
|
+
class LocalAiSessionProxy extends EventEmitter {
|
|
383
|
+
constructor(backend, options = {}) {
|
|
384
|
+
super();
|
|
385
|
+
this.backend = undefined;
|
|
386
|
+
this.options = options;
|
|
387
|
+
this.threadIdValue =
|
|
388
|
+
typeof options.resumeSessionId === "string" && options.resumeSessionId.trim()
|
|
389
|
+
? options.resumeSessionId.trim()
|
|
390
|
+
: "";
|
|
391
|
+
this.threadOptionsValue = {
|
|
392
|
+
model: typeof options.model === "string" && options.model.trim()
|
|
393
|
+
? options.model.trim()
|
|
394
|
+
: String(backend || "unknown").trim() || "unknown",
|
|
395
|
+
};
|
|
396
|
+
this.useSessionFileReplyStreamValue = true;
|
|
397
|
+
this.sessionInfo = null;
|
|
398
|
+
this.session = null;
|
|
399
|
+
this.closed = false;
|
|
400
|
+
this.sessionMessageHandler = null;
|
|
401
|
+
this.workingStatusHandler = null;
|
|
402
|
+
this.replyTarget = undefined;
|
|
403
|
+
this.snapshot = {
|
|
404
|
+
backend: undefined,
|
|
405
|
+
provider: undefined,
|
|
406
|
+
sessionId: this.threadIdValue || undefined,
|
|
407
|
+
useSessionFileReplyStream: this.useSessionFileReplyStreamValue,
|
|
408
|
+
workerReady: false,
|
|
409
|
+
};
|
|
410
|
+
this.readyPromise = this.initializeSession(backend, options);
|
|
411
|
+
this.readyPromise.catch(() => {
|
|
412
|
+
// keep parity with RemoteAiSession and avoid unhandled rejections
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
async initializeSession(backend, options) {
|
|
416
|
+
const session = await createLocalAiSession(backend, options);
|
|
417
|
+
if (this.closed) {
|
|
418
|
+
await session.close?.();
|
|
419
|
+
return session;
|
|
420
|
+
}
|
|
421
|
+
this.session = session;
|
|
422
|
+
for (const eventName of LOCAL_SESSION_EVENT_NAMES) {
|
|
423
|
+
if (typeof session.on === "function") {
|
|
424
|
+
session.on(eventName, async (payload) => {
|
|
425
|
+
if (eventName === "session" && payload && typeof payload === "object") {
|
|
426
|
+
this.sessionInfo = { ...payload };
|
|
427
|
+
if (payload.sessionId) {
|
|
428
|
+
this.threadIdValue = String(payload.sessionId);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
this.emit(eventName, payload);
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (this.sessionMessageHandler && typeof session.setSessionMessageHandler === "function") {
|
|
436
|
+
session.setSessionMessageHandler(this.sessionMessageHandler);
|
|
437
|
+
}
|
|
438
|
+
if (this.workingStatusHandler && typeof session.setWorkingStatusHandler === "function") {
|
|
439
|
+
session.setWorkingStatusHandler(this.workingStatusHandler);
|
|
440
|
+
}
|
|
441
|
+
if (this.replyTarget !== undefined && typeof session.setSessionReplyTarget === "function") {
|
|
442
|
+
session.setSessionReplyTarget(this.replyTarget);
|
|
443
|
+
}
|
|
444
|
+
const snapshot = typeof session.getSnapshot === "function" ? session.getSnapshot() : {};
|
|
445
|
+
this.backend = snapshot.backend || session.backend || this.backend;
|
|
446
|
+
this.threadIdValue = session.threadId || snapshot.sessionId || this.threadIdValue;
|
|
447
|
+
this.threadOptionsValue = session.threadOptions ? { ...session.threadOptions } : this.threadOptionsValue;
|
|
448
|
+
this.useSessionFileReplyStreamValue =
|
|
449
|
+
typeof session.usesSessionFileReplyStream === "function"
|
|
450
|
+
? Boolean(session.usesSessionFileReplyStream())
|
|
451
|
+
: snapshot.useSessionFileReplyStream !== undefined
|
|
452
|
+
? Boolean(snapshot.useSessionFileReplyStream)
|
|
453
|
+
: this.useSessionFileReplyStreamValue;
|
|
454
|
+
this.sessionInfo =
|
|
455
|
+
typeof session.getSessionInfo === "function"
|
|
456
|
+
? session.getSessionInfo()
|
|
457
|
+
: snapshot.sessionInfo && typeof snapshot.sessionInfo === "object"
|
|
458
|
+
? { ...snapshot.sessionInfo }
|
|
459
|
+
: this.sessionInfo;
|
|
460
|
+
this.snapshot = {
|
|
461
|
+
...this.snapshot,
|
|
462
|
+
...snapshot,
|
|
463
|
+
backend: this.backend,
|
|
464
|
+
sessionId: this.threadIdValue || undefined,
|
|
465
|
+
sessionInfo: this.sessionInfo,
|
|
466
|
+
useSessionFileReplyStream: this.useSessionFileReplyStreamValue,
|
|
467
|
+
workerReady: true,
|
|
468
|
+
};
|
|
469
|
+
return session;
|
|
470
|
+
}
|
|
471
|
+
get threadId() {
|
|
472
|
+
return this.session?.threadId || this.threadIdValue;
|
|
473
|
+
}
|
|
474
|
+
get threadOptions() {
|
|
475
|
+
return this.session?.threadOptions ? { ...this.session.threadOptions } : { ...this.threadOptionsValue };
|
|
476
|
+
}
|
|
477
|
+
getSnapshot() {
|
|
478
|
+
const sessionSnapshot = typeof this.session?.getSnapshot === "function" ? this.session.getSnapshot() : null;
|
|
479
|
+
if (sessionSnapshot) {
|
|
480
|
+
return {
|
|
481
|
+
...this.snapshot,
|
|
482
|
+
...sessionSnapshot,
|
|
483
|
+
backend: sessionSnapshot.backend || this.snapshot.backend,
|
|
484
|
+
sessionId: sessionSnapshot.sessionId || this.threadIdValue || undefined,
|
|
485
|
+
sessionInfo: typeof this.session?.getSessionInfo === "function"
|
|
486
|
+
? this.session.getSessionInfo()
|
|
487
|
+
: sessionSnapshot.sessionInfo || this.sessionInfo || null,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
return {
|
|
491
|
+
...this.snapshot,
|
|
492
|
+
sessionInfo: this.sessionInfo ? { ...this.sessionInfo } : null,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
usesSessionFileReplyStream() {
|
|
496
|
+
if (typeof this.session?.usesSessionFileReplyStream === "function") {
|
|
497
|
+
return Boolean(this.session.usesSessionFileReplyStream());
|
|
498
|
+
}
|
|
499
|
+
return Boolean(this.useSessionFileReplyStreamValue);
|
|
500
|
+
}
|
|
501
|
+
getSessionInfo() {
|
|
502
|
+
if (typeof this.session?.getSessionInfo === "function") {
|
|
503
|
+
return this.session.getSessionInfo();
|
|
504
|
+
}
|
|
505
|
+
return this.sessionInfo ? { ...this.sessionInfo } : null;
|
|
506
|
+
}
|
|
507
|
+
setSessionMessageHandler(handler) {
|
|
508
|
+
this.sessionMessageHandler = typeof handler === "function" ? handler : null;
|
|
509
|
+
if (typeof this.session?.setSessionMessageHandler === "function") {
|
|
510
|
+
this.session.setSessionMessageHandler(this.sessionMessageHandler);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
setWorkingStatusHandler(handler) {
|
|
514
|
+
this.workingStatusHandler = typeof handler === "function" ? handler : null;
|
|
515
|
+
if (typeof this.session?.setWorkingStatusHandler === "function") {
|
|
516
|
+
this.session.setWorkingStatusHandler(this.workingStatusHandler);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
setSessionReplyTarget(replyTarget) {
|
|
520
|
+
this.replyTarget = replyTarget;
|
|
521
|
+
if (typeof this.session?.setSessionReplyTarget === "function") {
|
|
522
|
+
this.session.setSessionReplyTarget(replyTarget);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
async ensureSessionInfo() {
|
|
526
|
+
const session = await this.readyPromise;
|
|
527
|
+
const sessionInfo = await session.ensureSessionInfo();
|
|
528
|
+
this.sessionInfo = sessionInfo && typeof sessionInfo === "object" ? { ...sessionInfo } : sessionInfo;
|
|
529
|
+
if (sessionInfo?.sessionId) {
|
|
530
|
+
this.threadIdValue = String(sessionInfo.sessionId);
|
|
531
|
+
}
|
|
532
|
+
return sessionInfo;
|
|
533
|
+
}
|
|
534
|
+
async getSessionUsageSummary() {
|
|
535
|
+
const session = await this.readyPromise;
|
|
536
|
+
return await session.getSessionUsageSummary();
|
|
537
|
+
}
|
|
538
|
+
async runTurn(promptText, options = {}) {
|
|
539
|
+
const session = await this.readyPromise;
|
|
540
|
+
return await session.runTurn(promptText, options);
|
|
541
|
+
}
|
|
542
|
+
async close() {
|
|
543
|
+
if (this.closed) {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
this.closed = true;
|
|
547
|
+
try {
|
|
548
|
+
const session = await this.readyPromise;
|
|
549
|
+
await session.close?.();
|
|
550
|
+
}
|
|
551
|
+
catch {
|
|
552
|
+
// best effort
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
355
556
|
export function createAiSession(backend, options = {}) {
|
|
356
|
-
const normalizedBackend = assertSupportedBackend(backend);
|
|
357
557
|
if (process.env.CONDUCTOR_AI_SDK_DISABLE_WORKER === "1") {
|
|
358
|
-
return
|
|
558
|
+
return new LocalAiSessionProxy(backend, options);
|
|
359
559
|
}
|
|
360
|
-
return new RemoteAiSession(
|
|
560
|
+
return new RemoteAiSession(backend, options);
|
|
361
561
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export function getExternalProviderRegistry(options?: {}): Promise<any>;
|
|
2
|
+
export function resolveExternalBackend(backend: any, options?: {}): Promise<any>;
|
|
3
|
+
export function getExternalProviderDescriptor(backend: any, options?: {}): Promise<any>;
|
|
4
|
+
export function resetExternalProviderRegistryForTests(): void;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { loadEnvConfig } from "./shared.js";
|
|
4
|
+
const BUILT_IN_BACKENDS = new Set(["codex", "claude", "kimi", "opencode"]);
|
|
5
|
+
const registryPromises = new Map();
|
|
6
|
+
let externalProviderImportNonce = 0;
|
|
7
|
+
function normalizeProviderPathEnv(value) {
|
|
8
|
+
return String(value || "").trim();
|
|
9
|
+
}
|
|
10
|
+
function listProviderModulePathsFromValue(rawValue) {
|
|
11
|
+
const raw = normalizeProviderPathEnv(rawValue);
|
|
12
|
+
if (!raw) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const parts = raw
|
|
16
|
+
.split(process.platform === "win32" ? ";" : ":")
|
|
17
|
+
.map((item) => item.trim())
|
|
18
|
+
.filter(Boolean);
|
|
19
|
+
return [...new Set(parts)];
|
|
20
|
+
}
|
|
21
|
+
function resolveProviderPathEnv(options = {}) {
|
|
22
|
+
const envValue = normalizeProviderPathEnv(process.env.AISDK_PROVIDER_PATH);
|
|
23
|
+
if (envValue) {
|
|
24
|
+
return envValue;
|
|
25
|
+
}
|
|
26
|
+
const envConfig = loadEnvConfig(options.configFile);
|
|
27
|
+
return normalizeProviderPathEnv(envConfig?.AISDK_PROVIDER_PATH);
|
|
28
|
+
}
|
|
29
|
+
function normalizeName(value) {
|
|
30
|
+
return String(value || "").trim().toLowerCase();
|
|
31
|
+
}
|
|
32
|
+
function createRegistry() {
|
|
33
|
+
return {
|
|
34
|
+
descriptors: [],
|
|
35
|
+
byBackend: new Map(),
|
|
36
|
+
aliasToBackend: new Map(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function registerAlias(registry, alias, backend, sourcePath) {
|
|
40
|
+
const normalizedAlias = normalizeName(alias);
|
|
41
|
+
if (!normalizedAlias) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (BUILT_IN_BACKENDS.has(normalizedAlias) && normalizedAlias !== backend) {
|
|
45
|
+
throw new Error(`External AI SDK provider alias "${normalizedAlias}" from ${sourcePath} conflicts with built-in backend "${normalizedAlias}".`);
|
|
46
|
+
}
|
|
47
|
+
const existingBackend = registry.aliasToBackend.get(normalizedAlias);
|
|
48
|
+
if (existingBackend && existingBackend !== backend) {
|
|
49
|
+
throw new Error(`External AI SDK provider alias "${normalizedAlias}" from ${sourcePath} conflicts with backend "${existingBackend}".`);
|
|
50
|
+
}
|
|
51
|
+
registry.aliasToBackend.set(normalizedAlias, backend);
|
|
52
|
+
}
|
|
53
|
+
function validateDescriptor(descriptor, sourcePath) {
|
|
54
|
+
if (!descriptor || typeof descriptor !== "object") {
|
|
55
|
+
throw new Error(`External AI SDK provider module ${sourcePath} contains an invalid provider descriptor.`);
|
|
56
|
+
}
|
|
57
|
+
const backend = normalizeName(descriptor.backend);
|
|
58
|
+
if (!backend) {
|
|
59
|
+
throw new Error(`External AI SDK provider module ${sourcePath} is missing provider.backend.`);
|
|
60
|
+
}
|
|
61
|
+
if (BUILT_IN_BACKENDS.has(backend)) {
|
|
62
|
+
throw new Error(`External AI SDK provider backend "${backend}" from ${sourcePath} conflicts with a built-in backend.`);
|
|
63
|
+
}
|
|
64
|
+
const variant = String(descriptor.variant || "").trim();
|
|
65
|
+
if (!variant) {
|
|
66
|
+
throw new Error(`External AI SDK provider "${backend}" from ${sourcePath} is missing provider.variant.`);
|
|
67
|
+
}
|
|
68
|
+
if (typeof descriptor.createSession !== "function") {
|
|
69
|
+
throw new Error(`External AI SDK provider "${backend}" from ${sourcePath} is missing provider.createSession().`);
|
|
70
|
+
}
|
|
71
|
+
const aliases = Array.isArray(descriptor.aliases) ? descriptor.aliases.map((item) => normalizeName(item)).filter(Boolean) : [];
|
|
72
|
+
return {
|
|
73
|
+
backend,
|
|
74
|
+
variant,
|
|
75
|
+
aliases,
|
|
76
|
+
createSession: descriptor.createSession,
|
|
77
|
+
isSupported: typeof descriptor.isSupported === "function" ? descriptor.isSupported : null,
|
|
78
|
+
sourcePath,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function loadRegistry(modulePaths) {
|
|
82
|
+
const registry = createRegistry();
|
|
83
|
+
for (const modulePath of modulePaths) {
|
|
84
|
+
let importedModule;
|
|
85
|
+
try {
|
|
86
|
+
const resolvedPath = path.isAbsolute(modulePath) ? modulePath : path.resolve(modulePath);
|
|
87
|
+
const moduleUrl = pathToFileURL(resolvedPath);
|
|
88
|
+
moduleUrl.searchParams.set("conductor-ai-sdk-provider-attempt", String(++externalProviderImportNonce));
|
|
89
|
+
importedModule = await import(moduleUrl.href);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw new Error(`Failed to load external AI SDK provider module ${modulePath}: ${error?.message || error}`);
|
|
93
|
+
}
|
|
94
|
+
const providers = Array.isArray(importedModule?.providers) ? importedModule.providers : [];
|
|
95
|
+
if (providers.length === 0) {
|
|
96
|
+
throw new Error(`External AI SDK provider module ${modulePath} must export a non-empty providers array.`);
|
|
97
|
+
}
|
|
98
|
+
for (const rawDescriptor of providers) {
|
|
99
|
+
const descriptor = validateDescriptor(rawDescriptor, modulePath);
|
|
100
|
+
if (registry.byBackend.has(descriptor.backend)) {
|
|
101
|
+
throw new Error(`External AI SDK provider backend "${descriptor.backend}" is declared more than once (latest: ${modulePath}).`);
|
|
102
|
+
}
|
|
103
|
+
registry.descriptors.push(descriptor);
|
|
104
|
+
registry.byBackend.set(descriptor.backend, descriptor);
|
|
105
|
+
registerAlias(registry, descriptor.backend, descriptor.backend, modulePath);
|
|
106
|
+
for (const alias of descriptor.aliases) {
|
|
107
|
+
registerAlias(registry, alias, descriptor.backend, modulePath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return registry;
|
|
112
|
+
}
|
|
113
|
+
export async function getExternalProviderRegistry(options = {}) {
|
|
114
|
+
const providerPathEnv = resolveProviderPathEnv(options);
|
|
115
|
+
if (!registryPromises.has(providerPathEnv)) {
|
|
116
|
+
const loadPromise = loadRegistry(listProviderModulePathsFromValue(providerPathEnv)).catch((error) => {
|
|
117
|
+
registryPromises.delete(providerPathEnv);
|
|
118
|
+
throw error;
|
|
119
|
+
});
|
|
120
|
+
registryPromises.set(providerPathEnv, loadPromise);
|
|
121
|
+
}
|
|
122
|
+
return registryPromises.get(providerPathEnv);
|
|
123
|
+
}
|
|
124
|
+
export async function resolveExternalBackend(backend, options = {}) {
|
|
125
|
+
const normalized = normalizeName(backend);
|
|
126
|
+
if (!normalized) {
|
|
127
|
+
return "";
|
|
128
|
+
}
|
|
129
|
+
const registry = await getExternalProviderRegistry(options);
|
|
130
|
+
return registry.aliasToBackend.get(normalized) || normalized;
|
|
131
|
+
}
|
|
132
|
+
export async function getExternalProviderDescriptor(backend, options = {}) {
|
|
133
|
+
const normalized = normalizeName(backend);
|
|
134
|
+
if (!normalized) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const registry = await getExternalProviderRegistry(options);
|
|
138
|
+
const resolvedBackend = registry.aliasToBackend.get(normalized) || normalized;
|
|
139
|
+
return registry.byBackend.get(resolvedBackend) || null;
|
|
140
|
+
}
|
|
141
|
+
export function resetExternalProviderRegistryForTests() {
|
|
142
|
+
registryPromises.clear();
|
|
143
|
+
}
|
|
@@ -179,10 +179,14 @@ export class ClaudeAgentSdkSession extends EventEmitter {
|
|
|
179
179
|
return this.sessionId;
|
|
180
180
|
}
|
|
181
181
|
get threadOptions() {
|
|
182
|
-
const model =
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
const model = this.sessionInfo?.model ||
|
|
183
|
+
(typeof this.options.model === "string" && this.options.model.trim()
|
|
184
|
+
? this.options.model.trim()
|
|
185
|
+
: this.backend);
|
|
186
|
+
return {
|
|
187
|
+
model,
|
|
188
|
+
modelProvider: this.sessionInfo?.modelProvider || undefined,
|
|
189
|
+
};
|
|
186
190
|
}
|
|
187
191
|
getSnapshot() {
|
|
188
192
|
return {
|
|
@@ -414,10 +418,19 @@ export class ClaudeAgentSdkSession extends EventEmitter {
|
|
|
414
418
|
const changed = this.sessionId !== normalizedSessionId;
|
|
415
419
|
this.sessionId = normalizedSessionId;
|
|
416
420
|
this.manualResumeReady = true;
|
|
421
|
+
const modelUsage = this.lastResult?.modelUsage && typeof this.lastResult.modelUsage === "object"
|
|
422
|
+
? this.lastResult.modelUsage
|
|
423
|
+
: null;
|
|
424
|
+
const resolvedModel = typeof modelUsage?.model === "string" && modelUsage.model.trim()
|
|
425
|
+
? modelUsage.model.trim()
|
|
426
|
+
: typeof this.options.model === "string" && this.options.model.trim()
|
|
427
|
+
? this.options.model.trim()
|
|
428
|
+
: undefined;
|
|
417
429
|
this.sessionInfo = {
|
|
418
430
|
...(this.sessionInfo || {}),
|
|
419
431
|
backend: this.backend,
|
|
420
432
|
sessionId: normalizedSessionId,
|
|
433
|
+
model: resolvedModel,
|
|
421
434
|
};
|
|
422
435
|
if (changed) {
|
|
423
436
|
this.trace(`session ready id=${normalizedSessionId}`);
|
|
@@ -37,7 +37,8 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
|
37
37
|
trace(message: any): void;
|
|
38
38
|
get threadId(): any;
|
|
39
39
|
get threadOptions(): {
|
|
40
|
-
model:
|
|
40
|
+
model: any;
|
|
41
|
+
modelProvider: any;
|
|
41
42
|
};
|
|
42
43
|
getSnapshot(): {
|
|
43
44
|
backend: string;
|
|
@@ -74,7 +75,7 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
|
74
75
|
getCurrentReplyTarget(): string | undefined;
|
|
75
76
|
boot(): Promise<void>;
|
|
76
77
|
bootInternal(): Promise<void>;
|
|
77
|
-
applyThreadInfo(
|
|
78
|
+
applyThreadInfo(payload: any, { resumeReady }?: {
|
|
78
79
|
resumeReady?: boolean | undefined;
|
|
79
80
|
}): void;
|
|
80
81
|
applySessionConfigured(params: any): void;
|
|
@@ -224,7 +224,10 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
224
224
|
return this.sessionId;
|
|
225
225
|
}
|
|
226
226
|
get threadOptions() {
|
|
227
|
-
return {
|
|
227
|
+
return {
|
|
228
|
+
model: this.sessionInfo?.model || this.backend,
|
|
229
|
+
modelProvider: this.sessionInfo?.modelProvider || undefined,
|
|
230
|
+
};
|
|
228
231
|
}
|
|
229
232
|
getSnapshot() {
|
|
230
233
|
return {
|
|
@@ -326,9 +329,10 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
326
329
|
else {
|
|
327
330
|
result = await this.transport.request("thread/start", params);
|
|
328
331
|
}
|
|
329
|
-
this.applyThreadInfo(result
|
|
332
|
+
this.applyThreadInfo(result, { resumeReady: Boolean(this.resumeSessionId) });
|
|
330
333
|
}
|
|
331
|
-
applyThreadInfo(
|
|
334
|
+
applyThreadInfo(payload, { resumeReady = false } = {}) {
|
|
335
|
+
const thread = payload?.thread && typeof payload.thread === "object" ? payload.thread : payload;
|
|
332
336
|
const threadId = typeof thread?.id === "string" ? thread.id.trim() : "";
|
|
333
337
|
const threadPath = typeof thread?.path === "string" ? thread.path.trim() : "";
|
|
334
338
|
if (!threadId) {
|
|
@@ -336,15 +340,30 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
336
340
|
}
|
|
337
341
|
this.sessionId = threadId;
|
|
338
342
|
this.threadPath = threadPath;
|
|
343
|
+
const resolvedModel = typeof payload?.model === "string" && payload.model.trim()
|
|
344
|
+
? payload.model.trim()
|
|
345
|
+
: this.sessionInfo?.model || undefined;
|
|
346
|
+
const resolvedModelProvider = typeof payload?.modelProvider === "string" && payload.modelProvider.trim()
|
|
347
|
+
? payload.modelProvider.trim()
|
|
348
|
+
: typeof thread?.modelProvider === "string" && thread.modelProvider.trim()
|
|
349
|
+
? thread.modelProvider.trim()
|
|
350
|
+
: this.sessionInfo?.modelProvider || undefined;
|
|
351
|
+
const reasoningEffort = typeof payload?.reasoningEffort === "string" && payload.reasoningEffort.trim()
|
|
352
|
+
? payload.reasoningEffort.trim()
|
|
353
|
+
: this.sessionInfo?.reasoningEffort || undefined;
|
|
339
354
|
this.sessionInfo = {
|
|
355
|
+
...(this.sessionInfo || {}),
|
|
340
356
|
backend: this.backend,
|
|
341
357
|
sessionId: threadId,
|
|
342
358
|
sessionFilePath: threadPath || undefined,
|
|
359
|
+
model: resolvedModel,
|
|
360
|
+
modelProvider: resolvedModelProvider,
|
|
361
|
+
reasoningEffort,
|
|
343
362
|
};
|
|
344
363
|
if (resumeReady) {
|
|
345
364
|
this.manualResumeReady = true;
|
|
346
365
|
}
|
|
347
|
-
this.trace(`thread ready id=${threadId} path="${sanitizeForLog(threadPath, 180)}"`);
|
|
366
|
+
this.trace(`thread ready id=${threadId} path="${sanitizeForLog(threadPath, 180)}" model="${sanitizeForLog(resolvedModel || this.backend, 80)}" provider="${sanitizeForLog(resolvedModelProvider || this.backend, 80)}"`);
|
|
348
367
|
this.emit("session", this.getSessionInfo());
|
|
349
368
|
}
|
|
350
369
|
applySessionConfigured(params) {
|
|
@@ -571,7 +590,7 @@ export class CodexAppServerSession extends EventEmitter {
|
|
|
571
590
|
}
|
|
572
591
|
switch (method) {
|
|
573
592
|
case "thread/started":
|
|
574
|
-
this.applyThreadInfo(params
|
|
593
|
+
this.applyThreadInfo(params, { resumeReady: Boolean(this.resumeSessionId) });
|
|
575
594
|
return;
|
|
576
595
|
case "sessionConfigured":
|
|
577
596
|
case "session_configured":
|
|
@@ -200,10 +200,14 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
200
200
|
return this.sessionId;
|
|
201
201
|
}
|
|
202
202
|
get threadOptions() {
|
|
203
|
-
const model =
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
203
|
+
const model = this.sessionInfo?.model ||
|
|
204
|
+
(typeof this.options.model === "string" && this.options.model.trim()
|
|
205
|
+
? this.options.model.trim()
|
|
206
|
+
: this.backend);
|
|
207
|
+
return {
|
|
208
|
+
model,
|
|
209
|
+
modelProvider: this.sessionInfo?.modelProvider || undefined,
|
|
210
|
+
};
|
|
207
211
|
}
|
|
208
212
|
getSnapshot() {
|
|
209
213
|
return {
|
|
@@ -536,9 +540,20 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
536
540
|
}
|
|
537
541
|
const changed = this.sessionId !== normalizedSessionId;
|
|
538
542
|
this.sessionId = normalizedSessionId;
|
|
543
|
+
const resolvedModel = typeof this.lastAssistantInfo?.model?.modelID === "string" && this.lastAssistantInfo.model.modelID.trim()
|
|
544
|
+
? this.lastAssistantInfo.model.modelID.trim()
|
|
545
|
+
: typeof this.options.model === "string" && this.options.model.trim()
|
|
546
|
+
? this.options.model.trim()
|
|
547
|
+
: undefined;
|
|
548
|
+
const resolvedModelProvider = typeof this.lastAssistantInfo?.model?.providerID === "string" && this.lastAssistantInfo.model.providerID.trim()
|
|
549
|
+
? this.lastAssistantInfo.model.providerID.trim()
|
|
550
|
+
: undefined;
|
|
539
551
|
this.sessionInfo = {
|
|
552
|
+
...(this.sessionInfo || {}),
|
|
540
553
|
backend: this.backend,
|
|
541
554
|
sessionId: normalizedSessionId,
|
|
555
|
+
model: resolvedModel,
|
|
556
|
+
modelProvider: resolvedModelProvider,
|
|
542
557
|
};
|
|
543
558
|
if (changed) {
|
|
544
559
|
this.trace(`session ready id=${normalizedSessionId}`);
|
|
@@ -874,6 +889,9 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
874
889
|
this.activeReplyTarget = "";
|
|
875
890
|
this.lastUsage = this.buildUsageFromAssistantInfo(currentTurn.lastAssistantInfo);
|
|
876
891
|
this.lastAssistantInfo = currentTurn.lastAssistantInfo || null;
|
|
892
|
+
this.applySessionInfo({
|
|
893
|
+
id: this.sessionId,
|
|
894
|
+
});
|
|
877
895
|
await this.emitTerminalWorkingStatus(currentTurn, {
|
|
878
896
|
phase: "turn_completed",
|
|
879
897
|
status_done_line: "opencode finished",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export function normalizeBackend(backend: any):
|
|
2
|
-
export function isSupportedBackend(backend: any): boolean
|
|
3
|
-
export function providerVariantForBackend(backend: any):
|
|
4
|
-
export function assertSupportedBackend(backend: any):
|
|
5
|
-
export function createLocalAiSession(backend: any, options?: {}):
|
|
1
|
+
export function normalizeBackend(backend: any, options?: {}): Promise<any>;
|
|
2
|
+
export function isSupportedBackend(backend: any, options?: {}): Promise<boolean>;
|
|
3
|
+
export function providerVariantForBackend(backend: any, options?: {}): Promise<any>;
|
|
4
|
+
export function assertSupportedBackend(backend: any, options?: {}): Promise<any>;
|
|
5
|
+
export function createLocalAiSession(backend: any, options?: {}): Promise<any>;
|
|
6
6
|
export const DEFAULT_PROVIDER_VARIANT: "codex-app-server";
|
|
7
7
|
export const CLAUDE_PROVIDER_VARIANT: "claude-agent-sdk";
|
|
8
8
|
export const KIMI_PROVIDER_VARIANT: "kimi-cli-wire";
|
package/dist/session-factory.js
CHANGED
|
@@ -2,11 +2,12 @@ import { CodexAppServerSession } from "./providers/codex-app-server-session.js";
|
|
|
2
2
|
import { ClaudeAgentSdkSession } from "./providers/claude-agent-sdk-session.js";
|
|
3
3
|
import { KimiCliSession } from "./providers/kimi-cli-session.js";
|
|
4
4
|
import { OpencodeSdkSession } from "./providers/opencode-sdk-session.js";
|
|
5
|
+
import { getExternalProviderDescriptor, resolveExternalBackend, } from "./external-provider-registry.js";
|
|
5
6
|
export const DEFAULT_PROVIDER_VARIANT = "codex-app-server";
|
|
6
7
|
export const CLAUDE_PROVIDER_VARIANT = "claude-agent-sdk";
|
|
7
8
|
export const KIMI_PROVIDER_VARIANT = "kimi-cli-wire";
|
|
8
9
|
export const OPENCODE_PROVIDER_VARIANT = "opencode-sdk";
|
|
9
|
-
|
|
10
|
+
function normalizeBuiltInBackendName(backend) {
|
|
10
11
|
const normalized = String(backend || "").trim().toLowerCase();
|
|
11
12
|
if (normalized === "code") {
|
|
12
13
|
return "codex";
|
|
@@ -22,12 +23,23 @@ export function normalizeBackend(backend) {
|
|
|
22
23
|
}
|
|
23
24
|
return normalized;
|
|
24
25
|
}
|
|
25
|
-
export function
|
|
26
|
-
const normalized =
|
|
27
|
-
|
|
26
|
+
export async function normalizeBackend(backend, options = {}) {
|
|
27
|
+
const normalized = normalizeBuiltInBackendName(backend);
|
|
28
|
+
if (normalized === "codex" || normalized === "claude" || normalized === "kimi" || normalized === "opencode") {
|
|
29
|
+
return normalized;
|
|
30
|
+
}
|
|
31
|
+
return await resolveExternalBackend(normalized, options);
|
|
32
|
+
}
|
|
33
|
+
export async function isSupportedBackend(backend, options = {}) {
|
|
34
|
+
const normalized = await normalizeBackend(backend, options);
|
|
35
|
+
if (normalized === "codex" || normalized === "claude" || normalized === "kimi" || normalized === "opencode") {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
const descriptor = await getExternalProviderDescriptor(normalized, options);
|
|
39
|
+
return Boolean(descriptor);
|
|
28
40
|
}
|
|
29
|
-
export function providerVariantForBackend(backend) {
|
|
30
|
-
const normalized = normalizeBackend(backend);
|
|
41
|
+
export async function providerVariantForBackend(backend, options = {}) {
|
|
42
|
+
const normalized = await normalizeBackend(backend, options);
|
|
31
43
|
if (normalized === "claude") {
|
|
32
44
|
return CLAUDE_PROVIDER_VARIANT;
|
|
33
45
|
}
|
|
@@ -37,17 +49,28 @@ export function providerVariantForBackend(backend) {
|
|
|
37
49
|
if (normalized === "opencode") {
|
|
38
50
|
return OPENCODE_PROVIDER_VARIANT;
|
|
39
51
|
}
|
|
52
|
+
if (normalized === "codex") {
|
|
53
|
+
return DEFAULT_PROVIDER_VARIANT;
|
|
54
|
+
}
|
|
55
|
+
const descriptor = await getExternalProviderDescriptor(normalized, options);
|
|
56
|
+
if (descriptor?.variant) {
|
|
57
|
+
return descriptor.variant;
|
|
58
|
+
}
|
|
40
59
|
return DEFAULT_PROVIDER_VARIANT;
|
|
41
60
|
}
|
|
42
|
-
export function assertSupportedBackend(backend) {
|
|
43
|
-
const normalized = normalizeBackend(backend);
|
|
61
|
+
export async function assertSupportedBackend(backend, options = {}) {
|
|
62
|
+
const normalized = await normalizeBackend(backend, options);
|
|
44
63
|
if (normalized === "codex" || normalized === "claude" || normalized === "kimi" || normalized === "opencode") {
|
|
45
64
|
return normalized;
|
|
46
65
|
}
|
|
47
|
-
|
|
66
|
+
const descriptor = await getExternalProviderDescriptor(normalized, options);
|
|
67
|
+
if (descriptor) {
|
|
68
|
+
return normalized;
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`Unsupported AI SDK backend "${backend}". Built-in backends are codex app-server, claude agent-sdk, kimi cli wire, and opencode sdk. Set AISDK_PROVIDER_PATH to load external providers.`);
|
|
48
71
|
}
|
|
49
|
-
export function createLocalAiSession(backend, options = {}) {
|
|
50
|
-
const normalized = assertSupportedBackend(backend);
|
|
72
|
+
export async function createLocalAiSession(backend, options = {}) {
|
|
73
|
+
const normalized = await assertSupportedBackend(backend, options);
|
|
51
74
|
if (normalized === "claude") {
|
|
52
75
|
return new ClaudeAgentSdkSession(normalized, options);
|
|
53
76
|
}
|
|
@@ -57,7 +80,14 @@ export function createLocalAiSession(backend, options = {}) {
|
|
|
57
80
|
if (normalized === "opencode") {
|
|
58
81
|
return new OpencodeSdkSession(normalized, options);
|
|
59
82
|
}
|
|
60
|
-
|
|
83
|
+
if (normalized === "codex") {
|
|
84
|
+
return new CodexAppServerSession(normalized, options);
|
|
85
|
+
}
|
|
86
|
+
const descriptor = await getExternalProviderDescriptor(normalized, options);
|
|
87
|
+
if (!descriptor) {
|
|
88
|
+
throw new Error(`External AI SDK provider "${normalized}" is unavailable.`);
|
|
89
|
+
}
|
|
90
|
+
return await descriptor.createSession(normalized, options);
|
|
61
91
|
}
|
|
62
92
|
export { CodexAppServerSession };
|
|
63
93
|
export { ClaudeAgentSdkSession };
|
package/dist/worker.js
CHANGED
|
@@ -33,7 +33,7 @@ async function handleCreate(message) {
|
|
|
33
33
|
throw new Error("AI worker session already created");
|
|
34
34
|
}
|
|
35
35
|
sessionCreated = true;
|
|
36
|
-
session = createLocalAiSession(message.backend, {
|
|
36
|
+
session = await createLocalAiSession(message.backend, {
|
|
37
37
|
...(message.options && typeof message.options === "object" ? message.options : {}),
|
|
38
38
|
logger: {
|
|
39
39
|
log: (line) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@love-moon/ai-sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
"@types/node": "^22.10.2",
|
|
26
26
|
"typescript": "^5.6.3"
|
|
27
27
|
},
|
|
28
|
-
"gitCommitId": "
|
|
28
|
+
"gitCommitId": "7e0bd83"
|
|
29
29
|
}
|