@granular-software/sdk 0.3.3 → 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 +226 -12
- 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/cli/index.js
CHANGED
|
@@ -5741,23 +5741,203 @@ function normalizeAuthBaseUrl(input) {
|
|
|
5741
5741
|
}
|
|
5742
5742
|
return url;
|
|
5743
5743
|
}
|
|
5744
|
-
function
|
|
5744
|
+
function escapeHtml(input) {
|
|
5745
|
+
return input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5746
|
+
}
|
|
5747
|
+
function generateSquirclePath(size = 128, radius = 56, exp = 4, steps = 72) {
|
|
5748
|
+
const center = size / 2;
|
|
5749
|
+
const points = [];
|
|
5750
|
+
for (let i = 0; i <= steps; i++) {
|
|
5751
|
+
const angle = i / steps * 2 * Math.PI;
|
|
5752
|
+
const cosA = Math.cos(angle);
|
|
5753
|
+
const sinA = Math.sin(angle);
|
|
5754
|
+
const r = 1 / Math.pow(
|
|
5755
|
+
Math.pow(Math.abs(cosA), exp) + Math.pow(Math.abs(sinA), exp),
|
|
5756
|
+
1 / exp
|
|
5757
|
+
);
|
|
5758
|
+
const x = center + r * cosA * radius;
|
|
5759
|
+
const y = center + r * sinA * radius;
|
|
5760
|
+
points.push(`${x.toFixed(3)} ${y.toFixed(3)}`);
|
|
5761
|
+
}
|
|
5762
|
+
return `M ${points.join(" L ")} Z`;
|
|
5763
|
+
}
|
|
5764
|
+
function generateHilbertPath(order = 4, size = 128, radius = 51) {
|
|
5765
|
+
const n = 1 << order;
|
|
5766
|
+
const center = size / 2;
|
|
5767
|
+
const squircleExp = 4;
|
|
5768
|
+
const points = [];
|
|
5769
|
+
function squareToSquircle(u, v) {
|
|
5770
|
+
if (Math.abs(u) < 1e-3 && Math.abs(v) < 1e-3) return [u, v];
|
|
5771
|
+
const angle = Math.atan2(v, u);
|
|
5772
|
+
const cosA = Math.cos(angle);
|
|
5773
|
+
const sinA = Math.sin(angle);
|
|
5774
|
+
const squircleR = 1 / Math.pow(
|
|
5775
|
+
Math.pow(Math.abs(cosA), squircleExp) + Math.pow(Math.abs(sinA), squircleExp),
|
|
5776
|
+
1 / squircleExp
|
|
5777
|
+
);
|
|
5778
|
+
const squareDist = Math.max(Math.abs(u), Math.abs(v));
|
|
5779
|
+
const newDist = squareDist * squircleR;
|
|
5780
|
+
return [newDist * cosA, newDist * sinA];
|
|
5781
|
+
}
|
|
5782
|
+
function d2xy(d) {
|
|
5783
|
+
let x = 0;
|
|
5784
|
+
let y = 0;
|
|
5785
|
+
let t = d;
|
|
5786
|
+
for (let s = 1; s < n; s *= 2) {
|
|
5787
|
+
const rx = 1 & Math.floor(t) / 2;
|
|
5788
|
+
const ry = 1 & (Math.floor(t) ^ rx);
|
|
5789
|
+
if (ry === 0) {
|
|
5790
|
+
if (rx === 1) {
|
|
5791
|
+
x = s - 1 - x;
|
|
5792
|
+
y = s - 1 - y;
|
|
5793
|
+
}
|
|
5794
|
+
[x, y] = [y, x];
|
|
5795
|
+
}
|
|
5796
|
+
x += s * rx;
|
|
5797
|
+
y += s * ry;
|
|
5798
|
+
t = Math.floor(t) / 4;
|
|
5799
|
+
}
|
|
5800
|
+
return [x, y];
|
|
5801
|
+
}
|
|
5802
|
+
for (let i = 0; i < n * n; i++) {
|
|
5803
|
+
const [x, y] = d2xy(i);
|
|
5804
|
+
const normX = x / (n - 1) * 2 - 1;
|
|
5805
|
+
const normY = y / (n - 1) * 2 - 1;
|
|
5806
|
+
const [sqX, sqY] = squareToSquircle(normX, normY);
|
|
5807
|
+
points.push([center + sqX * radius, center + sqY * radius]);
|
|
5808
|
+
}
|
|
5809
|
+
return points.map(([x, y], i) => `${i === 0 ? "M" : "L"} ${x.toFixed(3)} ${y.toFixed(3)}`).join(" ");
|
|
5810
|
+
}
|
|
5811
|
+
function renderLogoSvg() {
|
|
5812
|
+
const squirclePath = generateSquirclePath();
|
|
5813
|
+
const hilbertPath = generateHilbertPath();
|
|
5814
|
+
return `<svg class="logo" viewBox="0 0 128 128" role="img" aria-label="Granular logo" xmlns="http://www.w3.org/2000/svg">
|
|
5815
|
+
<defs>
|
|
5816
|
+
<linearGradient id="granular-logo-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
5817
|
+
<stop offset="0%" stop-color="#ff8a54" />
|
|
5818
|
+
<stop offset="55%" stop-color="#ff6b35" />
|
|
5819
|
+
<stop offset="100%" stop-color="#e34b1f" />
|
|
5820
|
+
</linearGradient>
|
|
5821
|
+
<filter id="granular-logo-glow" x="-30%" y="-30%" width="160%" height="160%">
|
|
5822
|
+
<feGaussianBlur stdDeviation="1.6" result="blur" />
|
|
5823
|
+
<feMerge>
|
|
5824
|
+
<feMergeNode in="blur" />
|
|
5825
|
+
<feMergeNode in="SourceGraphic" />
|
|
5826
|
+
</feMerge>
|
|
5827
|
+
</filter>
|
|
5828
|
+
</defs>
|
|
5829
|
+
<path d="${squirclePath}" fill="rgba(255,255,255,0.05)" stroke="rgba(255,255,255,0.25)" stroke-width="1.2" />
|
|
5830
|
+
<path d="${hilbertPath}" fill="none" stroke="url(#granular-logo-gradient)" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round" filter="url(#granular-logo-glow)" />
|
|
5831
|
+
</svg>`;
|
|
5832
|
+
}
|
|
5833
|
+
function renderHtmlPage(title, message, variant) {
|
|
5834
|
+
const safeTitle = escapeHtml(title);
|
|
5835
|
+
const safeMessage = escapeHtml(message);
|
|
5836
|
+
const accent = variant === "success" ? "#ff6b35" : "#ff5c6b";
|
|
5837
|
+
const badge = variant === "success" ? "Success" : "Auth Error";
|
|
5745
5838
|
return `<!doctype html>
|
|
5746
5839
|
<html lang="en">
|
|
5747
5840
|
<head>
|
|
5748
5841
|
<meta charset="utf-8" />
|
|
5749
5842
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
5750
|
-
<title>${
|
|
5843
|
+
<title>${safeTitle}</title>
|
|
5751
5844
|
<style>
|
|
5752
|
-
:root { color-scheme:
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5845
|
+
:root { color-scheme: dark; }
|
|
5846
|
+
* { box-sizing: border-box; }
|
|
5847
|
+
body {
|
|
5848
|
+
margin: 0;
|
|
5849
|
+
min-height: 100vh;
|
|
5850
|
+
display: grid;
|
|
5851
|
+
place-items: center;
|
|
5852
|
+
padding: 28px;
|
|
5853
|
+
font-family: "Avenir Next", "SF Pro Display", "Segoe UI", sans-serif;
|
|
5854
|
+
color: #f8f8f8;
|
|
5855
|
+
background:
|
|
5856
|
+
radial-gradient(1000px 480px at 12% -10%, rgba(255, 110, 58, 0.26), transparent 60%),
|
|
5857
|
+
radial-gradient(760px 480px at 100% 120%, rgba(99, 102, 241, 0.22), transparent 65%),
|
|
5858
|
+
linear-gradient(140deg, #0f0f12 0%, #131319 45%, #11131a 100%);
|
|
5859
|
+
}
|
|
5860
|
+
.panel {
|
|
5861
|
+
width: min(520px, 100%);
|
|
5862
|
+
border-radius: 24px;
|
|
5863
|
+
border: 1px solid rgba(255, 255, 255, 0.16);
|
|
5864
|
+
background: linear-gradient(160deg, rgba(255,255,255,0.08), rgba(255,255,255,0.03));
|
|
5865
|
+
backdrop-filter: blur(8px);
|
|
5866
|
+
box-shadow:
|
|
5867
|
+
0 18px 60px rgba(0, 0, 0, 0.45),
|
|
5868
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.18);
|
|
5869
|
+
padding: 26px 26px 24px;
|
|
5870
|
+
}
|
|
5871
|
+
.head {
|
|
5872
|
+
display: flex;
|
|
5873
|
+
align-items: center;
|
|
5874
|
+
gap: 14px;
|
|
5875
|
+
margin-bottom: 14px;
|
|
5876
|
+
}
|
|
5877
|
+
.logo-wrap {
|
|
5878
|
+
width: 62px;
|
|
5879
|
+
height: 62px;
|
|
5880
|
+
border-radius: 18px;
|
|
5881
|
+
border: 1px solid rgba(255,255,255,0.2);
|
|
5882
|
+
background: radial-gradient(circle at 25% 20%, rgba(255,255,255,0.17), rgba(255,255,255,0.02));
|
|
5883
|
+
display: grid;
|
|
5884
|
+
place-items: center;
|
|
5885
|
+
flex: 0 0 auto;
|
|
5886
|
+
}
|
|
5887
|
+
.logo {
|
|
5888
|
+
width: 46px;
|
|
5889
|
+
height: 46px;
|
|
5890
|
+
display: block;
|
|
5891
|
+
}
|
|
5892
|
+
.badge {
|
|
5893
|
+
display: inline-flex;
|
|
5894
|
+
align-items: center;
|
|
5895
|
+
border-radius: 999px;
|
|
5896
|
+
padding: 5px 11px;
|
|
5897
|
+
border: 1px solid ${accent}66;
|
|
5898
|
+
background: ${accent}26;
|
|
5899
|
+
color: ${accent};
|
|
5900
|
+
font-size: 12px;
|
|
5901
|
+
font-weight: 600;
|
|
5902
|
+
letter-spacing: 0.04em;
|
|
5903
|
+
text-transform: uppercase;
|
|
5904
|
+
margin-bottom: 8px;
|
|
5905
|
+
}
|
|
5906
|
+
h1 {
|
|
5907
|
+
margin: 0;
|
|
5908
|
+
font-size: 25px;
|
|
5909
|
+
line-height: 1.2;
|
|
5910
|
+
letter-spacing: 0.01em;
|
|
5911
|
+
color: #ffffff;
|
|
5912
|
+
}
|
|
5913
|
+
p {
|
|
5914
|
+
margin: 0;
|
|
5915
|
+
color: #d7d8df;
|
|
5916
|
+
font-size: 15px;
|
|
5917
|
+
line-height: 1.6;
|
|
5918
|
+
}
|
|
5919
|
+
.footer {
|
|
5920
|
+
margin-top: 18px;
|
|
5921
|
+
padding-top: 14px;
|
|
5922
|
+
border-top: 1px solid rgba(255, 255, 255, 0.11);
|
|
5923
|
+
font-size: 12px;
|
|
5924
|
+
color: #9ea3b2;
|
|
5925
|
+
letter-spacing: 0.02em;
|
|
5926
|
+
}
|
|
5756
5927
|
</style>
|
|
5757
5928
|
</head>
|
|
5758
5929
|
<body>
|
|
5759
|
-
<
|
|
5760
|
-
|
|
5930
|
+
<main class="panel">
|
|
5931
|
+
<div class="head">
|
|
5932
|
+
<div class="logo-wrap">${renderLogoSvg()}</div>
|
|
5933
|
+
<div>
|
|
5934
|
+
<div class="badge">${badge}</div>
|
|
5935
|
+
<h1>${safeTitle}</h1>
|
|
5936
|
+
</div>
|
|
5937
|
+
</div>
|
|
5938
|
+
<p>${safeMessage}</p>
|
|
5939
|
+
<div class="footer">Granular CLI authentication flow completed.</div>
|
|
5940
|
+
</main>
|
|
5761
5941
|
</body>
|
|
5762
5942
|
</html>`;
|
|
5763
5943
|
}
|
|
@@ -5841,7 +6021,7 @@ async function loginWithBrowser(options) {
|
|
|
5841
6021
|
if (!returnedState || returnedState !== state) {
|
|
5842
6022
|
res.statusCode = 400;
|
|
5843
6023
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
5844
|
-
res.end(renderHtmlPage("Granular login failed", "State mismatch. Please close this window and retry."));
|
|
6024
|
+
res.end(renderHtmlPage("Granular login failed", "State mismatch. Please close this window and retry.", "error"));
|
|
5845
6025
|
if (!settled) {
|
|
5846
6026
|
settled = true;
|
|
5847
6027
|
clearTimeout(timeout);
|
|
@@ -5852,7 +6032,13 @@ async function loginWithBrowser(options) {
|
|
|
5852
6032
|
if (error2) {
|
|
5853
6033
|
res.statusCode = 400;
|
|
5854
6034
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
5855
|
-
res.end(
|
|
6035
|
+
res.end(
|
|
6036
|
+
renderHtmlPage(
|
|
6037
|
+
"Granular login failed",
|
|
6038
|
+
`${describeAuthError(error2)} You can close this window and return to the CLI.`,
|
|
6039
|
+
"error"
|
|
6040
|
+
)
|
|
6041
|
+
);
|
|
5856
6042
|
if (!settled) {
|
|
5857
6043
|
settled = true;
|
|
5858
6044
|
clearTimeout(timeout);
|
|
@@ -5863,7 +6049,7 @@ async function loginWithBrowser(options) {
|
|
|
5863
6049
|
if (!apiKey2) {
|
|
5864
6050
|
res.statusCode = 400;
|
|
5865
6051
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
5866
|
-
res.end(renderHtmlPage("Granular login failed", "No API key was returned. Please retry."));
|
|
6052
|
+
res.end(renderHtmlPage("Granular login failed", "No API key was returned. Please retry.", "error"));
|
|
5867
6053
|
if (!settled) {
|
|
5868
6054
|
settled = true;
|
|
5869
6055
|
clearTimeout(timeout);
|
|
@@ -5873,7 +6059,7 @@ async function loginWithBrowser(options) {
|
|
|
5873
6059
|
}
|
|
5874
6060
|
res.statusCode = 200;
|
|
5875
6061
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
5876
|
-
res.end(renderHtmlPage("Granular login complete", "Authentication succeeded. You can close this window."));
|
|
6062
|
+
res.end(renderHtmlPage("Granular login complete", "Authentication succeeded. You can close this window.", "success"));
|
|
5877
6063
|
if (!settled) {
|
|
5878
6064
|
settled = true;
|
|
5879
6065
|
clearTimeout(timeout);
|
|
@@ -7475,6 +7661,18 @@ function prompt(question, defaultValue) {
|
|
|
7475
7661
|
});
|
|
7476
7662
|
});
|
|
7477
7663
|
}
|
|
7664
|
+
function waitForEnter(message) {
|
|
7665
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
7666
|
+
return Promise.resolve();
|
|
7667
|
+
}
|
|
7668
|
+
const rl = readline__namespace.createInterface({ input: process.stdin, output: process.stdout });
|
|
7669
|
+
return new Promise((resolve) => {
|
|
7670
|
+
rl.question(` ${message}`, () => {
|
|
7671
|
+
rl.close();
|
|
7672
|
+
resolve();
|
|
7673
|
+
});
|
|
7674
|
+
});
|
|
7675
|
+
}
|
|
7478
7676
|
function confirm(question, defaultYes = true) {
|
|
7479
7677
|
const hint2 = defaultYes ? "Y/n" : "y/N";
|
|
7480
7678
|
return prompt(`${question} [${hint2}]`).then((answer) => {
|
|
@@ -7495,6 +7693,8 @@ async function initCommand(projectName, options) {
|
|
|
7495
7693
|
let apiKey = loadApiKey();
|
|
7496
7694
|
if (!apiKey) {
|
|
7497
7695
|
info("No API key found. Starting browser login...");
|
|
7696
|
+
await waitForEnter("Press Enter to open the login page in your browser...");
|
|
7697
|
+
console.log();
|
|
7498
7698
|
const waiting = spinner("Opening browser and waiting for authentication...");
|
|
7499
7699
|
try {
|
|
7500
7700
|
apiKey = await loginWithBrowser({
|
|
@@ -7615,6 +7815,18 @@ function promptSecret(question) {
|
|
|
7615
7815
|
});
|
|
7616
7816
|
});
|
|
7617
7817
|
}
|
|
7818
|
+
function waitForEnter2(message) {
|
|
7819
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
7820
|
+
return Promise.resolve();
|
|
7821
|
+
}
|
|
7822
|
+
const rl = readline__namespace.createInterface({ input: process.stdin, output: process.stdout });
|
|
7823
|
+
return new Promise((resolve) => {
|
|
7824
|
+
rl.question(` ${message}`, () => {
|
|
7825
|
+
rl.close();
|
|
7826
|
+
resolve();
|
|
7827
|
+
});
|
|
7828
|
+
});
|
|
7829
|
+
}
|
|
7618
7830
|
function maskApiKey(apiKey) {
|
|
7619
7831
|
if (apiKey.length < 10) return `${apiKey}...`;
|
|
7620
7832
|
return `${apiKey.substring(0, 10)}...`;
|
|
@@ -7636,6 +7848,8 @@ async function loginCommand(options = {}) {
|
|
|
7636
7848
|
apiKey = await promptSecret("Enter your API key");
|
|
7637
7849
|
} else {
|
|
7638
7850
|
const timeoutMs = options.timeout && options.timeout > 0 ? options.timeout * 1e3 : void 0;
|
|
7851
|
+
await waitForEnter2("Press Enter to open the login page in your browser...");
|
|
7852
|
+
console.log();
|
|
7639
7853
|
const waiting = spinner("Opening browser and waiting for authentication...");
|
|
7640
7854
|
try {
|
|
7641
7855
|
apiKey = await loginWithBrowser({
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Automerge from '@automerge/automerge';
|
|
2
|
-
import { W as WSClientOptions, T as ToolWithHandler, P as PublishToolsResult, J as Job, a as ToolHandler, I as InstanceToolHandler, b as ToolInfo, c as ToolsChangedEvent, D as DomainState, G as GranularOptions, R as RecordUserOptions, U as User, C as ConnectOptions, E as EnvironmentData, d as GraphQLResult, e as DefineRelationshipOptions, f as RelationshipInfo, M as ModelRef, g as ManifestContent, h as RecordObjectOptions, i as RecordObjectResult, S as SandboxListResponse, j as Sandbox, k as CreateSandboxData, l as DeleteResponse, m as PermissionProfile, n as CreatePermissionProfileData, o as CreateEnvironmentData, p as Subject, A as AssignmentListResponse } from './types-
|
|
3
|
-
export {
|
|
2
|
+
import { W as WSClientOptions, T as ToolWithHandler, P as PublishToolsResult, J as Job, a as ToolHandler, I as InstanceToolHandler, b as ToolInfo, c as ToolsChangedEvent, D as DomainState, G as GranularOptions, R as RecordUserOptions, U as User, C as ConnectOptions, E as EnvironmentData, d as GraphQLResult, e as DefineRelationshipOptions, f as RelationshipInfo, M as ModelRef, g as ManifestContent, h as RecordObjectOptions, i as RecordObjectResult, S as SandboxListResponse, j as Sandbox, k as CreateSandboxData, l as DeleteResponse, m as PermissionProfile, n as CreatePermissionProfileData, o as CreateEnvironmentData, p as Subject, A as AssignmentListResponse } from './types-D5B8WlF4.mjs';
|
|
3
|
+
export { a4 as APIError, u as Assignment, z as Build, F as BuildListResponse, B as BuildPolicy, y as BuildStatus, v as EnvironmentListResponse, r as GranularAuth, H as JobStatus, K as JobSubmitResult, w as Manifest, a2 as ManifestImport, x as ManifestListResponse, a1 as ManifestOperation, $ as ManifestPropertySpec, a0 as ManifestRelationshipDef, a3 as ManifestVolume, t as PermissionProfileListResponse, s as PermissionRules, L as Prompt, Q as RPCRequest, Y as RPCRequestFromServer, V as RPCResponse, X as SyncMessage, Z as ToolInvokeParams, _ as ToolResultParams, q as ToolSchema, N as WSDisconnectInfo, O as WSReconnectErrorInfo } from './types-D5B8WlF4.mjs';
|
|
4
4
|
import { Doc } from '@automerge/automerge/slim';
|
|
5
5
|
|
|
6
6
|
declare class WSClient {
|
|
@@ -24,6 +24,9 @@ declare class WSClient {
|
|
|
24
24
|
* @returns {Promise<void>} Resolves when connection is open
|
|
25
25
|
*/
|
|
26
26
|
connect(): Promise<void>;
|
|
27
|
+
private normalizeReason;
|
|
28
|
+
private rejectPending;
|
|
29
|
+
private buildDisconnectError;
|
|
27
30
|
private handleDisconnect;
|
|
28
31
|
private handleMessage;
|
|
29
32
|
/**
|
|
@@ -638,6 +641,8 @@ declare class Granular {
|
|
|
638
641
|
private apiUrl;
|
|
639
642
|
private httpUrl;
|
|
640
643
|
private WebSocketCtor?;
|
|
644
|
+
private onUnexpectedClose?;
|
|
645
|
+
private onReconnectError?;
|
|
641
646
|
/** Sandbox-level effect registry: sandboxId → (toolName → ToolWithHandler) */
|
|
642
647
|
private sandboxEffects;
|
|
643
648
|
/** Active environments tracker: sandboxId → Environment[] */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Automerge from '@automerge/automerge';
|
|
2
|
-
import { W as WSClientOptions, T as ToolWithHandler, P as PublishToolsResult, J as Job, a as ToolHandler, I as InstanceToolHandler, b as ToolInfo, c as ToolsChangedEvent, D as DomainState, G as GranularOptions, R as RecordUserOptions, U as User, C as ConnectOptions, E as EnvironmentData, d as GraphQLResult, e as DefineRelationshipOptions, f as RelationshipInfo, M as ModelRef, g as ManifestContent, h as RecordObjectOptions, i as RecordObjectResult, S as SandboxListResponse, j as Sandbox, k as CreateSandboxData, l as DeleteResponse, m as PermissionProfile, n as CreatePermissionProfileData, o as CreateEnvironmentData, p as Subject, A as AssignmentListResponse } from './types-
|
|
3
|
-
export {
|
|
2
|
+
import { W as WSClientOptions, T as ToolWithHandler, P as PublishToolsResult, J as Job, a as ToolHandler, I as InstanceToolHandler, b as ToolInfo, c as ToolsChangedEvent, D as DomainState, G as GranularOptions, R as RecordUserOptions, U as User, C as ConnectOptions, E as EnvironmentData, d as GraphQLResult, e as DefineRelationshipOptions, f as RelationshipInfo, M as ModelRef, g as ManifestContent, h as RecordObjectOptions, i as RecordObjectResult, S as SandboxListResponse, j as Sandbox, k as CreateSandboxData, l as DeleteResponse, m as PermissionProfile, n as CreatePermissionProfileData, o as CreateEnvironmentData, p as Subject, A as AssignmentListResponse } from './types-D5B8WlF4.js';
|
|
3
|
+
export { a4 as APIError, u as Assignment, z as Build, F as BuildListResponse, B as BuildPolicy, y as BuildStatus, v as EnvironmentListResponse, r as GranularAuth, H as JobStatus, K as JobSubmitResult, w as Manifest, a2 as ManifestImport, x as ManifestListResponse, a1 as ManifestOperation, $ as ManifestPropertySpec, a0 as ManifestRelationshipDef, a3 as ManifestVolume, t as PermissionProfileListResponse, s as PermissionRules, L as Prompt, Q as RPCRequest, Y as RPCRequestFromServer, V as RPCResponse, X as SyncMessage, Z as ToolInvokeParams, _ as ToolResultParams, q as ToolSchema, N as WSDisconnectInfo, O as WSReconnectErrorInfo } from './types-D5B8WlF4.js';
|
|
4
4
|
import { Doc } from '@automerge/automerge/slim';
|
|
5
5
|
|
|
6
6
|
declare class WSClient {
|
|
@@ -24,6 +24,9 @@ declare class WSClient {
|
|
|
24
24
|
* @returns {Promise<void>} Resolves when connection is open
|
|
25
25
|
*/
|
|
26
26
|
connect(): Promise<void>;
|
|
27
|
+
private normalizeReason;
|
|
28
|
+
private rejectPending;
|
|
29
|
+
private buildDisconnectError;
|
|
27
30
|
private handleDisconnect;
|
|
28
31
|
private handleMessage;
|
|
29
32
|
/**
|
|
@@ -638,6 +641,8 @@ declare class Granular {
|
|
|
638
641
|
private apiUrl;
|
|
639
642
|
private httpUrl;
|
|
640
643
|
private WebSocketCtor?;
|
|
644
|
+
private onUnexpectedClose?;
|
|
645
|
+
private onReconnectError?;
|
|
641
646
|
/** Sandbox-level effect registry: sandboxId → (toolName → ToolWithHandler) */
|
|
642
647
|
private sandboxEffects;
|
|
643
648
|
/** Active environments tracker: sandboxId → Environment[] */
|
package/dist/index.js
CHANGED
|
@@ -3966,6 +3966,10 @@ var WSClient = class {
|
|
|
3966
3966
|
*/
|
|
3967
3967
|
async connect() {
|
|
3968
3968
|
this.isExplicitlyDisconnected = false;
|
|
3969
|
+
if (this.reconnectTimer) {
|
|
3970
|
+
clearTimeout(this.reconnectTimer);
|
|
3971
|
+
this.reconnectTimer = null;
|
|
3972
|
+
}
|
|
3969
3973
|
let WebSocketClass = this.options.WebSocketCtor || GlobalWebSocket;
|
|
3970
3974
|
if (!WebSocketClass) {
|
|
3971
3975
|
try {
|
|
@@ -3987,6 +3991,10 @@ var WSClient = class {
|
|
|
3987
3991
|
const socket = this.ws;
|
|
3988
3992
|
if (typeof socket.on === "function") {
|
|
3989
3993
|
socket.on("open", () => {
|
|
3994
|
+
if (this.reconnectTimer) {
|
|
3995
|
+
clearTimeout(this.reconnectTimer);
|
|
3996
|
+
this.reconnectTimer = null;
|
|
3997
|
+
}
|
|
3990
3998
|
this.emit("open", {});
|
|
3991
3999
|
resolve();
|
|
3992
4000
|
});
|
|
@@ -4004,12 +4012,20 @@ var WSClient = class {
|
|
|
4004
4012
|
reject(error);
|
|
4005
4013
|
}
|
|
4006
4014
|
});
|
|
4007
|
-
socket.on("close", () => {
|
|
4008
|
-
this.
|
|
4009
|
-
|
|
4015
|
+
socket.on("close", (code, reason) => {
|
|
4016
|
+
this.handleDisconnect({
|
|
4017
|
+
code,
|
|
4018
|
+
reason: this.normalizeReason(reason),
|
|
4019
|
+
// ws does not provide wasClean on Node-style close callback
|
|
4020
|
+
wasClean: code === 1e3
|
|
4021
|
+
});
|
|
4010
4022
|
});
|
|
4011
4023
|
} else {
|
|
4012
4024
|
this.ws.onopen = () => {
|
|
4025
|
+
if (this.reconnectTimer) {
|
|
4026
|
+
clearTimeout(this.reconnectTimer);
|
|
4027
|
+
this.reconnectTimer = null;
|
|
4028
|
+
}
|
|
4013
4029
|
this.emit("open", {});
|
|
4014
4030
|
resolve();
|
|
4015
4031
|
};
|
|
@@ -4030,9 +4046,12 @@ var WSClient = class {
|
|
|
4030
4046
|
reject(error);
|
|
4031
4047
|
}
|
|
4032
4048
|
};
|
|
4033
|
-
this.ws.onclose = () => {
|
|
4034
|
-
this.
|
|
4035
|
-
|
|
4049
|
+
this.ws.onclose = (event) => {
|
|
4050
|
+
this.handleDisconnect({
|
|
4051
|
+
code: event.code,
|
|
4052
|
+
reason: event.reason,
|
|
4053
|
+
wasClean: event.wasClean
|
|
4054
|
+
});
|
|
4036
4055
|
};
|
|
4037
4056
|
}
|
|
4038
4057
|
} catch (error) {
|
|
@@ -4040,13 +4059,80 @@ var WSClient = class {
|
|
|
4040
4059
|
}
|
|
4041
4060
|
});
|
|
4042
4061
|
}
|
|
4043
|
-
|
|
4062
|
+
normalizeReason(reason) {
|
|
4063
|
+
if (reason === null || reason === void 0) return void 0;
|
|
4064
|
+
if (typeof reason === "string") return reason;
|
|
4065
|
+
if (typeof reason === "object" && reason && "toString" in reason) {
|
|
4066
|
+
try {
|
|
4067
|
+
const text = String(reason.toString());
|
|
4068
|
+
return text || void 0;
|
|
4069
|
+
} catch {
|
|
4070
|
+
return void 0;
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
return void 0;
|
|
4074
|
+
}
|
|
4075
|
+
rejectPending(error) {
|
|
4076
|
+
this.messageQueue.forEach((pending) => pending.reject(error));
|
|
4077
|
+
this.messageQueue = [];
|
|
4078
|
+
}
|
|
4079
|
+
buildDisconnectError(info) {
|
|
4080
|
+
const details = [
|
|
4081
|
+
info.code !== void 0 ? `code=${info.code}` : void 0,
|
|
4082
|
+
info.reason ? `reason=${info.reason}` : void 0
|
|
4083
|
+
].filter(Boolean).join(", ");
|
|
4084
|
+
const suffix = details ? ` (${details})` : "";
|
|
4085
|
+
return new Error(`WebSocket disconnected${suffix}`);
|
|
4086
|
+
}
|
|
4087
|
+
handleDisconnect(close = {}) {
|
|
4088
|
+
const reconnectDelayMs = 3e3;
|
|
4089
|
+
const unexpected = !this.isExplicitlyDisconnected;
|
|
4090
|
+
const info = {
|
|
4091
|
+
code: close.code,
|
|
4092
|
+
reason: close.reason,
|
|
4093
|
+
wasClean: close.wasClean,
|
|
4094
|
+
unexpected,
|
|
4095
|
+
timestamp: Date.now(),
|
|
4096
|
+
reconnectScheduled: false
|
|
4097
|
+
};
|
|
4044
4098
|
this.ws = null;
|
|
4045
|
-
|
|
4099
|
+
this.emit("close", info);
|
|
4100
|
+
if (this.reconnectTimer) {
|
|
4101
|
+
clearTimeout(this.reconnectTimer);
|
|
4102
|
+
this.reconnectTimer = null;
|
|
4103
|
+
}
|
|
4104
|
+
if (unexpected) {
|
|
4105
|
+
const disconnectError = this.buildDisconnectError(info);
|
|
4106
|
+
this.rejectPending(disconnectError);
|
|
4107
|
+
this.emit("disconnect", info);
|
|
4108
|
+
info.reconnectScheduled = true;
|
|
4109
|
+
info.reconnectDelayMs = reconnectDelayMs;
|
|
4110
|
+
if (this.options.onUnexpectedClose) {
|
|
4111
|
+
try {
|
|
4112
|
+
this.options.onUnexpectedClose(info);
|
|
4113
|
+
} catch (callbackError) {
|
|
4114
|
+
console.error("[Granular] onUnexpectedClose callback failed:", callbackError);
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4046
4117
|
this.reconnectTimer = setTimeout(() => {
|
|
4047
4118
|
console.log("[Granular] Attempting reconnect...");
|
|
4048
|
-
this.connect().catch((
|
|
4049
|
-
|
|
4119
|
+
this.connect().catch((error) => {
|
|
4120
|
+
console.error("[Granular] Reconnect failed:", error);
|
|
4121
|
+
const reconnectInfo = {
|
|
4122
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4123
|
+
sessionId: this.sessionId,
|
|
4124
|
+
timestamp: Date.now()
|
|
4125
|
+
};
|
|
4126
|
+
this.emit("reconnect_error", reconnectInfo);
|
|
4127
|
+
if (this.options.onReconnectError) {
|
|
4128
|
+
try {
|
|
4129
|
+
this.options.onReconnectError(reconnectInfo);
|
|
4130
|
+
} catch (callbackError) {
|
|
4131
|
+
console.error("[Granular] onReconnectError callback failed:", callbackError);
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
});
|
|
4135
|
+
}, reconnectDelayMs);
|
|
4050
4136
|
}
|
|
4051
4137
|
}
|
|
4052
4138
|
handleMessage(message) {
|
|
@@ -4266,13 +4352,15 @@ var WSClient = class {
|
|
|
4266
4352
|
*/
|
|
4267
4353
|
disconnect() {
|
|
4268
4354
|
this.isExplicitlyDisconnected = true;
|
|
4269
|
-
if (this.reconnectTimer)
|
|
4355
|
+
if (this.reconnectTimer) {
|
|
4356
|
+
clearTimeout(this.reconnectTimer);
|
|
4357
|
+
this.reconnectTimer = null;
|
|
4358
|
+
}
|
|
4270
4359
|
if (this.ws) {
|
|
4271
|
-
this.ws.close();
|
|
4360
|
+
this.ws.close(1e3, "Client disconnect");
|
|
4272
4361
|
this.ws = null;
|
|
4273
4362
|
}
|
|
4274
|
-
this.
|
|
4275
|
-
this.messageQueue = [];
|
|
4363
|
+
this.rejectPending(new Error("Client explicitly disconnected"));
|
|
4276
4364
|
this.rpcHandlers.clear();
|
|
4277
4365
|
this.emit("disconnect", {});
|
|
4278
4366
|
}
|
|
@@ -4817,7 +4905,7 @@ import { ${allImports} } from "./sandbox-tools";
|
|
|
4817
4905
|
this.checkForToolChanges();
|
|
4818
4906
|
});
|
|
4819
4907
|
this.client.on("prompt", (prompt) => this.emit("prompt", prompt));
|
|
4820
|
-
this.client.on("disconnect", () => this.emit("disconnect", {}));
|
|
4908
|
+
this.client.on("disconnect", (payload) => this.emit("disconnect", payload || {}));
|
|
4821
4909
|
this.client.on("job.status", (data) => {
|
|
4822
4910
|
this.emit("job:status", data);
|
|
4823
4911
|
});
|
|
@@ -5744,6 +5832,8 @@ var Granular = class {
|
|
|
5744
5832
|
apiUrl;
|
|
5745
5833
|
httpUrl;
|
|
5746
5834
|
WebSocketCtor;
|
|
5835
|
+
onUnexpectedClose;
|
|
5836
|
+
onReconnectError;
|
|
5747
5837
|
/** Sandbox-level effect registry: sandboxId → (toolName → ToolWithHandler) */
|
|
5748
5838
|
sandboxEffects = /* @__PURE__ */ new Map();
|
|
5749
5839
|
/** Active environments tracker: sandboxId → Environment[] */
|
|
@@ -5760,6 +5850,8 @@ var Granular = class {
|
|
|
5760
5850
|
this.apiKey = auth;
|
|
5761
5851
|
this.apiUrl = options.apiUrl || "wss://api.granular.dev/v2/ws";
|
|
5762
5852
|
this.WebSocketCtor = options.WebSocketCtor;
|
|
5853
|
+
this.onUnexpectedClose = options.onUnexpectedClose;
|
|
5854
|
+
this.onReconnectError = options.onReconnectError;
|
|
5763
5855
|
this.httpUrl = this.apiUrl.replace("wss://", "https://").replace("/ws", "");
|
|
5764
5856
|
}
|
|
5765
5857
|
/**
|
|
@@ -5844,7 +5936,9 @@ var Granular = class {
|
|
|
5844
5936
|
url: this.apiUrl,
|
|
5845
5937
|
sessionId: envData.environmentId,
|
|
5846
5938
|
token: this.apiKey,
|
|
5847
|
-
WebSocketCtor: this.WebSocketCtor
|
|
5939
|
+
WebSocketCtor: this.WebSocketCtor,
|
|
5940
|
+
onUnexpectedClose: this.onUnexpectedClose,
|
|
5941
|
+
onReconnectError: this.onReconnectError
|
|
5848
5942
|
});
|
|
5849
5943
|
await client.connect();
|
|
5850
5944
|
const graphqlEndpoint = `${this.httpUrl}/orchestrator/graphql`;
|