@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/cli/index.js
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
var fs = require('fs');
|
|
5
5
|
var path = require('path');
|
|
6
6
|
var readline = require('readline');
|
|
7
|
+
var http = require('http');
|
|
8
|
+
var child_process = require('child_process');
|
|
9
|
+
var crypto = require('crypto');
|
|
7
10
|
var process6 = require('process');
|
|
8
11
|
var os = require('os');
|
|
9
12
|
var tty = require('tty');
|
|
10
|
-
var child_process = require('child_process');
|
|
11
13
|
|
|
12
14
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
15
|
|
|
@@ -32,6 +34,7 @@ function _interopNamespace(e) {
|
|
|
32
34
|
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
33
35
|
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
34
36
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
37
|
+
var http__namespace = /*#__PURE__*/_interopNamespace(http);
|
|
35
38
|
var process6__default = /*#__PURE__*/_interopDefault(process6);
|
|
36
39
|
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
37
40
|
var tty__default = /*#__PURE__*/_interopDefault(tty);
|
|
@@ -5455,6 +5458,12 @@ function loadApiUrl() {
|
|
|
5455
5458
|
if (process.env.GRANULAR_API_URL) return process.env.GRANULAR_API_URL;
|
|
5456
5459
|
return "https://cf-api-gateway.arthur6084.workers.dev/granular";
|
|
5457
5460
|
}
|
|
5461
|
+
function loadAuthUrl() {
|
|
5462
|
+
if (process.env.GRANULAR_AUTH_URL) {
|
|
5463
|
+
return process.env.GRANULAR_AUTH_URL;
|
|
5464
|
+
}
|
|
5465
|
+
return "https://app.granular.software";
|
|
5466
|
+
}
|
|
5458
5467
|
function saveApiKey(apiKey) {
|
|
5459
5468
|
const envLocalPath = getEnvLocalPath();
|
|
5460
5469
|
let content = "";
|
|
@@ -5722,6 +5731,347 @@ var ApiClient = class {
|
|
|
5722
5731
|
});
|
|
5723
5732
|
}
|
|
5724
5733
|
};
|
|
5734
|
+
var CALLBACK_HOST = "127.0.0.1";
|
|
5735
|
+
var CALLBACK_PATH = "/callback";
|
|
5736
|
+
var DEFAULT_TIMEOUT_MS = 3 * 60 * 1e3;
|
|
5737
|
+
function normalizeAuthBaseUrl(input) {
|
|
5738
|
+
const url = new URL(input);
|
|
5739
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
5740
|
+
throw new Error("Auth URL must start with http:// or https://");
|
|
5741
|
+
}
|
|
5742
|
+
return url;
|
|
5743
|
+
}
|
|
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";
|
|
5838
|
+
return `<!doctype html>
|
|
5839
|
+
<html lang="en">
|
|
5840
|
+
<head>
|
|
5841
|
+
<meta charset="utf-8" />
|
|
5842
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
5843
|
+
<title>${safeTitle}</title>
|
|
5844
|
+
<style>
|
|
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
|
+
}
|
|
5927
|
+
</style>
|
|
5928
|
+
</head>
|
|
5929
|
+
<body>
|
|
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>
|
|
5941
|
+
</body>
|
|
5942
|
+
</html>`;
|
|
5943
|
+
}
|
|
5944
|
+
function describeAuthError(code) {
|
|
5945
|
+
switch (code) {
|
|
5946
|
+
case "unauthorized":
|
|
5947
|
+
return "You must sign in before authorizing the CLI.";
|
|
5948
|
+
case "no_organization":
|
|
5949
|
+
return "Your account is not associated with an organization.";
|
|
5950
|
+
case "key_creation_failed":
|
|
5951
|
+
return "Failed to create an API key for CLI login.";
|
|
5952
|
+
case "missing_key_value":
|
|
5953
|
+
return "The auth server did not return an API key value.";
|
|
5954
|
+
case "auth_failed":
|
|
5955
|
+
return "Authentication failed on the server.";
|
|
5956
|
+
default:
|
|
5957
|
+
return code;
|
|
5958
|
+
}
|
|
5959
|
+
}
|
|
5960
|
+
function openExternalUrl(url) {
|
|
5961
|
+
const platform = process.platform;
|
|
5962
|
+
return new Promise((resolve) => {
|
|
5963
|
+
let child;
|
|
5964
|
+
if (platform === "darwin") {
|
|
5965
|
+
child = child_process.spawn("open", [url], { stdio: "ignore" });
|
|
5966
|
+
} else if (platform === "win32") {
|
|
5967
|
+
child = child_process.spawn("cmd", ["/c", "start", "", url], {
|
|
5968
|
+
stdio: "ignore",
|
|
5969
|
+
windowsHide: true
|
|
5970
|
+
});
|
|
5971
|
+
} else {
|
|
5972
|
+
child = child_process.spawn("xdg-open", [url], { stdio: "ignore" });
|
|
5973
|
+
}
|
|
5974
|
+
child.once("error", () => resolve(false));
|
|
5975
|
+
child.once("spawn", () => resolve(true));
|
|
5976
|
+
});
|
|
5977
|
+
}
|
|
5978
|
+
function closeServer(server) {
|
|
5979
|
+
return new Promise((resolve) => {
|
|
5980
|
+
server.close(() => resolve());
|
|
5981
|
+
});
|
|
5982
|
+
}
|
|
5983
|
+
async function loginWithBrowser(options) {
|
|
5984
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
5985
|
+
const authBaseUrl = normalizeAuthBaseUrl(options.authBaseUrl);
|
|
5986
|
+
const state = crypto.randomBytes(24).toString("hex");
|
|
5987
|
+
const server = http__namespace.createServer();
|
|
5988
|
+
await new Promise((resolve, reject) => {
|
|
5989
|
+
server.once("error", reject);
|
|
5990
|
+
server.listen(0, CALLBACK_HOST, () => resolve());
|
|
5991
|
+
});
|
|
5992
|
+
const address = server.address();
|
|
5993
|
+
if (!address || typeof address === "string") {
|
|
5994
|
+
await closeServer(server);
|
|
5995
|
+
throw new Error("Could not start local callback server.");
|
|
5996
|
+
}
|
|
5997
|
+
const callbackUrl = `http://${CALLBACK_HOST}:${address.port}${CALLBACK_PATH}`;
|
|
5998
|
+
const authUrl = new URL("/api/cli/auth/start", authBaseUrl);
|
|
5999
|
+
authUrl.searchParams.set("callback", callbackUrl);
|
|
6000
|
+
authUrl.searchParams.set("state", state);
|
|
6001
|
+
const browserOpened = await openExternalUrl(authUrl.toString());
|
|
6002
|
+
options.onAuthUrl?.(authUrl.toString(), browserOpened);
|
|
6003
|
+
let settled = false;
|
|
6004
|
+
try {
|
|
6005
|
+
const apiKey = await new Promise((resolve, reject) => {
|
|
6006
|
+
const timeout = setTimeout(() => {
|
|
6007
|
+
if (settled) return;
|
|
6008
|
+
settled = true;
|
|
6009
|
+
reject(new Error(`Timed out after ${Math.round(timeoutMs / 1e3)}s waiting for browser authentication.`));
|
|
6010
|
+
}, timeoutMs);
|
|
6011
|
+
server.on("request", (req, res) => {
|
|
6012
|
+
const url = new URL(req.url || "/", `http://${CALLBACK_HOST}`);
|
|
6013
|
+
if (url.pathname !== CALLBACK_PATH) {
|
|
6014
|
+
res.statusCode = 404;
|
|
6015
|
+
res.end("Not found");
|
|
6016
|
+
return;
|
|
6017
|
+
}
|
|
6018
|
+
const returnedState = url.searchParams.get("state");
|
|
6019
|
+
const apiKey2 = url.searchParams.get("api_key");
|
|
6020
|
+
const error2 = url.searchParams.get("error");
|
|
6021
|
+
if (!returnedState || returnedState !== state) {
|
|
6022
|
+
res.statusCode = 400;
|
|
6023
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
6024
|
+
res.end(renderHtmlPage("Granular login failed", "State mismatch. Please close this window and retry.", "error"));
|
|
6025
|
+
if (!settled) {
|
|
6026
|
+
settled = true;
|
|
6027
|
+
clearTimeout(timeout);
|
|
6028
|
+
reject(new Error("Browser auth state mismatch."));
|
|
6029
|
+
}
|
|
6030
|
+
return;
|
|
6031
|
+
}
|
|
6032
|
+
if (error2) {
|
|
6033
|
+
res.statusCode = 400;
|
|
6034
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
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
|
+
);
|
|
6042
|
+
if (!settled) {
|
|
6043
|
+
settled = true;
|
|
6044
|
+
clearTimeout(timeout);
|
|
6045
|
+
reject(new Error(describeAuthError(error2)));
|
|
6046
|
+
}
|
|
6047
|
+
return;
|
|
6048
|
+
}
|
|
6049
|
+
if (!apiKey2) {
|
|
6050
|
+
res.statusCode = 400;
|
|
6051
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
6052
|
+
res.end(renderHtmlPage("Granular login failed", "No API key was returned. Please retry.", "error"));
|
|
6053
|
+
if (!settled) {
|
|
6054
|
+
settled = true;
|
|
6055
|
+
clearTimeout(timeout);
|
|
6056
|
+
reject(new Error("No API key returned from browser auth."));
|
|
6057
|
+
}
|
|
6058
|
+
return;
|
|
6059
|
+
}
|
|
6060
|
+
res.statusCode = 200;
|
|
6061
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
6062
|
+
res.end(renderHtmlPage("Granular login complete", "Authentication succeeded. You can close this window.", "success"));
|
|
6063
|
+
if (!settled) {
|
|
6064
|
+
settled = true;
|
|
6065
|
+
clearTimeout(timeout);
|
|
6066
|
+
resolve(apiKey2);
|
|
6067
|
+
}
|
|
6068
|
+
});
|
|
6069
|
+
});
|
|
6070
|
+
return apiKey;
|
|
6071
|
+
} finally {
|
|
6072
|
+
await closeServer(server);
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
5725
6075
|
|
|
5726
6076
|
// ../../node_modules/.bun/chalk@5.4.1/node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
5727
6077
|
var ANSI_BACKGROUND_OFFSET = 10;
|
|
@@ -7311,12 +7661,15 @@ function prompt(question, defaultValue) {
|
|
|
7311
7661
|
});
|
|
7312
7662
|
});
|
|
7313
7663
|
}
|
|
7314
|
-
function
|
|
7664
|
+
function waitForEnter(message) {
|
|
7665
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
7666
|
+
return Promise.resolve();
|
|
7667
|
+
}
|
|
7315
7668
|
const rl = readline__namespace.createInterface({ input: process.stdin, output: process.stdout });
|
|
7316
7669
|
return new Promise((resolve) => {
|
|
7317
|
-
rl.question(` ${
|
|
7670
|
+
rl.question(` ${message}`, () => {
|
|
7318
7671
|
rl.close();
|
|
7319
|
-
resolve(
|
|
7672
|
+
resolve();
|
|
7320
7673
|
});
|
|
7321
7674
|
});
|
|
7322
7675
|
}
|
|
@@ -7339,13 +7692,27 @@ async function initCommand(projectName, options) {
|
|
|
7339
7692
|
}
|
|
7340
7693
|
let apiKey = loadApiKey();
|
|
7341
7694
|
if (!apiKey) {
|
|
7342
|
-
info("No API key found.
|
|
7343
|
-
|
|
7344
|
-
dim("Get your API key at https://granular.dev/dashboard/api-keys");
|
|
7695
|
+
info("No API key found. Starting browser login...");
|
|
7696
|
+
await waitForEnter("Press Enter to open the login page in your browser...");
|
|
7345
7697
|
console.log();
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7698
|
+
const waiting = spinner("Opening browser and waiting for authentication...");
|
|
7699
|
+
try {
|
|
7700
|
+
apiKey = await loginWithBrowser({
|
|
7701
|
+
authBaseUrl: loadAuthUrl(),
|
|
7702
|
+
onAuthUrl: (authUrl, opened) => {
|
|
7703
|
+
if (!opened) {
|
|
7704
|
+
waiting.stop();
|
|
7705
|
+
warn("Could not open your browser automatically.");
|
|
7706
|
+
dim(`Open this URL to continue:
|
|
7707
|
+
${authUrl}`);
|
|
7708
|
+
waiting.start();
|
|
7709
|
+
}
|
|
7710
|
+
}
|
|
7711
|
+
});
|
|
7712
|
+
waiting.succeed(" Browser authentication completed.");
|
|
7713
|
+
} catch (err) {
|
|
7714
|
+
waiting.fail(` Browser authentication failed: ${err.message}`);
|
|
7715
|
+
dim("Run `granular login --manual` to paste an API key directly.");
|
|
7349
7716
|
process.exit(1);
|
|
7350
7717
|
}
|
|
7351
7718
|
} else {
|
|
@@ -7439,7 +7806,7 @@ async function initCommand(projectName, options) {
|
|
|
7439
7806
|
hint("granular simulate", "opens app.granular.software/simulator for this sandbox");
|
|
7440
7807
|
console.log();
|
|
7441
7808
|
}
|
|
7442
|
-
function
|
|
7809
|
+
function promptSecret(question) {
|
|
7443
7810
|
const rl = readline__namespace.createInterface({ input: process.stdin, output: process.stdout });
|
|
7444
7811
|
return new Promise((resolve) => {
|
|
7445
7812
|
rl.question(` ${question}: `, (answer) => {
|
|
@@ -7448,26 +7815,73 @@ function promptSecret2(question) {
|
|
|
7448
7815
|
});
|
|
7449
7816
|
});
|
|
7450
7817
|
}
|
|
7451
|
-
|
|
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
|
+
}
|
|
7830
|
+
function maskApiKey(apiKey) {
|
|
7831
|
+
if (apiKey.length < 10) return `${apiKey}...`;
|
|
7832
|
+
return `${apiKey.substring(0, 10)}...`;
|
|
7833
|
+
}
|
|
7834
|
+
async function loginCommand(options = {}) {
|
|
7452
7835
|
printHeader();
|
|
7453
7836
|
const existing = loadApiKey();
|
|
7454
7837
|
if (existing) {
|
|
7455
7838
|
info("An API key is already configured.");
|
|
7456
|
-
dim(`Current: ${existing
|
|
7839
|
+
dim(`Current: ${maskApiKey(existing)}`);
|
|
7457
7840
|
console.log();
|
|
7458
7841
|
}
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7842
|
+
const apiUrl = loadApiUrl();
|
|
7843
|
+
let apiKey = options.apiKey?.trim();
|
|
7844
|
+
if (!apiKey) {
|
|
7845
|
+
if (options.manual) {
|
|
7846
|
+
dim("Get your API key at https://app.granular.software/w/default/api-keys");
|
|
7847
|
+
console.log();
|
|
7848
|
+
apiKey = await promptSecret("Enter your API key");
|
|
7849
|
+
} else {
|
|
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();
|
|
7853
|
+
const waiting = spinner("Opening browser and waiting for authentication...");
|
|
7854
|
+
try {
|
|
7855
|
+
apiKey = await loginWithBrowser({
|
|
7856
|
+
authBaseUrl: loadAuthUrl(),
|
|
7857
|
+
timeoutMs,
|
|
7858
|
+
onAuthUrl: (authUrl, opened) => {
|
|
7859
|
+
if (!opened) {
|
|
7860
|
+
waiting.stop();
|
|
7861
|
+
warn("Could not open your browser automatically.");
|
|
7862
|
+
dim(`Open this URL to continue:
|
|
7863
|
+
${authUrl}`);
|
|
7864
|
+
waiting.start();
|
|
7865
|
+
}
|
|
7866
|
+
}
|
|
7867
|
+
});
|
|
7868
|
+
waiting.succeed(" Browser authentication completed.");
|
|
7869
|
+
} catch (err) {
|
|
7870
|
+
waiting.fail(` Browser authentication failed: ${err.message}`);
|
|
7871
|
+
dim("Retry with `granular login --manual` to paste an API key directly.");
|
|
7872
|
+
process.exit(1);
|
|
7873
|
+
}
|
|
7874
|
+
}
|
|
7875
|
+
}
|
|
7462
7876
|
if (!apiKey) {
|
|
7463
7877
|
error("API key is required.");
|
|
7464
7878
|
process.exit(1);
|
|
7465
7879
|
}
|
|
7466
|
-
if (!apiKey.startsWith("sk_")) {
|
|
7467
|
-
warn(
|
|
7880
|
+
if (!apiKey.startsWith("sk_") && !apiKey.startsWith("gn_sk_")) {
|
|
7881
|
+
warn("API key does not look like a recognized Granular/WorkOS key. Continuing anyway...");
|
|
7468
7882
|
}
|
|
7469
7883
|
const spinner2 = spinner("Validating...");
|
|
7470
|
-
const api = new ApiClient(apiKey,
|
|
7884
|
+
const api = new ApiClient(apiKey, apiUrl);
|
|
7471
7885
|
const valid = await api.validateKey();
|
|
7472
7886
|
if (!valid) {
|
|
7473
7887
|
spinner2.fail(" Invalid API key.");
|
|
@@ -7475,7 +7889,7 @@ async function loginCommand() {
|
|
|
7475
7889
|
}
|
|
7476
7890
|
saveApiKey(apiKey);
|
|
7477
7891
|
spinner2.succeed(" Authenticated successfully.");
|
|
7478
|
-
dim("API key saved to .env.local");
|
|
7892
|
+
dim("API key saved to .env.local (used by CLI and SDK examples)");
|
|
7479
7893
|
console.log();
|
|
7480
7894
|
}
|
|
7481
7895
|
|
|
@@ -8217,9 +8631,16 @@ program2.command("init [project-name]").description("Initialize a new Granular p
|
|
|
8217
8631
|
process.exit(1);
|
|
8218
8632
|
}
|
|
8219
8633
|
});
|
|
8220
|
-
program2.command("login").description("Authenticate with
|
|
8634
|
+
program2.command("login").description("Authenticate with Granular (browser by default)").option("--manual", "Prompt for API key instead of browser login").option("--api-key <key>", "Use a provided API key directly").option("--timeout <seconds>", "Browser auth timeout in seconds", (value) => {
|
|
8635
|
+
const n = Number.parseInt(value, 10);
|
|
8636
|
+
return Number.isFinite(n) ? n : 180;
|
|
8637
|
+
}).action(async (opts) => {
|
|
8221
8638
|
try {
|
|
8222
|
-
await loginCommand(
|
|
8639
|
+
await loginCommand({
|
|
8640
|
+
manual: opts.manual,
|
|
8641
|
+
apiKey: opts.apiKey,
|
|
8642
|
+
timeout: opts.timeout
|
|
8643
|
+
});
|
|
8223
8644
|
} catch (err) {
|
|
8224
8645
|
error(err.message);
|
|
8225
8646
|
process.exit(1);
|
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[] */
|