@yushaw/sanqian-sdk 0.2.0 → 0.2.1
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/index.d.mts +40 -4
- package/dist/index.d.ts +40 -4
- package/dist/index.js +101 -106
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +101 -106
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -11,6 +11,11 @@ var DiscoveryManager = class {
|
|
|
11
11
|
watcher = null;
|
|
12
12
|
onChange = null;
|
|
13
13
|
pollInterval = null;
|
|
14
|
+
/**
|
|
15
|
+
* Cached executable path from last successful connection.json read.
|
|
16
|
+
* Persists even when Sanqian is not running, for use in auto-launch.
|
|
17
|
+
*/
|
|
18
|
+
cachedExecutable = null;
|
|
14
19
|
/**
|
|
15
20
|
* Get the path to connection.json
|
|
16
21
|
*/
|
|
@@ -36,6 +41,9 @@ var DiscoveryManager = class {
|
|
|
36
41
|
if (!info.port || !info.token || !info.pid) {
|
|
37
42
|
return null;
|
|
38
43
|
}
|
|
44
|
+
if (info.executable) {
|
|
45
|
+
this.cachedExecutable = info.executable;
|
|
46
|
+
}
|
|
39
47
|
if (!this.isProcessRunning(info.pid)) {
|
|
40
48
|
return null;
|
|
41
49
|
}
|
|
@@ -197,9 +205,22 @@ var DiscoveryManager = class {
|
|
|
197
205
|
/**
|
|
198
206
|
* Launch Sanqian in hidden/tray mode
|
|
199
207
|
* Returns true if launch was initiated successfully
|
|
208
|
+
*
|
|
209
|
+
* Priority for finding Sanqian executable:
|
|
210
|
+
* 1. customPath parameter (if provided)
|
|
211
|
+
* 2. Cached executable from connection.json (most reliable)
|
|
212
|
+
* 3. Search in standard installation locations (fallback)
|
|
200
213
|
*/
|
|
201
214
|
launchSanqian(customPath) {
|
|
202
|
-
|
|
215
|
+
let sanqianPath = null;
|
|
216
|
+
if (customPath) {
|
|
217
|
+
sanqianPath = this.findSanqianPath(customPath);
|
|
218
|
+
} else if (this.cachedExecutable && existsSync(this.cachedExecutable)) {
|
|
219
|
+
sanqianPath = this.cachedExecutable;
|
|
220
|
+
console.log(`[SDK] Using cached executable: ${sanqianPath}`);
|
|
221
|
+
} else {
|
|
222
|
+
sanqianPath = this.findSanqianPath();
|
|
223
|
+
}
|
|
203
224
|
if (!sanqianPath) {
|
|
204
225
|
console.error("[SDK] Sanqian executable not found");
|
|
205
226
|
return false;
|
|
@@ -263,6 +284,8 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
263
284
|
heartbeatAckPending = false;
|
|
264
285
|
missedHeartbeats = 0;
|
|
265
286
|
static MAX_MISSED_HEARTBEATS = 2;
|
|
287
|
+
// Connection promise for deduplication (prevents multiple concurrent connect attempts)
|
|
288
|
+
connectingPromise = null;
|
|
266
289
|
// Event listeners
|
|
267
290
|
eventListeners = /* @__PURE__ */ new Map();
|
|
268
291
|
constructor(config) {
|
|
@@ -288,40 +311,12 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
288
311
|
*
|
|
289
312
|
* If autoLaunchSanqian is enabled and Sanqian is not running,
|
|
290
313
|
* SDK will attempt to start it in hidden/tray mode.
|
|
314
|
+
*
|
|
315
|
+
* @throws Error if Sanqian executable cannot be found (when autoLaunchSanqian is enabled)
|
|
316
|
+
* @throws Error if connection times out after 2 minutes
|
|
291
317
|
*/
|
|
292
318
|
async connect() {
|
|
293
|
-
|
|
294
|
-
if (!info) {
|
|
295
|
-
if (this.config.autoLaunchSanqian) {
|
|
296
|
-
console.log("[SDK] Sanqian not running, attempting to launch...");
|
|
297
|
-
const launched = this.discovery.launchSanqian(this.config.sanqianPath);
|
|
298
|
-
if (launched) {
|
|
299
|
-
console.log("[SDK] Sanqian launch initiated, waiting for startup...");
|
|
300
|
-
} else {
|
|
301
|
-
console.warn("[SDK] Failed to launch Sanqian, will wait for manual start");
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
return new Promise((resolve, reject) => {
|
|
305
|
-
console.log("[SDK] Waiting for Sanqian...");
|
|
306
|
-
const timeout = setTimeout(() => {
|
|
307
|
-
this.discovery.stopWatching();
|
|
308
|
-
reject(new Error("Sanqian connection timeout"));
|
|
309
|
-
}, 6e4);
|
|
310
|
-
this.discovery.startWatching(async (newInfo) => {
|
|
311
|
-
if (newInfo) {
|
|
312
|
-
clearTimeout(timeout);
|
|
313
|
-
this.discovery.stopWatching();
|
|
314
|
-
try {
|
|
315
|
-
await this.connectWithInfo(newInfo);
|
|
316
|
-
resolve();
|
|
317
|
-
} catch (e) {
|
|
318
|
-
reject(e);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
await this.connectWithInfo(info);
|
|
319
|
+
return this.ensureReady();
|
|
325
320
|
}
|
|
326
321
|
/**
|
|
327
322
|
* Connect with known connection info
|
|
@@ -675,57 +670,77 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
675
670
|
return this.state.connected && this.state.registered;
|
|
676
671
|
}
|
|
677
672
|
/**
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
*
|
|
673
|
+
* Ensure SDK is ready for API calls.
|
|
674
|
+
*
|
|
675
|
+
* This is the unified entry point for all API methods that require a connection.
|
|
676
|
+
* It handles:
|
|
677
|
+
* - Already connected: returns immediately
|
|
678
|
+
* - Connection in progress: waits for existing attempt (deduplication)
|
|
679
|
+
* - Not connected: initiates connection (with optional auto-launch)
|
|
680
|
+
*
|
|
681
|
+
* Design pattern: gRPC wait-for-ready + ClientFactory promise deduplication
|
|
682
|
+
*
|
|
683
|
+
* @throws Error if connection fails or times out
|
|
681
684
|
*/
|
|
682
|
-
async
|
|
685
|
+
async ensureReady() {
|
|
683
686
|
if (this.isConnected()) {
|
|
684
687
|
return;
|
|
685
688
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
689
|
+
if (this.connectingPromise) {
|
|
690
|
+
console.log("[SDK] Connection already in progress, waiting...");
|
|
691
|
+
return this.connectingPromise;
|
|
692
|
+
}
|
|
693
|
+
this.connectingPromise = this.doFullConnect();
|
|
694
|
+
try {
|
|
695
|
+
await this.connectingPromise;
|
|
696
|
+
} finally {
|
|
697
|
+
this.connectingPromise = null;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Internal: Perform full connection flow
|
|
702
|
+
* Handles launching Sanqian if needed, then connecting and registering.
|
|
703
|
+
*
|
|
704
|
+
* Note: This method is protected by connectingPromise deduplication in ensureReady(),
|
|
705
|
+
* so there's no need for additional launch-in-progress tracking within a single instance.
|
|
706
|
+
*/
|
|
707
|
+
async doFullConnect() {
|
|
708
|
+
console.log("[SDK] Starting full connection flow...");
|
|
709
|
+
let info = this.discovery.read();
|
|
710
|
+
if (!info) {
|
|
711
|
+
if (this.config.autoLaunchSanqian) {
|
|
712
|
+
console.log("[SDK] Sanqian not running, attempting to launch...");
|
|
713
|
+
const launched = this.discovery.launchSanqian(this.config.sanqianPath);
|
|
714
|
+
if (!launched) {
|
|
715
|
+
throw new Error(
|
|
716
|
+
"Sanqian executable not found. Please ensure Sanqian is installed, or provide a custom path via sanqianPath config option."
|
|
717
|
+
);
|
|
707
718
|
}
|
|
708
|
-
|
|
719
|
+
console.log("[SDK] Sanqian launch initiated, waiting for startup...");
|
|
720
|
+
info = await this.waitForSanqianStartup();
|
|
721
|
+
} else {
|
|
722
|
+
throw new Error("Sanqian is not running. Please start it manually.");
|
|
709
723
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
} else {
|
|
725
|
-
this.scheduleReconnect();
|
|
726
|
-
}
|
|
724
|
+
}
|
|
725
|
+
await this.connectWithInfo(info);
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Wait for Sanqian to start and connection.json to become available
|
|
729
|
+
*/
|
|
730
|
+
async waitForSanqianStartup(timeout = 12e4) {
|
|
731
|
+
const startTime = Date.now();
|
|
732
|
+
const pollInterval = 500;
|
|
733
|
+
while (Date.now() - startTime < timeout) {
|
|
734
|
+
const info = this.discovery.read();
|
|
735
|
+
if (info) {
|
|
736
|
+
console.log("[SDK] Sanqian started, connection info available");
|
|
737
|
+
return info;
|
|
727
738
|
}
|
|
728
|
-
|
|
739
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
740
|
+
}
|
|
741
|
+
throw new Error(
|
|
742
|
+
"Sanqian startup timeout (2 minutes). Please check if Sanqian started correctly."
|
|
743
|
+
);
|
|
729
744
|
}
|
|
730
745
|
/**
|
|
731
746
|
* Update tool list dynamically
|
|
@@ -762,9 +777,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
762
777
|
* @returns Full agent info (or agent_id string for backward compatibility)
|
|
763
778
|
*/
|
|
764
779
|
async createAgent(config) {
|
|
765
|
-
|
|
766
|
-
throw new Error("Not connected to Sanqian");
|
|
767
|
-
}
|
|
780
|
+
await this.ensureReady();
|
|
768
781
|
const msgId = this.generateId();
|
|
769
782
|
const message = {
|
|
770
783
|
id: msgId,
|
|
@@ -789,9 +802,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
789
802
|
* List all private agents owned by this app
|
|
790
803
|
*/
|
|
791
804
|
async listAgents() {
|
|
792
|
-
|
|
793
|
-
throw new Error("Not connected to Sanqian");
|
|
794
|
-
}
|
|
805
|
+
await this.ensureReady();
|
|
795
806
|
const msgId = this.generateId();
|
|
796
807
|
const message = {
|
|
797
808
|
id: msgId,
|
|
@@ -807,9 +818,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
807
818
|
* Delete a private agent
|
|
808
819
|
*/
|
|
809
820
|
async deleteAgent(agentId) {
|
|
810
|
-
|
|
811
|
-
throw new Error("Not connected to Sanqian");
|
|
812
|
-
}
|
|
821
|
+
await this.ensureReady();
|
|
813
822
|
const msgId = this.generateId();
|
|
814
823
|
const message = {
|
|
815
824
|
id: msgId,
|
|
@@ -830,9 +839,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
830
839
|
* @returns Updated agent info
|
|
831
840
|
*/
|
|
832
841
|
async updateAgent(agentId, updates) {
|
|
833
|
-
|
|
834
|
-
throw new Error("Not connected to Sanqian");
|
|
835
|
-
}
|
|
842
|
+
await this.ensureReady();
|
|
836
843
|
const msgId = this.generateId();
|
|
837
844
|
const message = {
|
|
838
845
|
id: msgId,
|
|
@@ -853,9 +860,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
853
860
|
* List conversations for this app
|
|
854
861
|
*/
|
|
855
862
|
async listConversations(options) {
|
|
856
|
-
|
|
857
|
-
throw new Error("Not connected to Sanqian");
|
|
858
|
-
}
|
|
863
|
+
await this.ensureReady();
|
|
859
864
|
const msgId = this.generateId();
|
|
860
865
|
const message = {
|
|
861
866
|
id: msgId,
|
|
@@ -877,9 +882,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
877
882
|
* Get conversation details with messages
|
|
878
883
|
*/
|
|
879
884
|
async getConversation(conversationId, options) {
|
|
880
|
-
|
|
881
|
-
throw new Error("Not connected to Sanqian");
|
|
882
|
-
}
|
|
885
|
+
await this.ensureReady();
|
|
883
886
|
const msgId = this.generateId();
|
|
884
887
|
const message = {
|
|
885
888
|
id: msgId,
|
|
@@ -899,9 +902,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
899
902
|
* Delete a conversation
|
|
900
903
|
*/
|
|
901
904
|
async deleteConversation(conversationId) {
|
|
902
|
-
|
|
903
|
-
throw new Error("Not connected to Sanqian");
|
|
904
|
-
}
|
|
905
|
+
await this.ensureReady();
|
|
905
906
|
const msgId = this.generateId();
|
|
906
907
|
const message = {
|
|
907
908
|
id: msgId,
|
|
@@ -931,10 +932,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
931
932
|
* @returns Chat response with assistant message and conversation ID
|
|
932
933
|
*/
|
|
933
934
|
async chat(agentId, messages, options) {
|
|
934
|
-
|
|
935
|
-
console.log("[SDK] Not connected, attempting to reconnect...");
|
|
936
|
-
await this.waitForConnection();
|
|
937
|
-
}
|
|
935
|
+
await this.ensureReady();
|
|
938
936
|
const msgId = this.generateId();
|
|
939
937
|
const message = {
|
|
940
938
|
id: msgId,
|
|
@@ -973,10 +971,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
973
971
|
* @returns AsyncIterable of stream events
|
|
974
972
|
*/
|
|
975
973
|
async *chatStream(agentId, messages, options) {
|
|
976
|
-
|
|
977
|
-
console.log("[SDK] Not connected, attempting to reconnect...");
|
|
978
|
-
await this.waitForConnection();
|
|
979
|
-
}
|
|
974
|
+
await this.ensureReady();
|
|
980
975
|
const msgId = this.generateId();
|
|
981
976
|
const message = {
|
|
982
977
|
id: msgId,
|