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 +5 -4
- package/dist/bin.mjs +1 -1
- package/dist/{cli-Buz_BC3I.mjs → cli-jiby9qdf.mjs} +68 -19
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +2 -2
- package/package.json +2 -2
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
|
|
17
|
-
`
|
|
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
|
|
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
|
@@ -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
|
|
1372
|
-
return process.env.
|
|
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
|
|
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
|
|
1381
|
-
return process.env.STATELY_OAUTH_CLIENT_ID ?? process.env.NEXT_PUBLIC_STATELY_OAUTH_CLIENT_ID
|
|
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
|
|
1387
|
-
if (!issuer) throw new Error(`No OAuth authorization server advertised by ${resourceUrl}.
|
|
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
|
|
1396
|
-
protectedResource,
|
|
1441
|
+
clientId,
|
|
1442
|
+
...protectedResource ? { protectedResource } : {},
|
|
1397
1443
|
resourceUrl,
|
|
1398
|
-
scopes
|
|
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: "
|
|
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
|
-
|
|
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: "
|
|
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 {
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
24
|
+
"@statelyai/sdk": "0.12.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"tsdown": "0.21.0-beta.2",
|