@granular-software/sdk 0.3.2 → 0.3.4
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/adapters/anthropic.d.mts +1 -1
- package/dist/adapters/anthropic.d.ts +1 -1
- package/dist/adapters/langchain.d.mts +1 -1
- package/dist/adapters/langchain.d.ts +1 -1
- package/dist/adapters/mastra.d.mts +1 -1
- package/dist/adapters/mastra.d.ts +1 -1
- package/dist/adapters/openai.d.mts +1 -1
- package/dist/adapters/openai.d.ts +1 -1
- package/dist/cli/index.js +443 -22
- package/dist/index.d.mts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +110 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +110 -16
- package/dist/index.mjs.map +1 -1
- package/dist/{types-CnX4jXYQ.d.mts → types-D5B8WlF4.d.mts} +27 -1
- package/dist/{types-CnX4jXYQ.d.ts → types-D5B8WlF4.d.ts} +27 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3944,6 +3944,10 @@ var WSClient = class {
|
|
|
3944
3944
|
*/
|
|
3945
3945
|
async connect() {
|
|
3946
3946
|
this.isExplicitlyDisconnected = false;
|
|
3947
|
+
if (this.reconnectTimer) {
|
|
3948
|
+
clearTimeout(this.reconnectTimer);
|
|
3949
|
+
this.reconnectTimer = null;
|
|
3950
|
+
}
|
|
3947
3951
|
let WebSocketClass = this.options.WebSocketCtor || GlobalWebSocket;
|
|
3948
3952
|
if (!WebSocketClass) {
|
|
3949
3953
|
try {
|
|
@@ -3965,6 +3969,10 @@ var WSClient = class {
|
|
|
3965
3969
|
const socket = this.ws;
|
|
3966
3970
|
if (typeof socket.on === "function") {
|
|
3967
3971
|
socket.on("open", () => {
|
|
3972
|
+
if (this.reconnectTimer) {
|
|
3973
|
+
clearTimeout(this.reconnectTimer);
|
|
3974
|
+
this.reconnectTimer = null;
|
|
3975
|
+
}
|
|
3968
3976
|
this.emit("open", {});
|
|
3969
3977
|
resolve();
|
|
3970
3978
|
});
|
|
@@ -3982,12 +3990,20 @@ var WSClient = class {
|
|
|
3982
3990
|
reject(error);
|
|
3983
3991
|
}
|
|
3984
3992
|
});
|
|
3985
|
-
socket.on("close", () => {
|
|
3986
|
-
this.
|
|
3987
|
-
|
|
3993
|
+
socket.on("close", (code, reason) => {
|
|
3994
|
+
this.handleDisconnect({
|
|
3995
|
+
code,
|
|
3996
|
+
reason: this.normalizeReason(reason),
|
|
3997
|
+
// ws does not provide wasClean on Node-style close callback
|
|
3998
|
+
wasClean: code === 1e3
|
|
3999
|
+
});
|
|
3988
4000
|
});
|
|
3989
4001
|
} else {
|
|
3990
4002
|
this.ws.onopen = () => {
|
|
4003
|
+
if (this.reconnectTimer) {
|
|
4004
|
+
clearTimeout(this.reconnectTimer);
|
|
4005
|
+
this.reconnectTimer = null;
|
|
4006
|
+
}
|
|
3991
4007
|
this.emit("open", {});
|
|
3992
4008
|
resolve();
|
|
3993
4009
|
};
|
|
@@ -4008,9 +4024,12 @@ var WSClient = class {
|
|
|
4008
4024
|
reject(error);
|
|
4009
4025
|
}
|
|
4010
4026
|
};
|
|
4011
|
-
this.ws.onclose = () => {
|
|
4012
|
-
this.
|
|
4013
|
-
|
|
4027
|
+
this.ws.onclose = (event) => {
|
|
4028
|
+
this.handleDisconnect({
|
|
4029
|
+
code: event.code,
|
|
4030
|
+
reason: event.reason,
|
|
4031
|
+
wasClean: event.wasClean
|
|
4032
|
+
});
|
|
4014
4033
|
};
|
|
4015
4034
|
}
|
|
4016
4035
|
} catch (error) {
|
|
@@ -4018,13 +4037,80 @@ var WSClient = class {
|
|
|
4018
4037
|
}
|
|
4019
4038
|
});
|
|
4020
4039
|
}
|
|
4021
|
-
|
|
4040
|
+
normalizeReason(reason) {
|
|
4041
|
+
if (reason === null || reason === void 0) return void 0;
|
|
4042
|
+
if (typeof reason === "string") return reason;
|
|
4043
|
+
if (typeof reason === "object" && reason && "toString" in reason) {
|
|
4044
|
+
try {
|
|
4045
|
+
const text = String(reason.toString());
|
|
4046
|
+
return text || void 0;
|
|
4047
|
+
} catch {
|
|
4048
|
+
return void 0;
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
return void 0;
|
|
4052
|
+
}
|
|
4053
|
+
rejectPending(error) {
|
|
4054
|
+
this.messageQueue.forEach((pending) => pending.reject(error));
|
|
4055
|
+
this.messageQueue = [];
|
|
4056
|
+
}
|
|
4057
|
+
buildDisconnectError(info) {
|
|
4058
|
+
const details = [
|
|
4059
|
+
info.code !== void 0 ? `code=${info.code}` : void 0,
|
|
4060
|
+
info.reason ? `reason=${info.reason}` : void 0
|
|
4061
|
+
].filter(Boolean).join(", ");
|
|
4062
|
+
const suffix = details ? ` (${details})` : "";
|
|
4063
|
+
return new Error(`WebSocket disconnected${suffix}`);
|
|
4064
|
+
}
|
|
4065
|
+
handleDisconnect(close = {}) {
|
|
4066
|
+
const reconnectDelayMs = 3e3;
|
|
4067
|
+
const unexpected = !this.isExplicitlyDisconnected;
|
|
4068
|
+
const info = {
|
|
4069
|
+
code: close.code,
|
|
4070
|
+
reason: close.reason,
|
|
4071
|
+
wasClean: close.wasClean,
|
|
4072
|
+
unexpected,
|
|
4073
|
+
timestamp: Date.now(),
|
|
4074
|
+
reconnectScheduled: false
|
|
4075
|
+
};
|
|
4022
4076
|
this.ws = null;
|
|
4023
|
-
|
|
4077
|
+
this.emit("close", info);
|
|
4078
|
+
if (this.reconnectTimer) {
|
|
4079
|
+
clearTimeout(this.reconnectTimer);
|
|
4080
|
+
this.reconnectTimer = null;
|
|
4081
|
+
}
|
|
4082
|
+
if (unexpected) {
|
|
4083
|
+
const disconnectError = this.buildDisconnectError(info);
|
|
4084
|
+
this.rejectPending(disconnectError);
|
|
4085
|
+
this.emit("disconnect", info);
|
|
4086
|
+
info.reconnectScheduled = true;
|
|
4087
|
+
info.reconnectDelayMs = reconnectDelayMs;
|
|
4088
|
+
if (this.options.onUnexpectedClose) {
|
|
4089
|
+
try {
|
|
4090
|
+
this.options.onUnexpectedClose(info);
|
|
4091
|
+
} catch (callbackError) {
|
|
4092
|
+
console.error("[Granular] onUnexpectedClose callback failed:", callbackError);
|
|
4093
|
+
}
|
|
4094
|
+
}
|
|
4024
4095
|
this.reconnectTimer = setTimeout(() => {
|
|
4025
4096
|
console.log("[Granular] Attempting reconnect...");
|
|
4026
|
-
this.connect().catch((
|
|
4027
|
-
|
|
4097
|
+
this.connect().catch((error) => {
|
|
4098
|
+
console.error("[Granular] Reconnect failed:", error);
|
|
4099
|
+
const reconnectInfo = {
|
|
4100
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4101
|
+
sessionId: this.sessionId,
|
|
4102
|
+
timestamp: Date.now()
|
|
4103
|
+
};
|
|
4104
|
+
this.emit("reconnect_error", reconnectInfo);
|
|
4105
|
+
if (this.options.onReconnectError) {
|
|
4106
|
+
try {
|
|
4107
|
+
this.options.onReconnectError(reconnectInfo);
|
|
4108
|
+
} catch (callbackError) {
|
|
4109
|
+
console.error("[Granular] onReconnectError callback failed:", callbackError);
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
});
|
|
4113
|
+
}, reconnectDelayMs);
|
|
4028
4114
|
}
|
|
4029
4115
|
}
|
|
4030
4116
|
handleMessage(message) {
|
|
@@ -4244,13 +4330,15 @@ var WSClient = class {
|
|
|
4244
4330
|
*/
|
|
4245
4331
|
disconnect() {
|
|
4246
4332
|
this.isExplicitlyDisconnected = true;
|
|
4247
|
-
if (this.reconnectTimer)
|
|
4333
|
+
if (this.reconnectTimer) {
|
|
4334
|
+
clearTimeout(this.reconnectTimer);
|
|
4335
|
+
this.reconnectTimer = null;
|
|
4336
|
+
}
|
|
4248
4337
|
if (this.ws) {
|
|
4249
|
-
this.ws.close();
|
|
4338
|
+
this.ws.close(1e3, "Client disconnect");
|
|
4250
4339
|
this.ws = null;
|
|
4251
4340
|
}
|
|
4252
|
-
this.
|
|
4253
|
-
this.messageQueue = [];
|
|
4341
|
+
this.rejectPending(new Error("Client explicitly disconnected"));
|
|
4254
4342
|
this.rpcHandlers.clear();
|
|
4255
4343
|
this.emit("disconnect", {});
|
|
4256
4344
|
}
|
|
@@ -4795,7 +4883,7 @@ import { ${allImports} } from "./sandbox-tools";
|
|
|
4795
4883
|
this.checkForToolChanges();
|
|
4796
4884
|
});
|
|
4797
4885
|
this.client.on("prompt", (prompt) => this.emit("prompt", prompt));
|
|
4798
|
-
this.client.on("disconnect", () => this.emit("disconnect", {}));
|
|
4886
|
+
this.client.on("disconnect", (payload) => this.emit("disconnect", payload || {}));
|
|
4799
4887
|
this.client.on("job.status", (data) => {
|
|
4800
4888
|
this.emit("job:status", data);
|
|
4801
4889
|
});
|
|
@@ -5722,6 +5810,8 @@ var Granular = class {
|
|
|
5722
5810
|
apiUrl;
|
|
5723
5811
|
httpUrl;
|
|
5724
5812
|
WebSocketCtor;
|
|
5813
|
+
onUnexpectedClose;
|
|
5814
|
+
onReconnectError;
|
|
5725
5815
|
/** Sandbox-level effect registry: sandboxId → (toolName → ToolWithHandler) */
|
|
5726
5816
|
sandboxEffects = /* @__PURE__ */ new Map();
|
|
5727
5817
|
/** Active environments tracker: sandboxId → Environment[] */
|
|
@@ -5738,6 +5828,8 @@ var Granular = class {
|
|
|
5738
5828
|
this.apiKey = auth;
|
|
5739
5829
|
this.apiUrl = options.apiUrl || "wss://api.granular.dev/v2/ws";
|
|
5740
5830
|
this.WebSocketCtor = options.WebSocketCtor;
|
|
5831
|
+
this.onUnexpectedClose = options.onUnexpectedClose;
|
|
5832
|
+
this.onReconnectError = options.onReconnectError;
|
|
5741
5833
|
this.httpUrl = this.apiUrl.replace("wss://", "https://").replace("/ws", "");
|
|
5742
5834
|
}
|
|
5743
5835
|
/**
|
|
@@ -5822,7 +5914,9 @@ var Granular = class {
|
|
|
5822
5914
|
url: this.apiUrl,
|
|
5823
5915
|
sessionId: envData.environmentId,
|
|
5824
5916
|
token: this.apiKey,
|
|
5825
|
-
WebSocketCtor: this.WebSocketCtor
|
|
5917
|
+
WebSocketCtor: this.WebSocketCtor,
|
|
5918
|
+
onUnexpectedClose: this.onUnexpectedClose,
|
|
5919
|
+
onReconnectError: this.onReconnectError
|
|
5826
5920
|
});
|
|
5827
5921
|
await client.connect();
|
|
5828
5922
|
const graphqlEndpoint = `${this.httpUrl}/orchestrator/graphql`;
|