statelyai 0.6.0 → 0.6.1

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
@@ -13,11 +13,10 @@ This package contains the `statelyai` command implementation.
13
13
  statelyai login
14
14
  ```
15
15
 
16
- Automation can still pass `--api-key`, set `STATELY_API_KEY`, or set
17
- `STATELY_ACCESS_TOKEN`. For no-auth self-hosted deployments, run
18
- `statelyai login --auth none`.
16
+ Automation can set `STATELY_ACCESS_TOKEN` for OAuth, or pass `--api-key` /
17
+ `STATELY_API_KEY` for legacy API-key commands.
19
18
 
20
- For a self-hosted or local MCP/API resource, discover OAuth from that resource:
19
+ For a custom OAuth protected resource, discover OAuth from that resource:
21
20
 
22
21
  ```bash
23
22
  statelyai login --base-url http://localhost:3000/registry/api/mcp
@@ -25,6 +24,8 @@ statelyai login --base-url http://localhost:3000/registry/api/mcp
25
24
 
26
25
  `statelyai login --auth bearer` uses the same browser OAuth flow explicitly.
27
26
  `statelyai login --api-key` keeps the legacy manual API-key path.
27
+ `statelyai open` requires OAuth (`statelyai login` or `STATELY_ACCESS_TOKEN`);
28
+ free accounts open read-only root-level views.
28
29
 
29
30
  2. Initialize the current repo:
30
31
 
package/dist/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { _ as run } from "./cli-Buz_BC3I.mjs";
2
+ import { v as run } from "./cli-jiby9qdf.mjs";
3
3
 
4
4
  //#region src/bin.ts
5
5
  run();
@@ -11,7 +11,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
11
11
  import { promisify } from "node:util";
12
12
  import { Args, Command, Flags, flush, handle, run } from "@oclif/core";
13
13
  import os from "node:os";
14
- import { StudioApiError, buildAuthorizeUrl, createPkcePair, createStatelyClient, discoverAuthorizationServer, discoverProtectedResource, exchangeCodeForToken, getStatelyPragma } from "@statelyai/sdk";
14
+ import { StudioApiError, buildAuthorizeUrl, createPkcePair, createStatelyClient, discoverAuthorizationServer, discoverProtectedResource, exchangeCodeForToken, getStatelyPragma, registerOAuthClient } from "@statelyai/sdk";
15
15
  import { planSync, pullSync, pushLocalMachineLinks } from "@statelyai/sdk/sync";
16
16
 
17
17
  //#region src/cliHost.ts
