@granular-software/sdk 0.4.10 → 0.4.12
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/README.md +2 -0
- package/dist/cli/index.js +31 -9
- package/dist/index.d.mts +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +52 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +52 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,6 +48,8 @@ CLI overrides:
|
|
|
48
48
|
- `granular --prod <command>`
|
|
49
49
|
- `granular --env local|production <command>`
|
|
50
50
|
|
|
51
|
+
When the resolved API URL points at `localhost` or `127.0.0.1`, the SDK and CLI automatically swap opaque `sk_...` WorkOS keys for the local dev key `gn_sk_tenant_default_principal_local_e2e_00000000` so local runs work without server-side WorkOS validation. Override with `GRANULAR_LOCAL_API_KEY=...` or disable with `GRANULAR_DISABLE_LOCAL_API_KEY_FALLBACK=true`.
|
|
52
|
+
|
|
51
53
|
## Agent documentation (CLI)
|
|
52
54
|
|
|
53
55
|
Projects created with `granular init` can include Markdown for coding agents (optional `AGENTS.md` plus generated guides):
|
package/dist/cli/index.js
CHANGED
|
@@ -5388,6 +5388,7 @@ var import_dotenv = __toESM(require_main());
|
|
|
5388
5388
|
// src/endpoints.ts
|
|
5389
5389
|
var LOCAL_API_URL = "ws://localhost:8787/granular";
|
|
5390
5390
|
var PRODUCTION_API_URL = "wss://cf-api-gateway.arthur6084.workers.dev/granular";
|
|
5391
|
+
var DEFAULT_LOCAL_API_KEY = "gn_sk_tenant_default_principal_local_e2e_00000000";
|
|
5391
5392
|
var LOCAL_AUTH_URL = "http://localhost:3000";
|
|
5392
5393
|
var PRODUCTION_AUTH_URL = "https://app.granular.software";
|
|
5393
5394
|
function readEnv(name) {
|
|
@@ -5407,6 +5408,21 @@ function isTruthy(value) {
|
|
|
5407
5408
|
const normalized = value.trim().toLowerCase();
|
|
5408
5409
|
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
|
5409
5410
|
}
|
|
5411
|
+
function isLocalApiUrl(url) {
|
|
5412
|
+
try {
|
|
5413
|
+
const parsed = new URL(url);
|
|
5414
|
+
return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
5415
|
+
} catch {
|
|
5416
|
+
return false;
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
function resolveAuthTokenForApiUrl(authToken, apiUrl) {
|
|
5420
|
+
if (!authToken.startsWith("sk_") || !isLocalApiUrl(apiUrl) || isTruthy(readEnv("GRANULAR_DISABLE_LOCAL_API_KEY_FALLBACK"))) {
|
|
5421
|
+
return authToken;
|
|
5422
|
+
}
|
|
5423
|
+
const override = readEnv("GRANULAR_LOCAL_API_KEY")?.trim();
|
|
5424
|
+
return override || DEFAULT_LOCAL_API_KEY;
|
|
5425
|
+
}
|
|
5410
5426
|
function resolveEndpointMode(explicitMode) {
|
|
5411
5427
|
const explicit = normalizeMode(explicitMode);
|
|
5412
5428
|
if (explicit === "local" || explicit === "production") {
|
|
@@ -6762,7 +6778,7 @@ var ApiClient = class {
|
|
|
6762
6778
|
apiKey;
|
|
6763
6779
|
baseUrl;
|
|
6764
6780
|
constructor(apiKey, apiUrl) {
|
|
6765
|
-
this.apiKey = apiKey;
|
|
6781
|
+
this.apiKey = resolveAuthTokenForApiUrl(apiKey, apiUrl);
|
|
6766
6782
|
this.baseUrl = apiUrl.replace("wss://", "https://").replace("ws://", "http://").replace(/\/ws$/, "");
|
|
6767
6783
|
}
|
|
6768
6784
|
async request(path4, options = {}) {
|
|
@@ -9722,7 +9738,7 @@ async function initCommand(projectName, options) {
|
|
|
9722
9738
|
dim(" The seed script shows how records enter the graph. The effects script shows how external systems plug in.");
|
|
9723
9739
|
console.log();
|
|
9724
9740
|
}
|
|
9725
|
-
var
|
|
9741
|
+
var DEFAULT_LOCAL_API_KEY2 = "gn_sk_tenant_default_principal_local_e2e_00000000";
|
|
9726
9742
|
function promptSecret(question) {
|
|
9727
9743
|
const rl = readline__namespace.createInterface({ input: process.stdin, output: process.stdout });
|
|
9728
9744
|
return new Promise((resolve) => {
|
|
@@ -9748,7 +9764,7 @@ function maskApiKey(apiKey) {
|
|
|
9748
9764
|
if (apiKey.length < 10) return `${apiKey}...`;
|
|
9749
9765
|
return `${apiKey.substring(0, 10)}...`;
|
|
9750
9766
|
}
|
|
9751
|
-
function
|
|
9767
|
+
function isLocalApiUrl2(apiUrl) {
|
|
9752
9768
|
return apiUrl.startsWith("ws://localhost:") || apiUrl.startsWith("wss://localhost:") || apiUrl.startsWith("ws://127.0.0.1:") || apiUrl.startsWith("wss://127.0.0.1:") || apiUrl.startsWith("http://localhost:") || apiUrl.startsWith("https://localhost:") || apiUrl.startsWith("http://127.0.0.1:") || apiUrl.startsWith("https://127.0.0.1:");
|
|
9753
9769
|
}
|
|
9754
9770
|
async function loginCommand(options = {}) {
|
|
@@ -9762,8 +9778,8 @@ async function loginCommand(options = {}) {
|
|
|
9762
9778
|
const apiUrl = loadApiUrl();
|
|
9763
9779
|
let apiKey = options.apiKey?.trim();
|
|
9764
9780
|
if (!apiKey) {
|
|
9765
|
-
if (!options.manual &&
|
|
9766
|
-
apiKey = existing?.trim() || process.env.GRANULAR_LOCAL_API_KEY?.trim() ||
|
|
9781
|
+
if (!options.manual && isLocalApiUrl2(apiUrl)) {
|
|
9782
|
+
apiKey = existing?.trim() || process.env.GRANULAR_LOCAL_API_KEY?.trim() || DEFAULT_LOCAL_API_KEY2;
|
|
9767
9783
|
info("Using local Granular API key for localhost development.");
|
|
9768
9784
|
} else if (options.manual) {
|
|
9769
9785
|
dim("Get your API key at https://app.granular.software/w/default/api-keys");
|
|
@@ -10324,13 +10340,19 @@ function openUrl(url) {
|
|
|
10324
10340
|
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
|
|
10325
10341
|
child_process.spawn(cmd, [url], { stdio: "ignore", shell: platform === "win32" });
|
|
10326
10342
|
}
|
|
10327
|
-
async function simulateCommand(sandboxIdArg) {
|
|
10343
|
+
async function simulateCommand(sandboxIdArg, options) {
|
|
10328
10344
|
const sandboxId = sandboxIdArg ?? resolveConfig().sandboxId;
|
|
10329
10345
|
if (!sandboxId) {
|
|
10330
10346
|
error("No sandbox ID. Run from a project with `granular init` or pass a sandbox ID: granular simulate <sandbox-id>");
|
|
10331
10347
|
process.exit(1);
|
|
10332
10348
|
}
|
|
10333
|
-
const
|
|
10349
|
+
const params = new URLSearchParams({
|
|
10350
|
+
sandboxId
|
|
10351
|
+
});
|
|
10352
|
+
if (options?.subjectId) {
|
|
10353
|
+
params.set("subject_id", options.subjectId);
|
|
10354
|
+
}
|
|
10355
|
+
const url = `${SIMULATOR_BASE}?${params.toString()}`;
|
|
10334
10356
|
info(`Opening simulator: ${url}`);
|
|
10335
10357
|
openUrl(url);
|
|
10336
10358
|
}
|
|
@@ -10470,9 +10492,9 @@ program2.command("document").description("Generate GRANULAR_SANDBOX.md (agent re
|
|
|
10470
10492
|
process.exit(1);
|
|
10471
10493
|
}
|
|
10472
10494
|
});
|
|
10473
|
-
program2.command("simulate [sandbox-id]").description("Open the Granular simulator in the browser for the current (or given) sandbox").action(async (sandboxId) => {
|
|
10495
|
+
program2.command("simulate [sandbox-id]").description("Open the Granular simulator in the browser for the current (or given) sandbox").option("--subject-id <subjectId>", "Open the simulator preloaded for a specific subject").action(async (sandboxId, options) => {
|
|
10474
10496
|
try {
|
|
10475
|
-
await simulateCommand(sandboxId);
|
|
10497
|
+
await simulateCommand(sandboxId, options);
|
|
10476
10498
|
} catch (err) {
|
|
10477
10499
|
error(err.message);
|
|
10478
10500
|
process.exit(1);
|
package/dist/index.d.mts
CHANGED
|
@@ -457,7 +457,7 @@ interface EffectsChangedEvent extends ToolsChangedEvent {
|
|
|
457
457
|
}
|
|
458
458
|
type EffectHandler = ToolHandler;
|
|
459
459
|
type InstanceEffectHandler = InstanceToolHandler;
|
|
460
|
-
type JobStatus = 'queued' | 'running' | 'succeeded' | 'failed' | 'timeout' | 'canceled';
|
|
460
|
+
type JobStatus = 'queued' | 'running' | 'awaitingTool' | 'awaitingHuman' | 'succeeded' | 'failed' | 'timeout' | 'canceled';
|
|
461
461
|
type JobFeedbackSentiment = 'good' | 'bad';
|
|
462
462
|
interface JobFeedbackToolCall {
|
|
463
463
|
callId?: string;
|
|
@@ -529,8 +529,15 @@ interface Prompt {
|
|
|
529
529
|
type: 'confirm' | 'choice' | 'input';
|
|
530
530
|
title: string;
|
|
531
531
|
message: string;
|
|
532
|
-
options?: string
|
|
532
|
+
options?: Array<string | {
|
|
533
|
+
value: string;
|
|
534
|
+
label: string;
|
|
535
|
+
description?: string;
|
|
536
|
+
}>;
|
|
533
537
|
defaultValue?: unknown;
|
|
538
|
+
placeholder?: string;
|
|
539
|
+
allowEmpty?: boolean;
|
|
540
|
+
metadata?: Record<string, unknown>;
|
|
534
541
|
}
|
|
535
542
|
type SessionHeapFieldType = 'string' | 'number' | 'boolean' | 'null' | 'unknown';
|
|
536
543
|
interface SessionHeapFieldValue {
|
package/dist/index.d.ts
CHANGED
|
@@ -457,7 +457,7 @@ interface EffectsChangedEvent extends ToolsChangedEvent {
|
|
|
457
457
|
}
|
|
458
458
|
type EffectHandler = ToolHandler;
|
|
459
459
|
type InstanceEffectHandler = InstanceToolHandler;
|
|
460
|
-
type JobStatus = 'queued' | 'running' | 'succeeded' | 'failed' | 'timeout' | 'canceled';
|
|
460
|
+
type JobStatus = 'queued' | 'running' | 'awaitingTool' | 'awaitingHuman' | 'succeeded' | 'failed' | 'timeout' | 'canceled';
|
|
461
461
|
type JobFeedbackSentiment = 'good' | 'bad';
|
|
462
462
|
interface JobFeedbackToolCall {
|
|
463
463
|
callId?: string;
|
|
@@ -529,8 +529,15 @@ interface Prompt {
|
|
|
529
529
|
type: 'confirm' | 'choice' | 'input';
|
|
530
530
|
title: string;
|
|
531
531
|
message: string;
|
|
532
|
-
options?: string
|
|
532
|
+
options?: Array<string | {
|
|
533
|
+
value: string;
|
|
534
|
+
label: string;
|
|
535
|
+
description?: string;
|
|
536
|
+
}>;
|
|
533
537
|
defaultValue?: unknown;
|
|
538
|
+
placeholder?: string;
|
|
539
|
+
allowEmpty?: boolean;
|
|
540
|
+
metadata?: Record<string, unknown>;
|
|
534
541
|
}
|
|
535
542
|
type SessionHeapFieldType = 'string' | 'number' | 'boolean' | 'null' | 'unknown';
|
|
536
543
|
interface SessionHeapFieldValue {
|
package/dist/index.js
CHANGED
|
@@ -4650,7 +4650,7 @@ var Session = class {
|
|
|
4650
4650
|
* Respond to a prompt request from the sandbox
|
|
4651
4651
|
*/
|
|
4652
4652
|
async answerPrompt(promptId, answer) {
|
|
4653
|
-
await this.client.call("prompt.answer", { promptId, value: answer });
|
|
4653
|
+
await this.client.call("prompt.answer", { promptId, answer, value: answer });
|
|
4654
4654
|
}
|
|
4655
4655
|
/**
|
|
4656
4656
|
* Get the current list of available effects.
|
|
@@ -4967,13 +4967,31 @@ import { ${allImports} } from "./sandbox-tools";
|
|
|
4967
4967
|
});
|
|
4968
4968
|
}
|
|
4969
4969
|
setupEventHandlers() {
|
|
4970
|
+
this.client.on("open", (payload) => this.emit("open", payload || {}));
|
|
4970
4971
|
this.client.on("sync", (doc) => {
|
|
4971
4972
|
this.currentDomainRevision = this.extractDomainRevisionFromDoc(doc);
|
|
4972
4973
|
this.emit("sync", doc);
|
|
4973
4974
|
this.checkForToolChanges();
|
|
4974
4975
|
});
|
|
4975
|
-
|
|
4976
|
+
const emitPrompt = (payload) => {
|
|
4977
|
+
const raw = payload;
|
|
4978
|
+
const prompt = raw.prompt || {
|
|
4979
|
+
id: typeof raw.id === "string" ? raw.id : String(raw.promptId || ""),
|
|
4980
|
+
type: raw.type === "confirm" || raw.type === "choice" || raw.type === "input" ? raw.type : raw.promptType === "confirm" || raw.promptType === "choice" ? raw.promptType : "input",
|
|
4981
|
+
title: typeof raw.title === "string" ? raw.title : "Input required",
|
|
4982
|
+
message: typeof raw.message === "string" ? raw.message : "",
|
|
4983
|
+
options: raw.options,
|
|
4984
|
+
defaultValue: raw.defaultValue,
|
|
4985
|
+
placeholder: raw.placeholder,
|
|
4986
|
+
allowEmpty: raw.allowEmpty,
|
|
4987
|
+
metadata: raw.metadata
|
|
4988
|
+
};
|
|
4989
|
+
this.emit("prompt", prompt);
|
|
4990
|
+
};
|
|
4991
|
+
this.client.on("prompt", emitPrompt);
|
|
4992
|
+
this.client.on("prompt.request", emitPrompt);
|
|
4976
4993
|
this.client.on("disconnect", (payload) => this.emit("disconnect", payload || {}));
|
|
4994
|
+
this.client.on("reconnect_error", (payload) => this.emit("reconnect_error", payload || {}));
|
|
4977
4995
|
this.client.on("job.status", (data) => {
|
|
4978
4996
|
this.emit("job:status", data);
|
|
4979
4997
|
});
|
|
@@ -5045,6 +5063,8 @@ function normalizeJobStatus(status) {
|
|
|
5045
5063
|
case "failed":
|
|
5046
5064
|
case "running":
|
|
5047
5065
|
case "queued":
|
|
5066
|
+
case "awaitingTool":
|
|
5067
|
+
case "awaitingHuman":
|
|
5048
5068
|
case "succeeded":
|
|
5049
5069
|
case "timeout":
|
|
5050
5070
|
case "canceled":
|
|
@@ -5179,6 +5199,19 @@ var JobImplementation = class {
|
|
|
5179
5199
|
this.client.on(`job.${id}.error`, (error) => {
|
|
5180
5200
|
this.finalize("failed", void 0, error);
|
|
5181
5201
|
});
|
|
5202
|
+
this.client.on("job.status", (data) => {
|
|
5203
|
+
const jobData = data;
|
|
5204
|
+
if (jobData.jobId !== id) {
|
|
5205
|
+
return;
|
|
5206
|
+
}
|
|
5207
|
+
const normalizedStatus = normalizeJobStatus(jobData.status);
|
|
5208
|
+
this.status = normalizedStatus;
|
|
5209
|
+
this.metadata.status = normalizedStatus;
|
|
5210
|
+
if (normalizedStatus === "running") {
|
|
5211
|
+
this.markStarted();
|
|
5212
|
+
}
|
|
5213
|
+
this.emit("status", normalizedStatus);
|
|
5214
|
+
});
|
|
5182
5215
|
this.client.on("job.completed", (data) => {
|
|
5183
5216
|
const jobData = data;
|
|
5184
5217
|
if (jobData.jobId === id) {
|
|
@@ -5316,6 +5349,7 @@ var JobImplementation = class {
|
|
|
5316
5349
|
// src/endpoints.ts
|
|
5317
5350
|
var LOCAL_API_URL = "ws://localhost:8787/granular";
|
|
5318
5351
|
var PRODUCTION_API_URL = "wss://cf-api-gateway.arthur6084.workers.dev/granular";
|
|
5352
|
+
var DEFAULT_LOCAL_API_KEY = "gn_sk_tenant_default_principal_local_e2e_00000000";
|
|
5319
5353
|
function readEnv(name) {
|
|
5320
5354
|
if (typeof process === "undefined" || !process.env) return void 0;
|
|
5321
5355
|
return process.env[name];
|
|
@@ -5333,6 +5367,21 @@ function isTruthy(value) {
|
|
|
5333
5367
|
const normalized = value.trim().toLowerCase();
|
|
5334
5368
|
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
|
5335
5369
|
}
|
|
5370
|
+
function isLocalApiUrl(url) {
|
|
5371
|
+
try {
|
|
5372
|
+
const parsed = new URL(url);
|
|
5373
|
+
return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
5374
|
+
} catch {
|
|
5375
|
+
return false;
|
|
5376
|
+
}
|
|
5377
|
+
}
|
|
5378
|
+
function resolveAuthTokenForApiUrl(authToken, apiUrl) {
|
|
5379
|
+
if (!authToken.startsWith("sk_") || !isLocalApiUrl(apiUrl) || isTruthy(readEnv("GRANULAR_DISABLE_LOCAL_API_KEY_FALLBACK"))) {
|
|
5380
|
+
return authToken;
|
|
5381
|
+
}
|
|
5382
|
+
const override = readEnv("GRANULAR_LOCAL_API_KEY")?.trim();
|
|
5383
|
+
return override || DEFAULT_LOCAL_API_KEY;
|
|
5384
|
+
}
|
|
5336
5385
|
function resolveEndpointMode(explicitMode) {
|
|
5337
5386
|
const explicit = normalizeMode(explicitMode);
|
|
5338
5387
|
if (explicit === "local" || explicit === "production") {
|
|
@@ -6251,8 +6300,8 @@ var Granular = class {
|
|
|
6251
6300
|
if (!auth) {
|
|
6252
6301
|
throw new Error("Granular client requires either apiKey or token. Set GRANULAR_API_KEY or GRANULAR_TOKEN, or pass one in options.");
|
|
6253
6302
|
}
|
|
6254
|
-
this.apiKey = auth;
|
|
6255
6303
|
this.apiUrl = resolveApiUrl(options.apiUrl, options.endpointMode);
|
|
6304
|
+
this.apiKey = resolveAuthTokenForApiUrl(auth, this.apiUrl);
|
|
6256
6305
|
this.tokenProvider = options.tokenProvider;
|
|
6257
6306
|
this.WebSocketCtor = options.WebSocketCtor;
|
|
6258
6307
|
this.onUnexpectedClose = options.onUnexpectedClose;
|