@love-moon/conductor-sdk 0.2.31 → 0.2.33
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/backend/client.d.ts +34 -6
- package/dist/backend/client.js +30 -6
- package/dist/client.d.ts +10 -1
- package/dist/client.js +91 -19
- package/dist/context/project_context.d.ts +11 -0
- package/dist/context/project_context.js +44 -0
- package/dist/ws/client.d.ts +3 -0
- package/dist/ws/client.js +10 -0
- package/package.json +2 -2
package/dist/backend/client.d.ts
CHANGED
|
@@ -36,8 +36,14 @@ export declare class BackendApiError extends Error {
|
|
|
36
36
|
export declare class ProjectSummary {
|
|
37
37
|
readonly id: string;
|
|
38
38
|
readonly name?: string | undefined;
|
|
39
|
-
readonly
|
|
40
|
-
|
|
39
|
+
readonly daemonHost?: string | null | undefined;
|
|
40
|
+
readonly workspacePath?: string | null | undefined;
|
|
41
|
+
readonly repoRoot?: string | null | undefined;
|
|
42
|
+
readonly worktreeBranch?: string | null | undefined;
|
|
43
|
+
readonly lastCommit?: string | null | undefined;
|
|
44
|
+
readonly fileCount?: number | null | undefined;
|
|
45
|
+
readonly isDefault?: boolean | undefined;
|
|
46
|
+
constructor(id: string, name?: string | undefined, daemonHost?: string | null | undefined, workspacePath?: string | null | undefined, repoRoot?: string | null | undefined, worktreeBranch?: string | null | undefined, lastCommit?: string | null | undefined, fileCount?: number | null | undefined, isDefault?: boolean | undefined);
|
|
41
47
|
static fromJSON(payload: Record<string, any>): ProjectSummary;
|
|
42
48
|
asObject(): Record<string, unknown>;
|
|
43
49
|
}
|
|
@@ -62,9 +68,16 @@ export declare class BackendApiClient {
|
|
|
62
68
|
constructor(config: ConductorConfig, options?: BackendClientOptions);
|
|
63
69
|
listProjects(): Promise<ProjectSummary[]>;
|
|
64
70
|
createProject(params: {
|
|
65
|
-
name
|
|
66
|
-
description?: string;
|
|
71
|
+
name?: string;
|
|
67
72
|
metadata?: Record<string, unknown>;
|
|
73
|
+
isDefault?: boolean;
|
|
74
|
+
bindingConfirmed?: boolean;
|
|
75
|
+
daemonHost?: string;
|
|
76
|
+
workspacePath?: string;
|
|
77
|
+
repoRoot?: string;
|
|
78
|
+
worktreeBranch?: string;
|
|
79
|
+
lastCommit?: string;
|
|
80
|
+
fileCount?: number;
|
|
68
81
|
}): Promise<ProjectSummary>;
|
|
69
82
|
listTasks(params?: {
|
|
70
83
|
projectId?: string;
|
|
@@ -132,7 +145,9 @@ export declare class BackendApiClient {
|
|
|
132
145
|
accepted?: boolean;
|
|
133
146
|
}): Promise<Record<string, unknown>>;
|
|
134
147
|
matchProjectByPath(params: {
|
|
135
|
-
hostname
|
|
148
|
+
hostname?: string;
|
|
149
|
+
daemonHost?: string;
|
|
150
|
+
daemon_host?: string;
|
|
136
151
|
path: string;
|
|
137
152
|
}): Promise<{
|
|
138
153
|
project: ProjectSummary | null;
|
|
@@ -141,12 +156,25 @@ export declare class BackendApiClient {
|
|
|
141
156
|
getProject(projectId: string): Promise<{
|
|
142
157
|
id: string;
|
|
143
158
|
name?: string;
|
|
144
|
-
description?: string | null;
|
|
145
159
|
metadata?: Record<string, unknown>;
|
|
160
|
+
daemonHost?: string | null;
|
|
161
|
+
workspacePath?: string | null;
|
|
162
|
+
repoRoot?: string | null;
|
|
163
|
+
worktreeBranch?: string | null;
|
|
164
|
+
lastCommit?: string | null;
|
|
165
|
+
fileCount?: number | null;
|
|
166
|
+
isDefault?: boolean;
|
|
146
167
|
}>;
|
|
147
168
|
updateProject(projectId: string, params: {
|
|
148
169
|
name?: string;
|
|
149
170
|
metadata?: Record<string, unknown>;
|
|
171
|
+
bindingConfirmed?: boolean;
|
|
172
|
+
daemonHost?: string;
|
|
173
|
+
workspacePath?: string;
|
|
174
|
+
repoRoot?: string;
|
|
175
|
+
worktreeBranch?: string;
|
|
176
|
+
lastCommit?: string;
|
|
177
|
+
fileCount?: number;
|
|
150
178
|
}): Promise<ProjectSummary>;
|
|
151
179
|
private request;
|
|
152
180
|
private buildUrl;
|
package/dist/backend/client.js
CHANGED
|
@@ -10,24 +10,42 @@ export class BackendApiError extends Error {
|
|
|
10
10
|
export class ProjectSummary {
|
|
11
11
|
id;
|
|
12
12
|
name;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
daemonHost;
|
|
14
|
+
workspacePath;
|
|
15
|
+
repoRoot;
|
|
16
|
+
worktreeBranch;
|
|
17
|
+
lastCommit;
|
|
18
|
+
fileCount;
|
|
19
|
+
isDefault;
|
|
20
|
+
constructor(id, name, daemonHost, workspacePath, repoRoot, worktreeBranch, lastCommit, fileCount, isDefault) {
|
|
15
21
|
this.id = id;
|
|
16
22
|
this.name = name;
|
|
17
|
-
this.
|
|
23
|
+
this.daemonHost = daemonHost;
|
|
24
|
+
this.workspacePath = workspacePath;
|
|
25
|
+
this.repoRoot = repoRoot;
|
|
26
|
+
this.worktreeBranch = worktreeBranch;
|
|
27
|
+
this.lastCommit = lastCommit;
|
|
28
|
+
this.fileCount = fileCount;
|
|
29
|
+
this.isDefault = isDefault;
|
|
18
30
|
}
|
|
19
31
|
static fromJSON(payload) {
|
|
20
32
|
const id = payload.id ? String(payload.id) : '';
|
|
21
33
|
if (!id) {
|
|
22
34
|
throw new Error('Project payload missing id');
|
|
23
35
|
}
|
|
24
|
-
return new ProjectSummary(id, payload.name ?? undefined, payload.
|
|
36
|
+
return new ProjectSummary(id, payload.name ?? undefined, payload.daemonHost ?? payload.daemon_host ?? null, payload.workspacePath ?? payload.workspace_path ?? null, payload.repoRoot ?? payload.repo_root ?? null, payload.worktreeBranch ?? payload.worktree_branch ?? null, payload.lastCommit ?? payload.last_commit ?? null, payload.fileCount ?? payload.file_count ?? null, payload.isDefault ?? payload.is_default ?? undefined);
|
|
25
37
|
}
|
|
26
38
|
asObject() {
|
|
27
39
|
return {
|
|
28
40
|
id: this.id,
|
|
29
41
|
name: this.name,
|
|
30
|
-
|
|
42
|
+
daemonHost: this.daemonHost,
|
|
43
|
+
workspacePath: this.workspacePath,
|
|
44
|
+
repoRoot: this.repoRoot,
|
|
45
|
+
worktreeBranch: this.worktreeBranch,
|
|
46
|
+
lastCommit: this.lastCommit,
|
|
47
|
+
fileCount: this.fileCount,
|
|
48
|
+
isDefault: this.isDefault,
|
|
31
49
|
};
|
|
32
50
|
}
|
|
33
51
|
}
|
|
@@ -270,8 +288,14 @@ export class BackendApiClient {
|
|
|
270
288
|
return {
|
|
271
289
|
id: payload.id,
|
|
272
290
|
name: payload.name,
|
|
273
|
-
description: payload.description,
|
|
274
291
|
metadata: payload.metadata ?? undefined,
|
|
292
|
+
daemonHost: payload.daemonHost ?? payload.daemon_host ?? undefined,
|
|
293
|
+
workspacePath: payload.workspacePath ?? payload.workspace_path ?? undefined,
|
|
294
|
+
repoRoot: payload.repoRoot ?? payload.repo_root ?? undefined,
|
|
295
|
+
worktreeBranch: payload.worktreeBranch ?? payload.worktree_branch ?? undefined,
|
|
296
|
+
lastCommit: payload.lastCommit ?? payload.last_commit ?? undefined,
|
|
297
|
+
fileCount: payload.fileCount ?? payload.file_count ?? undefined,
|
|
298
|
+
isDefault: payload.isDefault ?? payload.is_default ?? undefined,
|
|
275
299
|
};
|
|
276
300
|
}
|
|
277
301
|
async updateProject(projectId, params) {
|
package/dist/client.d.ts
CHANGED
|
@@ -47,6 +47,10 @@ export interface StopTaskEvent {
|
|
|
47
47
|
requestId?: string;
|
|
48
48
|
reason?: string;
|
|
49
49
|
}
|
|
50
|
+
export interface FlushPendingUpstreamEventsOptions {
|
|
51
|
+
timeoutMs?: number;
|
|
52
|
+
retryIntervalMs?: number;
|
|
53
|
+
}
|
|
50
54
|
export declare class ConductorClient {
|
|
51
55
|
private readonly config;
|
|
52
56
|
private readonly env;
|
|
@@ -68,6 +72,10 @@ export declare class ConductorClient {
|
|
|
68
72
|
constructor(init: ConductorClientInit);
|
|
69
73
|
static connect(options?: ConductorClientConnectOptions): Promise<ConductorClient>;
|
|
70
74
|
close(): Promise<void>;
|
|
75
|
+
flushPendingUpstreamEvents(options?: FlushPendingUpstreamEventsOptions): Promise<{
|
|
76
|
+
flushed: boolean;
|
|
77
|
+
remaining: number;
|
|
78
|
+
}>;
|
|
71
79
|
forceReconnect(reason?: string): Promise<void>;
|
|
72
80
|
createTaskSession(payload: Record<string, any>): Promise<Record<string, any>>;
|
|
73
81
|
sendMessage(taskId: string, content: string, metadata?: Record<string, any>): Promise<Record<string, any>>;
|
|
@@ -96,7 +104,8 @@ export declare class ConductorClient {
|
|
|
96
104
|
receiveMessages(taskId: string, limit?: number): Promise<Record<string, any>>;
|
|
97
105
|
ackMessages(taskId: string, ackToken?: string | null): Promise<Record<string, any> | undefined>;
|
|
98
106
|
listProjects(): Promise<Record<string, any>>;
|
|
99
|
-
createProject(name: string,
|
|
107
|
+
createProject(name: string, metadata?: Record<string, unknown>): Promise<Record<string, any>>;
|
|
108
|
+
createProject(payload: Record<string, any>): Promise<Record<string, any>>;
|
|
100
109
|
listTasks(payload?: Record<string, any>): Promise<Record<string, any>>;
|
|
101
110
|
getLocalProjectRecord(payload?: Record<string, any>): Promise<Record<string, any>>;
|
|
102
111
|
getLocalTaskRecord(payload?: Record<string, any>): Promise<Record<string, any>>;
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
2
4
|
import { BackendApiClient, BackendApiError } from './backend/index.js';
|
|
3
5
|
import { loadConfig } from './config/index.js';
|
|
6
|
+
import { ProjectContext } from './context/index.js';
|
|
4
7
|
import { getPlanLimitMessageFromError } from './limits/index.js';
|
|
5
8
|
import { MessageRouter } from './message/index.js';
|
|
6
9
|
import { DownstreamCursorStore, DurableUpstreamOutboxStore, normalizeDownstreamCommandCursor, } from './outbox/index.js';
|
|
@@ -91,10 +94,30 @@ export class ConductorClient {
|
|
|
91
94
|
if (this.closed) {
|
|
92
95
|
return;
|
|
93
96
|
}
|
|
94
|
-
this.closed = true;
|
|
95
97
|
this.clearDurableOutboxTimer();
|
|
98
|
+
await this.flushPendingUpstreamEvents({
|
|
99
|
+
timeoutMs: 2_000,
|
|
100
|
+
retryIntervalMs: 100,
|
|
101
|
+
});
|
|
102
|
+
this.closed = true;
|
|
96
103
|
await this.wsClient.disconnect();
|
|
97
104
|
}
|
|
105
|
+
async flushPendingUpstreamEvents(options = {}) {
|
|
106
|
+
const timeoutMs = Math.max(0, options.timeoutMs ?? 5_000);
|
|
107
|
+
const retryIntervalMs = Math.max(10, options.retryIntervalMs ?? 250);
|
|
108
|
+
const startedAt = Date.now();
|
|
109
|
+
while (true) {
|
|
110
|
+
await this.requestDurableOutboxFlush(true);
|
|
111
|
+
const remaining = this.upstreamOutbox.load().length;
|
|
112
|
+
if (remaining === 0) {
|
|
113
|
+
return { flushed: true, remaining: 0 };
|
|
114
|
+
}
|
|
115
|
+
if (Date.now() - startedAt >= timeoutMs) {
|
|
116
|
+
return { flushed: false, remaining };
|
|
117
|
+
}
|
|
118
|
+
await sleep(retryIntervalMs);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
98
121
|
async forceReconnect(reason = 'manual_reconnect') {
|
|
99
122
|
if (typeof this.wsClient.forceReconnect === 'function') {
|
|
100
123
|
await this.wsClient.forceReconnect(reason);
|
|
@@ -318,12 +341,14 @@ export class ConductorClient {
|
|
|
318
341
|
: {
|
|
319
342
|
id: project.id,
|
|
320
343
|
name: project.name ?? null,
|
|
321
|
-
description: project.description ?? null,
|
|
322
344
|
}),
|
|
323
345
|
};
|
|
324
346
|
}
|
|
325
|
-
async createProject(
|
|
326
|
-
const
|
|
347
|
+
async createProject(nameOrPayload, metadata) {
|
|
348
|
+
const payload = typeof nameOrPayload === 'string'
|
|
349
|
+
? { name: nameOrPayload, metadata }
|
|
350
|
+
: { ...nameOrPayload };
|
|
351
|
+
const project = await this.backendApi.createProject(payload);
|
|
327
352
|
return typeof project.asObject === 'function' ? project.asObject() : project;
|
|
328
353
|
}
|
|
329
354
|
async listTasks(payload = {}) {
|
|
@@ -436,12 +461,20 @@ export class ConductorClient {
|
|
|
436
461
|
};
|
|
437
462
|
}
|
|
438
463
|
async matchProjectByPath(payload = {}) {
|
|
439
|
-
const
|
|
440
|
-
|
|
464
|
+
const daemonHost = (typeof payload.daemon_host === 'string' && payload.daemon_host.trim()) ||
|
|
465
|
+
(typeof payload.daemonHost === 'string' && payload.daemonHost.trim()) ||
|
|
466
|
+
(typeof payload.hostname === 'string' && payload.hostname.trim()) ||
|
|
467
|
+
this.agentHost;
|
|
468
|
+
const projectPath = resolveWorkspacePath(typeof payload.project_path === 'string' && payload.project_path
|
|
441
469
|
? payload.project_path
|
|
442
|
-
:
|
|
470
|
+
: typeof payload.projectPath === 'string' && payload.projectPath
|
|
471
|
+
? payload.projectPath
|
|
472
|
+
: this.projectPath);
|
|
473
|
+
if (!projectPath) {
|
|
474
|
+
throw new Error('project_path is required');
|
|
475
|
+
}
|
|
443
476
|
const result = await this.backendApi.matchProjectByPath({
|
|
444
|
-
|
|
477
|
+
daemon_host: daemonHost,
|
|
445
478
|
path: projectPath,
|
|
446
479
|
});
|
|
447
480
|
if (result.project) {
|
|
@@ -461,20 +494,36 @@ export class ConductorClient {
|
|
|
461
494
|
if (!projectId) {
|
|
462
495
|
throw new Error('project_id is required');
|
|
463
496
|
}
|
|
464
|
-
const
|
|
465
|
-
|
|
497
|
+
const daemonHost = (typeof payload.daemon_host === 'string' && payload.daemon_host.trim()) ||
|
|
498
|
+
(typeof payload.daemonHost === 'string' && payload.daemonHost.trim()) ||
|
|
499
|
+
(typeof payload.hostname === 'string' && payload.hostname.trim()) ||
|
|
500
|
+
this.agentHost;
|
|
501
|
+
const projectPath = resolveWorkspacePath(typeof payload.project_path === 'string' && payload.project_path
|
|
466
502
|
? payload.project_path
|
|
467
|
-
:
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
503
|
+
: typeof payload.projectPath === 'string' && payload.projectPath
|
|
504
|
+
? payload.projectPath
|
|
505
|
+
: this.projectPath);
|
|
506
|
+
const snapshot = resolveWorkspaceSnapshot(projectPath);
|
|
507
|
+
const boundProject = await this.backendApi.updateProject(projectId, {
|
|
508
|
+
bindingConfirmed: true,
|
|
509
|
+
daemonHost,
|
|
510
|
+
workspacePath: snapshot.projectRoot,
|
|
511
|
+
repoRoot: snapshot.repoRoot,
|
|
512
|
+
worktreeBranch: snapshot.worktreeBranch,
|
|
513
|
+
lastCommit: snapshot.lastCommit,
|
|
514
|
+
fileCount: snapshot.fileCount,
|
|
515
|
+
});
|
|
474
516
|
return {
|
|
475
517
|
success: true,
|
|
476
|
-
|
|
477
|
-
|
|
518
|
+
project_id: boundProject.id,
|
|
519
|
+
project_name: boundProject.name ?? null,
|
|
520
|
+
hostname: daemonHost,
|
|
521
|
+
daemon_host: daemonHost,
|
|
522
|
+
path: snapshot.projectRoot,
|
|
523
|
+
repo_root: snapshot.repoRoot ?? null,
|
|
524
|
+
worktree_branch: snapshot.worktreeBranch ?? null,
|
|
525
|
+
last_commit: snapshot.lastCommit ?? null,
|
|
526
|
+
file_count: snapshot.fileCount ?? null,
|
|
478
527
|
};
|
|
479
528
|
}
|
|
480
529
|
handleBackendEvent = async (payload) => {
|
|
@@ -886,3 +935,26 @@ function normalizeDeliveryScopeId(scopeId) {
|
|
|
886
935
|
const normalized = String(scopeId || '').trim();
|
|
887
936
|
return normalized || 'default';
|
|
888
937
|
}
|
|
938
|
+
function resolveWorkspacePath(candidate) {
|
|
939
|
+
const trimmed = typeof candidate === 'string' ? candidate.trim() : '';
|
|
940
|
+
if (!trimmed) {
|
|
941
|
+
return '';
|
|
942
|
+
}
|
|
943
|
+
try {
|
|
944
|
+
return fs.realpathSync(trimmed);
|
|
945
|
+
}
|
|
946
|
+
catch {
|
|
947
|
+
return path.resolve(trimmed);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
function resolveWorkspaceSnapshot(projectPath) {
|
|
951
|
+
try {
|
|
952
|
+
const context = new ProjectContext(projectPath);
|
|
953
|
+
return context.snapshot();
|
|
954
|
+
}
|
|
955
|
+
catch {
|
|
956
|
+
return {
|
|
957
|
+
projectRoot: resolveWorkspacePath(projectPath),
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
}
|
|
@@ -2,13 +2,24 @@ export interface GuessResult {
|
|
|
2
2
|
projectRoot: string;
|
|
3
3
|
repoRoot?: string;
|
|
4
4
|
}
|
|
5
|
+
export interface WorkspaceSnapshot {
|
|
6
|
+
projectRoot: string;
|
|
7
|
+
repoRoot?: string;
|
|
8
|
+
worktreeBranch?: string;
|
|
9
|
+
lastCommit?: string;
|
|
10
|
+
fileCount?: number;
|
|
11
|
+
}
|
|
5
12
|
export declare class ProjectContext {
|
|
6
13
|
private readonly root;
|
|
7
14
|
constructor(targetPath?: string);
|
|
8
15
|
guess(): GuessResult;
|
|
16
|
+
snapshot(): WorkspaceSnapshot;
|
|
9
17
|
listFiles(relativeToRepo?: boolean): string[];
|
|
10
18
|
readFile(relativePath: string): string;
|
|
11
19
|
getDiff(staged?: boolean): string;
|
|
12
20
|
private gitRoot;
|
|
21
|
+
private gitBranch;
|
|
22
|
+
private gitHead;
|
|
23
|
+
private gitFileCount;
|
|
13
24
|
private gitListFiles;
|
|
14
25
|
}
|
|
@@ -14,6 +14,21 @@ export class ProjectContext {
|
|
|
14
14
|
repoRoot: repoRoot ?? undefined,
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
+
snapshot() {
|
|
18
|
+
const guess = this.guess();
|
|
19
|
+
if (!guess.repoRoot) {
|
|
20
|
+
return {
|
|
21
|
+
projectRoot: guess.projectRoot,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
projectRoot: guess.projectRoot,
|
|
26
|
+
repoRoot: guess.repoRoot,
|
|
27
|
+
worktreeBranch: this.gitBranch(guess.repoRoot) ?? undefined,
|
|
28
|
+
lastCommit: this.gitHead(guess.repoRoot) ?? undefined,
|
|
29
|
+
fileCount: this.gitFileCount(guess.repoRoot) ?? undefined,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
17
32
|
listFiles(relativeToRepo = true) {
|
|
18
33
|
const guess = this.guess();
|
|
19
34
|
if (guess.repoRoot) {
|
|
@@ -55,6 +70,35 @@ export class ProjectContext {
|
|
|
55
70
|
return null;
|
|
56
71
|
}
|
|
57
72
|
}
|
|
73
|
+
gitBranch(repoRoot) {
|
|
74
|
+
try {
|
|
75
|
+
const branch = runGit(['rev-parse', '--abbrev-ref', 'HEAD'], repoRoot).trim();
|
|
76
|
+
if (!branch || branch === 'HEAD') {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return branch;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
gitHead(repoRoot) {
|
|
86
|
+
try {
|
|
87
|
+
const head = runGit(['rev-parse', 'HEAD'], repoRoot).trim();
|
|
88
|
+
return head || null;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
gitFileCount(repoRoot) {
|
|
95
|
+
try {
|
|
96
|
+
return this.gitListFiles(repoRoot).length;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
58
102
|
gitListFiles(repoRoot) {
|
|
59
103
|
try {
|
|
60
104
|
const output = runGit(['ls-files'], repoRoot);
|
package/dist/ws/client.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ export interface WebSocketDisconnectEvent {
|
|
|
41
41
|
}
|
|
42
42
|
export interface WebSocketClientOptions {
|
|
43
43
|
reconnectDelay?: number;
|
|
44
|
+
duplicateHostReconnectDelay?: number;
|
|
44
45
|
heartbeatInterval?: number;
|
|
45
46
|
extraHeaders?: Record<string, string>;
|
|
46
47
|
connectImpl?: ConnectImpl;
|
|
@@ -54,6 +55,7 @@ export declare class ConductorWebSocketClient {
|
|
|
54
55
|
private readonly url;
|
|
55
56
|
private readonly token;
|
|
56
57
|
private readonly reconnectDelay;
|
|
58
|
+
private readonly duplicateHostReconnectDelay;
|
|
57
59
|
private readonly heartbeatInterval;
|
|
58
60
|
private readonly connectImpl;
|
|
59
61
|
private readonly onConnected?;
|
|
@@ -83,6 +85,7 @@ export declare class ConductorWebSocketClient {
|
|
|
83
85
|
private listenLoop;
|
|
84
86
|
private heartbeatLoop;
|
|
85
87
|
private handleConnectionLoss;
|
|
88
|
+
private getReconnectDelay;
|
|
86
89
|
private dispatch;
|
|
87
90
|
private notifyConnected;
|
|
88
91
|
private notifyReconnected;
|
package/dist/ws/client.js
CHANGED
|
@@ -12,6 +12,7 @@ export class ConductorWebSocketClient {
|
|
|
12
12
|
url;
|
|
13
13
|
token;
|
|
14
14
|
reconnectDelay;
|
|
15
|
+
duplicateHostReconnectDelay;
|
|
15
16
|
heartbeatInterval;
|
|
16
17
|
connectImpl;
|
|
17
18
|
onConnected;
|
|
@@ -32,6 +33,7 @@ export class ConductorWebSocketClient {
|
|
|
32
33
|
this.url = config.resolvedWebsocketUrl;
|
|
33
34
|
this.token = config.agentToken;
|
|
34
35
|
this.reconnectDelay = options.reconnectDelay ?? 10_000;
|
|
36
|
+
this.duplicateHostReconnectDelay = options.duplicateHostReconnectDelay ?? Math.max(this.reconnectDelay, 2_000);
|
|
35
37
|
this.heartbeatInterval = options.heartbeatInterval ?? 20_000;
|
|
36
38
|
this.extraHeaders = {
|
|
37
39
|
'x-conductor-host': options.hostName ?? defaultHostName(),
|
|
@@ -184,8 +186,16 @@ export class ConductorWebSocketClient {
|
|
|
184
186
|
this.conn = null;
|
|
185
187
|
this.runtime = null;
|
|
186
188
|
this.notifyDisconnected(this.buildDisconnectEvent(runtime));
|
|
189
|
+
await wait(this.getReconnectDelay(runtime), this.waitController.signal);
|
|
187
190
|
await this.openConnection(true);
|
|
188
191
|
}
|
|
192
|
+
getReconnectDelay(runtime) {
|
|
193
|
+
if (runtime?.closeCode === 4002 &&
|
|
194
|
+
String(runtime?.closeReason || '').trim().toLowerCase() === 'duplicate-host') {
|
|
195
|
+
return this.duplicateHostReconnectDelay;
|
|
196
|
+
}
|
|
197
|
+
return this.reconnectDelay;
|
|
198
|
+
}
|
|
189
199
|
async dispatch(message) {
|
|
190
200
|
const now = Date.now();
|
|
191
201
|
if (this.runtime) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@love-moon/conductor-sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.33",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -27,5 +27,5 @@
|
|
|
27
27
|
"typescript": "^5.6.3",
|
|
28
28
|
"vitest": "^2.1.4"
|
|
29
29
|
},
|
|
30
|
-
"gitCommitId": "
|
|
30
|
+
"gitCommitId": "db7f9bf"
|
|
31
31
|
}
|