@@ -86,6 +86,7 @@ var RemoteEditorSession = class {
86
86
  lastPersistedGraph;
87
87
  currentSourceLocations;
88
88
  currentFormat;
89
+ editorSyncAccess;
89
90
  ready = false;
90
91
  pendingMessages = [];
91
92
  pendingRetrievals = /* @__PURE__ */ new Map();
@@ -146,13 +147,16 @@ var RemoteEditorSession = class {
146
147
  const update = await this.parseRootDocument();
147
148
  this.currentFormat = update.format;
148
149
  this.currentSourceLocations = update.sourceLocations;
150
+ this.editorSyncAccess = update.access;
149
151
  this.updateSourceLocations(update.sourceLocations);
150
152
  this.postMessage({
151
153
  type: "@statelyai.init",
152
154
  machine: update.machine,
153
155
  format: update.format,
154
156
  sourceLocations: update.sourceLocations,
155
- mode: "editing",
157
+ mode: update.access?.readOnly ? "viewing" : "editing",
158
+ readOnly: update.access?.readOnly,
159
+ capabilities: update.access?.capabilities,
156
160
  theme: "light",
157
161
  unsavedIndicator: { enabled: true },
158
162
  leftPanels: [],
@@ -173,6 +177,7 @@ var RemoteEditorSession = class {
173
177
  const update = await this.parseRootDocument();
174
178
  this.currentFormat = update.format;
175
179
  this.currentSourceLocations = update.sourceLocations;
180
+ this.editorSyncAccess = update.access ?? this.editorSyncAccess;
176
181
  this.updateSourceLocations(update.sourceLocations);
177
182
  this.postMessage({
178
183
  type: "@statelyai.update",
@@ -185,6 +190,16 @@ var RemoteEditorSession = class {
185
190
  }
186
191
  }
187
192
  async saveFromEditor(msg) {
193
+ if (this.editorSyncAccess?.readOnly) {
194
+ const message = "Your Stately plan allows read-only editor sync.";
195
+ this.postMessage({
196
+ type: "@statelyai.toast",
197
+ message,
198
+ toastType: "error"
199
+ });
200
+ this.showError(message);
201
+ return;
202
+ }
188
203
  const validationErrors = getValidationErrors(msg.validations);
189
204
  if (validationErrors.length > 0) {
190
205
  const message = formatValidationErrorMessage(validationErrors);
@@ -1359,6 +1374,23 @@ async function resolveApiKey(explicitApiKey) {
1359
1374
  };
1360
1375
  return { source: "missing" };
1361
1376
  }
1377
+ async function resolveOAuthAccessToken() {
1378
+ const envCredential = getEnvCredential();
1379
+ if (envCredential?.credential.type === "oauth") return {
1380
+ apiKey: envCredential.credential.accessToken,
1381
+ source: "env",
1382
+ detail: envCredential.variable
1383
+ };
1384
+ if (envCredential?.credential.type === "api_key") throw new Error("`statelyai open` requires OAuth login. Run `statelyai login` instead of using an API key.");
1385
+ const storedCredential = await getStoredCredential();
1386
+ if (storedCredential?.credential?.type === "oauth") return {
1387
+ apiKey: storedCredential.credential.accessToken,
1388
+ source: "stored",
1389
+ detail: describeCredentialBackend(storedCredential.backend, storedCredential.location)
1390
+ };
1391
+ if (storedCredential?.credential?.type === "api_key") throw new Error("`statelyai open` requires OAuth login. Run `statelyai login` to replace the stored API key.");
1392
+ return { source: "missing" };
1393
+ }
1362
1394
  function getDefaultBaseUrl() {
1363
1395
  return process.env.STATELY_API_URL ?? process.env.NEXT_PUBLIC_BASE_URL ?? process.env.NEXT_PUBLIC_STATELY_API_URL;
1364
1396
  }
@@ -1368,34 +1400,48 @@ function getDefaultEditorUrl() {
1368
1400
  function getResolvedStudioUrl(baseUrl) {
1369
1401
  return baseUrl ?? getDefaultBaseUrl() ?? "https://stately.ai";
1370
1402
  }
1371
- function getDefaultMcpResourceUrl() {
1372
- return process.env.STATELY_MCP_RESOURCE_URL ?? process.env.NEXT_PUBLIC_MCP_RESOURCE_URL ?? process.env.STATELY_MCP_API_BASE_URL ?? "https://editor.stately.ai/api/mcp";
1403
+ function getDefaultOAuthIssuer() {
1404
+ return process.env.STATELY_OAUTH_ISSUER ?? process.env.NEXT_PUBLIC_STATELY_OAUTH_ISSUER ?? "https://stately.ai";
1373
1405
  }
1374
1406
  function normalizeLoginResourceUrl(baseUrl) {
1375
- const raw = baseUrl ?? getDefaultMcpResourceUrl();
1407
+ const raw = baseUrl;
1376
1408
  const url = new URL(raw);
1377
1409
  if (url.pathname === "/" || url.pathname === "") url.pathname = "/api/mcp";
1378
1410
  return url.toString();
1379
1411
  }
1380
- function getOauthClientId() {
1381
- return process.env.STATELY_OAUTH_CLIENT_ID ?? process.env.NEXT_PUBLIC_STATELY_OAUTH_CLIENT_ID ?? "statelyai-cli";
1412
+ function getConfiguredOauthClientId() {
1413
+ return process.env.STATELY_OAUTH_CLIENT_ID ?? process.env.NEXT_PUBLIC_STATELY_OAUTH_CLIENT_ID;
1382
1414
  }
1383
1415
  async function discoverOAuthLogin(options = {}) {
1384
- const resourceUrl = normalizeLoginResourceUrl(options.baseUrl);
1385
- const protectedResource = await discoverProtectedResource(resourceUrl, { fetch: options.fetch });
1386
- const [issuer] = protectedResource.authorization_servers ?? [];
1387
- if (!issuer) throw new Error(`No OAuth authorization server advertised by ${resourceUrl}. Use \`statelyai login --auth none\` for no-auth self-hosted deployments.`);
1416
+ const resourceUrl = options.baseUrl ? normalizeLoginResourceUrl(options.baseUrl) : getDefaultOAuthIssuer();
1417
+ const protectedResource = options.baseUrl ? await discoverProtectedResource(resourceUrl, { fetch: options.fetch }) : void 0;
1418
+ const issuer = protectedResource ? protectedResource.authorization_servers?.[0] : resourceUrl;
1419
+ if (!issuer) throw new Error(`No OAuth authorization server advertised by ${resourceUrl}. Pass --base-url for a Stately OAuth-enabled resource or set STATELY_OAUTH_ISSUER.`);
1388
1420
  const authorizationServer = await discoverAuthorizationServer(issuer, { fetch: options.fetch });
1389
1421
  const authorizationEndpoint = authorizationServer.authorization_endpoint;
1390
1422
  const tokenEndpoint = authorizationServer.token_endpoint;
1391
1423
  if (!authorizationEndpoint || !tokenEndpoint) throw new Error(`Authorization server ${issuer} did not advertise authorization and token endpoints.`);
1424
+ const configuredClientId = options.clientId ?? getConfiguredOauthClientId();
1425
+ const scopes = protectedResource?.scopes_supported ?? [];
1426
+ let clientId = configuredClientId;
1427
+ if (!clientId && authorizationServer.registration_endpoint && options.redirectUri) try {
1428
+ clientId = (await registerOAuthClient({
1429
+ registrationEndpoint: authorizationServer.registration_endpoint,
1430
+ redirectUri: options.redirectUri,
1431
+ scopes,
1432
+ fetch: options.fetch
1433
+ })).client_id;
1434
+ } catch (error) {
1435
+ throw new Error(`OAuth dynamic client registration failed for ${authorizationServer.registration_endpoint}. Enable dynamic client registration on the OAuth authorization server, or set STATELY_OAUTH_CLIENT_ID to a registered OAuth client id.`, { cause: error });
1436
+ }
1437
+ if (!clientId) throw new Error(`Authorization server ${issuer} did not advertise dynamic client registration. Set STATELY_OAUTH_CLIENT_ID to a registered OAuth client id.`);
1392
1438
  return {
1393
1439
  authorizationServer,
1394
1440
  authorizationEndpoint,
1395
- clientId: options.clientId ?? getOauthClientId(),
1396
- protectedResource,
1441
+ clientId,
1442
+ ...protectedResource ? { protectedResource } : {},
1397
1443
  resourceUrl,
1398
- scopes: protectedResource.scopes_supported ?? [],
1444
+ scopes,
1399
1445
  tokenEndpoint
1400
1446
  };
1401
1447
  }
@@ -1564,7 +1610,8 @@ async function buildOAuthLoginStart(options) {
1564
1610
  const [discovery, pkce] = await Promise.all([discoverOAuthLogin({
1565
1611
  baseUrl: options.baseUrl,
1566
1612
  clientId: options.clientId,
1567
- fetch: options.fetch
1613
+ fetch: options.fetch,
1614
+ redirectUri: options.redirectUri
1568
1615
  }), createPkcePair()]);
1569
1616
  return {
1570
1617
  authorizeUrl: buildAuthorizeUrl({
@@ -2286,7 +2333,7 @@ var OpenCommand = class OpenCommand extends Command {
2286
2333
  }) };
2287
2334
  static flags = {
2288
2335
  help: Flags.help({ char: "h" }),
2289
- "api-key": Flags.string({ description: "Stately API key used when the editor server requires auth" }),
2336
+ "api-key": Flags.string({ description: "Deprecated. `statelyai open` requires OAuth login." }),
2290
2337
  "editor-url": Flags.string({ description: "Base URL for the Stately editor embed" }),
2291
2338
  host: Flags.string({
2292
2339
  description: "Local server host",
@@ -2309,7 +2356,9 @@ var OpenCommand = class OpenCommand extends Command {
2309
2356
  };
2310
2357
  async run() {
2311
2358
  const { args, flags } = await this.parse(OpenCommand);
2312
- const apiKey = (await resolveApiKey(flags["api-key"])).apiKey;
2359
+ if (flags["api-key"]) throw new Error("`statelyai open` requires OAuth login. Run `statelyai login` and retry.");
2360
+ const apiKey = (await resolveOAuthAccessToken()).apiKey;
2361
+ if (!apiKey) throw new Error("`statelyai open` requires OAuth login. Run `statelyai login` and retry.");
2313
2362
  await openEditor({
2314
2363
  fileName: path.resolve(args.file),
2315
2364
  editorUrl: flags["editor-url"] ?? getDefaultEditorUrl(),
@@ -2399,7 +2448,7 @@ var LoginCommand = class LoginCommand extends Command {
2399
2448
  static flags = {
2400
2449
  help: Flags.help({ char: "h" }),
2401
2450
  "api-key": Flags.string({ description: "API key to store without an interactive prompt" }),
2402
- "base-url": Flags.string({ description: "MCP/API resource URL used for OAuth discovery" }),
2451
+ "base-url": Flags.string({ description: "OAuth protected-resource URL for custom deployments" }),
2403
2452
  stdin: Flags.boolean({
2404
2453
  description: "Read the API key from standard input",
2405
2454
  default: false
@@ -2509,4 +2558,4 @@ function isDirectExecution() {
2509
2558
  if (isDirectExecution()) run$1();
2510
2559
 
2511
2560
  //#endregion
2512
- export { run$1 as _, discoverOAuthLogin as a, formatPlanSummary as c, getEnvCredential as d, inferInitProjectName as f, resolveConfiguredPullTargets as g, resolveApiKey as h, discoverLinkedPullTargets as i, formatStoredCredentialSource as l, isFileGitDirty as m, buildOAuthLoginStart as n, discoverRemoteProjectMachineTargets as o, initProject as p, classifyPushCandidates as r, formatMissingNewMachineDirMessage as s, COMMANDS as t, getEnvApiKey as u, scanProjectSources as v, createStatelyProjectConfig as y };
2561
+ export { resolveOAuthAccessToken as _, discoverOAuthLogin as a, createStatelyProjectConfig as b, formatPlanSummary as c, getEnvCredential as d, inferInitProjectName as f, resolveConfiguredPullTargets as g, resolveApiKey as h, discoverLinkedPullTargets as i, formatStoredCredentialSource as l, isFileGitDirty as m, buildOAuthLoginStart as n, discoverRemoteProjectMachineTargets as o, initProject as p, classifyPushCandidates as r, formatMissingNewMachineDirMessage as s, COMMANDS as t, getEnvApiKey as u, run$1 as v, scanProjectSources as y };
package/dist/index.d.mts CHANGED
@@ -115,11 +115,12 @@ declare function getEnvCredential(): {
115
115
  variable: 'NEXT_PUBLIC_STATELY_API_KEY' | 'STATELY_API_KEY' | 'STATELY_ACCESS_TOKEN';
116
116
  } | undefined;
117
117
  declare function resolveApiKey(explicitApiKey?: string): Promise<ApiKeyResolution>;
118
+ declare function resolveOAuthAccessToken(): Promise<ApiKeyResolution>;
118
119
  interface OAuthLoginDiscovery {
119
120
  authorizationServer: OAuthAuthorizationServerMetadata;
120
121
  authorizationEndpoint: string;
121
122
  clientId: string;
122
- protectedResource: OAuthProtectedResourceMetadata;
123
+ protectedResource?: OAuthProtectedResourceMetadata;
123
124
  resourceUrl: string;
124
125
  scopes: string[];
125
126
  tokenEndpoint: string;
@@ -128,6 +129,7 @@ declare function discoverOAuthLogin(options?: {
128
129
  baseUrl?: string;
129
130
  clientId?: string;
130
131
  fetch?: typeof fetch;
132
+ redirectUri?: string;
131
133
  }): Promise<OAuthLoginDiscovery>;
132
134
  declare function inferInitProjectName(cwd: string, repo?: ConnectedRepo): string;
133
135
  declare function buildOAuthLoginStart(options: {
@@ -315,4 +317,4 @@ declare const COMMANDS: {
315
317
  };
316
318
  declare function run(argv?: any, entryUrl?: string): Promise<void>;
317
319
  //#endregion
318
- export { ApiKeyResolution, COMMANDS, InitProjectOptions, InitProjectResult, LinkedPullTarget, OAuthLoginDiscovery, PushCandidateClassification, RemoteProjectMachineTarget, ResolvedConfiguredPullTargets, ScanProjectSourcesOptions, buildOAuthLoginStart, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, formatStoredCredentialSource, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, run, scanProjectSources };
320
+ export { ApiKeyResolution, COMMANDS, InitProjectOptions, InitProjectResult, LinkedPullTarget, OAuthLoginDiscovery, PushCandidateClassification, RemoteProjectMachineTarget, ResolvedConfiguredPullTargets, ScanProjectSourcesOptions, buildOAuthLoginStart, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, formatStoredCredentialSource, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, resolveOAuthAccessToken, run, scanProjectSources };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { _ as run, a as discoverOAuthLogin, c as formatPlanSummary, d as getEnvCredential, f as inferInitProjectName, g as resolveConfiguredPullTargets, h as resolveApiKey, i as discoverLinkedPullTargets, l as formatStoredCredentialSource, m as isFileGitDirty, n as buildOAuthLoginStart, o as discoverRemoteProjectMachineTargets, p as initProject, r as classifyPushCandidates, s as formatMissingNewMachineDirMessage, t as COMMANDS, u as getEnvApiKey, v as scanProjectSources, y as createStatelyProjectConfig } from "./cli-Buz_BC3I.mjs";
1
+ import { _ as resolveOAuthAccessToken, a as discoverOAuthLogin, b as createStatelyProjectConfig, c as formatPlanSummary, d as getEnvCredential, f as inferInitProjectName, g as resolveConfiguredPullTargets, h as resolveApiKey, i as discoverLinkedPullTargets, l as formatStoredCredentialSource, m as isFileGitDirty, n as buildOAuthLoginStart, o as discoverRemoteProjectMachineTargets, p as initProject, r as classifyPushCandidates, s as formatMissingNewMachineDirMessage, t as COMMANDS, u as getEnvApiKey, v as run, y as scanProjectSources } from "./cli-jiby9qdf.mjs";
2
2
 
3
- export { COMMANDS, buildOAuthLoginStart, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, formatStoredCredentialSource, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, run, scanProjectSources };
3
+ export { COMMANDS, buildOAuthLoginStart, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, formatStoredCredentialSource, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, resolveOAuthAccessToken, run, scanProjectSources };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statelyai",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Command-line tools for Stately",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@oclif/core": "^4.10.3",
24
- "@statelyai/sdk": "0.12.0"
24
+ "@statelyai/sdk": "0.12.1"
25
25
  },
26
26
  "devDependencies": {
27
27
  "tsdown": "0.21.0-beta.2",