@yushaw/sanqian-sdk 0.1.6 → 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 +103 -110
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +103 -110
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
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,17 +839,13 @@ 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,
|
|
839
846
|
type: "update_agent",
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
...updates
|
|
843
|
-
}
|
|
847
|
+
agent_id: agentId,
|
|
848
|
+
updates
|
|
844
849
|
};
|
|
845
850
|
const response = await this.sendAndWait(message, msgId, 1e4);
|
|
846
851
|
if (!response.success || !response.agent) {
|
|
@@ -855,9 +860,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
855
860
|
* List conversations for this app
|
|
856
861
|
*/
|
|
857
862
|
async listConversations(options) {
|
|
858
|
-
|
|
859
|
-
throw new Error("Not connected to Sanqian");
|
|
860
|
-
}
|
|
863
|
+
await this.ensureReady();
|
|
861
864
|
const msgId = this.generateId();
|
|
862
865
|
const message = {
|
|
863
866
|
id: msgId,
|
|
@@ -879,9 +882,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
879
882
|
* Get conversation details with messages
|
|
880
883
|
*/
|
|
881
884
|
async getConversation(conversationId, options) {
|
|
882
|
-
|
|
883
|
-
throw new Error("Not connected to Sanqian");
|
|
884
|
-
}
|
|
885
|
+
await this.ensureReady();
|
|
885
886
|
const msgId = this.generateId();
|
|
886
887
|
const message = {
|
|
887
888
|
id: msgId,
|
|
@@ -901,9 +902,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
901
902
|
* Delete a conversation
|
|
902
903
|
*/
|
|
903
904
|
async deleteConversation(conversationId) {
|
|
904
|
-
|
|
905
|
-
throw new Error("Not connected to Sanqian");
|
|
906
|
-
}
|
|
905
|
+
await this.ensureReady();
|
|
907
906
|
const msgId = this.generateId();
|
|
908
907
|
const message = {
|
|
909
908
|
id: msgId,
|
|
@@ -933,10 +932,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
933
932
|
* @returns Chat response with assistant message and conversation ID
|
|
934
933
|
*/
|
|
935
934
|
async chat(agentId, messages, options) {
|
|
936
|
-
|
|
937
|
-
console.log("[SDK] Not connected, attempting to reconnect...");
|
|
938
|
-
await this.waitForConnection();
|
|
939
|
-
}
|
|
935
|
+
await this.ensureReady();
|
|
940
936
|
const msgId = this.generateId();
|
|
941
937
|
const message = {
|
|
942
938
|
id: msgId,
|
|
@@ -975,10 +971,7 @@ var SanqianSDK = class _SanqianSDK {
|
|
|
975
971
|
* @returns AsyncIterable of stream events
|
|
976
972
|
*/
|
|
977
973
|
async *chatStream(agentId, messages, options) {
|
|
978
|
-
|
|
979
|
-
console.log("[SDK] Not connected, attempting to reconnect...");
|
|
980
|
-
await this.waitForConnection();
|
|
981
|
-
}
|
|
974
|
+
await this.ensureReady();
|
|
982
975
|
const msgId = this.generateId();
|
|
983
976
|
const message = {
|
|
984
977
|
id: msgId,
|