@granular-software/sdk 0.4.10 → 0.4.11

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 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 DEFAULT_LOCAL_API_KEY = "gn_sk_tenant_default_principal_local_e2e_00000000";
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 isLocalApiUrl(apiUrl) {
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 && isLocalApiUrl(apiUrl)) {
9766
- apiKey = existing?.trim() || process.env.GRANULAR_LOCAL_API_KEY?.trim() || DEFAULT_LOCAL_API_KEY;
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 url = `${SIMULATOR_BASE}?sandboxId=${encodeURIComponent(sandboxId)}`;
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.js CHANGED
@@ -5316,6 +5316,7 @@ var JobImplementation = class {
5316
5316
  // src/endpoints.ts
5317
5317
  var LOCAL_API_URL = "ws://localhost:8787/granular";
5318
5318
  var PRODUCTION_API_URL = "wss://cf-api-gateway.arthur6084.workers.dev/granular";
5319
+ var DEFAULT_LOCAL_API_KEY = "gn_sk_tenant_default_principal_local_e2e_00000000";
5319
5320
  function readEnv(name) {
5320
5321
  if (typeof process === "undefined" || !process.env) return void 0;
5321
5322
  return process.env[name];
@@ -5333,6 +5334,21 @@ function isTruthy(value) {
5333
5334
  const normalized = value.trim().toLowerCase();
5334
5335
  return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
5335
5336
  }
5337
+ function isLocalApiUrl(url) {
5338
+ try {
5339
+ const parsed = new URL(url);
5340
+ return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
5341
+ } catch {
5342
+ return false;
5343
+ }
5344
+ }
5345
+ function resolveAuthTokenForApiUrl(authToken, apiUrl) {
5346
+ if (!authToken.startsWith("sk_") || !isLocalApiUrl(apiUrl) || isTruthy(readEnv("GRANULAR_DISABLE_LOCAL_API_KEY_FALLBACK"))) {
5347
+ return authToken;
5348
+ }
5349
+ const override = readEnv("GRANULAR_LOCAL_API_KEY")?.trim();
5350
+ return override || DEFAULT_LOCAL_API_KEY;
5351
+ }
5336
5352
  function resolveEndpointMode(explicitMode) {
5337
5353
  const explicit = normalizeMode(explicitMode);
5338
5354
  if (explicit === "local" || explicit === "production") {
@@ -6251,8 +6267,8 @@ var Granular = class {
6251
6267
  if (!auth) {
6252
6268
  throw new Error("Granular client requires either apiKey or token. Set GRANULAR_API_KEY or GRANULAR_TOKEN, or pass one in options.");
6253
6269
  }
6254
- this.apiKey = auth;
6255
6270
  this.apiUrl = resolveApiUrl(options.apiUrl, options.endpointMode);
6271
+ this.apiKey = resolveAuthTokenForApiUrl(auth, this.apiUrl);
6256
6272
  this.tokenProvider = options.tokenProvider;
6257
6273
  this.WebSocketCtor = options.WebSocketCtor;
6258
6274
  this.onUnexpectedClose = options.onUnexpectedClose;