statelyai 0.6.0 → 0.7.0
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 +7 -5
- package/dist/bin.mjs +1 -1
- package/dist/{cli-Buz_BC3I.mjs → cli-DR7_Qkzm.mjs} +82 -96
- package/dist/index.d.mts +5 -10
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,18 +13,20 @@ 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 `STATELY_API_KEY` for
|
|
17
|
+
API-key auth.
|
|
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
|
|
24
23
|
```
|
|
25
24
|
|
|
26
25
|
`statelyai login --auth bearer` uses the same browser OAuth flow explicitly.
|
|
27
|
-
`statelyai login --api-key`
|
|
26
|
+
`statelyai login --api-key` prompts securely for an API key, and
|
|
27
|
+
`statelyai login --stdin` reads an API key from standard input. All commands use
|
|
28
|
+
stored OAuth credentials or stored API keys. Free accounts open read-only
|
|
29
|
+
root-level views.
|
|
28
30
|
|
|
29
31
|
2. Initialize the current repo:
|
|
30
32
|
|
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,20 @@ 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);
|
|
152
|
+
if (this.options.apiKey) this.sendMessage({
|
|
153
|
+
type: "@statelyai.setApiKey",
|
|
154
|
+
apiKey: this.options.apiKey
|
|
155
|
+
});
|
|
150
156
|
this.postMessage({
|
|
151
157
|
type: "@statelyai.init",
|
|
152
158
|
machine: update.machine,
|
|
153
159
|
format: update.format,
|
|
154
160
|
sourceLocations: update.sourceLocations,
|
|
155
|
-
mode: "editing",
|
|
161
|
+
mode: update.access?.readOnly ? "viewing" : "editing",
|
|
162
|
+
readOnly: update.access?.readOnly,
|
|
163
|
+
capabilities: update.access?.capabilities,
|
|
156
164
|
theme: "light",
|
|
157
165
|
unsavedIndicator: { enabled: true },
|
|
158
166
|
leftPanels: [],
|
|
@@ -173,6 +181,7 @@ var RemoteEditorSession = class {
|
|
|
173
181
|
const update = await this.parseRootDocument();
|
|
174
182
|
this.currentFormat = update.format;
|
|
175
183
|
this.currentSourceLocations = update.sourceLocations;
|
|
184
|
+
this.editorSyncAccess = update.access ?? this.editorSyncAccess;
|
|
176
185
|
this.updateSourceLocations(update.sourceLocations);
|
|
177
186
|
this.postMessage({
|
|
178
187
|
type: "@statelyai.update",
|
|
@@ -185,6 +194,16 @@ var RemoteEditorSession = class {
|
|
|
185
194
|
}
|
|
186
195
|
}
|
|
187
196
|
async saveFromEditor(msg) {
|
|
197
|
+
if (this.editorSyncAccess?.readOnly) {
|
|
198
|
+
const message = "Your Stately plan allows read-only editor sync.";
|
|
199
|
+
this.postMessage({
|
|
200
|
+
type: "@statelyai.toast",
|
|
201
|
+
message,
|
|
202
|
+
toastType: "error"
|
|
203
|
+
});
|
|
204
|
+
this.showError(message);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
188
207
|
const validationErrors = getValidationErrors(msg.validations);
|
|
189
208
|
if (validationErrors.length > 0) {
|
|
190
209
|
const message = formatValidationErrorMessage(validationErrors);
|
|
@@ -355,10 +374,7 @@ async function openEditor(options) {
|
|
|
355
374
|
return;
|
|
356
375
|
}
|
|
357
376
|
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
358
|
-
response.end(getCliWebviewContent({
|
|
359
|
-
editorUrl: options.editorUrl,
|
|
360
|
-
apiKey: options.apiKey
|
|
361
|
-
}));
|
|
377
|
+
response.end(getCliWebviewContent({ editorUrl: options.editorUrl }));
|
|
362
378
|
});
|
|
363
379
|
server.on("upgrade", (request, socket) => {
|
|
364
380
|
if (request.url !== "/ws") {
|
|
@@ -514,8 +530,6 @@ function watchWorkspace(rootDir, listener) {
|
|
|
514
530
|
}
|
|
515
531
|
function getCliWebviewContent(options) {
|
|
516
532
|
const baseUrl = normalizedBaseUrl(options.editorUrl);
|
|
517
|
-
const url = new URL(`${baseUrl}/embed`);
|
|
518
|
-
if (options.apiKey) url.searchParams.set("api_key", options.apiKey);
|
|
519
533
|
return `<!DOCTYPE html>
|
|
520
534
|
<html lang="en" style="height:100%;margin:0">
|
|
521
535
|
<head>
|
|
@@ -529,7 +543,7 @@ function getCliWebviewContent(options) {
|
|
|
529
543
|
<body>
|
|
530
544
|
<iframe
|
|
531
545
|
id="stately-editor"
|
|
532
|
-
src="${escapeAttribute(
|
|
546
|
+
src="${escapeAttribute(new URL(`${baseUrl}/embed`).toString())}"
|
|
533
547
|
allow="clipboard-read; clipboard-write"
|
|
534
548
|
></iframe>
|
|
535
549
|
<script>
|
|
@@ -703,15 +717,6 @@ function getCredentialsFilePath() {
|
|
|
703
717
|
function shouldUseFileBackendOnly() {
|
|
704
718
|
return process.env.STATELYAI_CREDENTIALS_BACKEND === "file" || process.env.STATELYAI_CONFIG_DIR !== void 0;
|
|
705
719
|
}
|
|
706
|
-
async function readFileCredentials() {
|
|
707
|
-
const storedCredential = await readFileStoredCredential();
|
|
708
|
-
if (storedCredential?.credential?.type !== "api_key") return;
|
|
709
|
-
return {
|
|
710
|
-
apiKey: storedCredential.credential.token,
|
|
711
|
-
backend: storedCredential.backend,
|
|
712
|
-
location: storedCredential.location
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
720
|
function parseStoredCredential(raw, backend, location) {
|
|
716
721
|
try {
|
|
717
722
|
const parsed = JSON.parse(raw);
|
|
@@ -816,15 +821,6 @@ async function deleteFileCredentials() {
|
|
|
816
821
|
return false;
|
|
817
822
|
}
|
|
818
823
|
}
|
|
819
|
-
async function readMacOSKeychain() {
|
|
820
|
-
const storedCredential = await readMacOSStoredCredential();
|
|
821
|
-
if (storedCredential?.credential?.type !== "api_key") return;
|
|
822
|
-
return {
|
|
823
|
-
apiKey: storedCredential.credential.token,
|
|
824
|
-
backend: storedCredential.backend,
|
|
825
|
-
location: storedCredential.location
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
824
|
async function readMacOSStoredCredential() {
|
|
829
825
|
if (process.platform !== "darwin") return;
|
|
830
826
|
try {
|
|
@@ -897,15 +893,6 @@ async function deleteMacOSKeychain() {
|
|
|
897
893
|
return false;
|
|
898
894
|
}
|
|
899
895
|
}
|
|
900
|
-
async function readLinuxSecretTool() {
|
|
901
|
-
const storedCredential = await readLinuxStoredCredential();
|
|
902
|
-
if (storedCredential?.credential?.type !== "api_key") return;
|
|
903
|
-
return {
|
|
904
|
-
apiKey: storedCredential.credential.token,
|
|
905
|
-
backend: storedCredential.backend,
|
|
906
|
-
location: storedCredential.location
|
|
907
|
-
};
|
|
908
|
-
}
|
|
909
896
|
async function readLinuxStoredCredential() {
|
|
910
897
|
if (process.platform !== "linux") return;
|
|
911
898
|
try {
|
|
@@ -976,10 +963,6 @@ async function deleteLinuxSecretTool() {
|
|
|
976
963
|
return false;
|
|
977
964
|
}
|
|
978
965
|
}
|
|
979
|
-
async function getStoredApiKey() {
|
|
980
|
-
if (shouldUseFileBackendOnly()) return readFileCredentials();
|
|
981
|
-
return await readMacOSKeychain() ?? await readLinuxSecretTool() ?? await readFileCredentials();
|
|
982
|
-
}
|
|
983
966
|
async function getStoredCredential() {
|
|
984
967
|
if (shouldUseFileBackendOnly()) return readFileStoredCredential();
|
|
985
968
|
return await readMacOSStoredCredential() ?? await readLinuxStoredCredential() ?? await readFileStoredCredential();
|
|
@@ -1339,23 +1322,18 @@ function getEnvCredential() {
|
|
|
1339
1322
|
variable: "NEXT_PUBLIC_STATELY_API_KEY"
|
|
1340
1323
|
};
|
|
1341
1324
|
}
|
|
1342
|
-
async function resolveApiKey(
|
|
1343
|
-
if (explicitApiKey) return {
|
|
1344
|
-
apiKey: explicitApiKey,
|
|
1345
|
-
source: "flag",
|
|
1346
|
-
detail: "--api-key"
|
|
1347
|
-
};
|
|
1325
|
+
async function resolveApiKey() {
|
|
1348
1326
|
const envApiKey = getEnvApiKey();
|
|
1349
1327
|
if (envApiKey) return {
|
|
1350
1328
|
apiKey: envApiKey.apiKey,
|
|
1351
1329
|
source: "env",
|
|
1352
1330
|
detail: envApiKey.variable
|
|
1353
1331
|
};
|
|
1354
|
-
const
|
|
1355
|
-
if (
|
|
1356
|
-
apiKey:
|
|
1332
|
+
const storedCredential = await getStoredCredential();
|
|
1333
|
+
if (storedCredential?.credential) return {
|
|
1334
|
+
apiKey: storedCredential.credential.type === "oauth" ? storedCredential.credential.accessToken : storedCredential.credential.token,
|
|
1357
1335
|
source: "stored",
|
|
1358
|
-
detail: describeCredentialBackend(
|
|
1336
|
+
detail: describeCredentialBackend(storedCredential.backend, storedCredential.location)
|
|
1359
1337
|
};
|
|
1360
1338
|
return { source: "missing" };
|
|
1361
1339
|
}
|
|
@@ -1368,34 +1346,48 @@ function getDefaultEditorUrl() {
|
|
|
1368
1346
|
function getResolvedStudioUrl(baseUrl) {
|
|
1369
1347
|
return baseUrl ?? getDefaultBaseUrl() ?? "https://stately.ai";
|
|
1370
1348
|
}
|
|
1371
|
-
function
|
|
1372
|
-
return process.env.
|
|
1349
|
+
function getDefaultOAuthIssuer() {
|
|
1350
|
+
return process.env.STATELY_OAUTH_ISSUER ?? process.env.NEXT_PUBLIC_STATELY_OAUTH_ISSUER ?? "https://stately.ai";
|
|
1373
1351
|
}
|
|
1374
1352
|
function normalizeLoginResourceUrl(baseUrl) {
|
|
1375
|
-
const raw = baseUrl
|
|
1353
|
+
const raw = baseUrl;
|
|
1376
1354
|
const url = new URL(raw);
|
|
1377
1355
|
if (url.pathname === "/" || url.pathname === "") url.pathname = "/api/mcp";
|
|
1378
1356
|
return url.toString();
|
|
1379
1357
|
}
|
|
1380
|
-
function
|
|
1381
|
-
return process.env.STATELY_OAUTH_CLIENT_ID ?? process.env.NEXT_PUBLIC_STATELY_OAUTH_CLIENT_ID
|
|
1358
|
+
function getConfiguredOauthClientId() {
|
|
1359
|
+
return process.env.STATELY_OAUTH_CLIENT_ID ?? process.env.NEXT_PUBLIC_STATELY_OAUTH_CLIENT_ID;
|
|
1382
1360
|
}
|
|
1383
1361
|
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}.
|
|
1362
|
+
const resourceUrl = options.baseUrl ? normalizeLoginResourceUrl(options.baseUrl) : getDefaultOAuthIssuer();
|
|
1363
|
+
const protectedResource = options.baseUrl ? await discoverProtectedResource(resourceUrl, { fetch: options.fetch }) : void 0;
|
|
1364
|
+
const issuer = protectedResource ? protectedResource.authorization_servers?.[0] : resourceUrl;
|
|
1365
|
+
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
1366
|
const authorizationServer = await discoverAuthorizationServer(issuer, { fetch: options.fetch });
|
|
1389
1367
|
const authorizationEndpoint = authorizationServer.authorization_endpoint;
|
|
1390
1368
|
const tokenEndpoint = authorizationServer.token_endpoint;
|
|
1391
1369
|
if (!authorizationEndpoint || !tokenEndpoint) throw new Error(`Authorization server ${issuer} did not advertise authorization and token endpoints.`);
|
|
1370
|
+
const configuredClientId = options.clientId ?? getConfiguredOauthClientId();
|
|
1371
|
+
const scopes = protectedResource?.scopes_supported ?? [];
|
|
1372
|
+
let clientId = configuredClientId;
|
|
1373
|
+
if (!clientId && authorizationServer.registration_endpoint && options.redirectUri) try {
|
|
1374
|
+
clientId = (await registerOAuthClient({
|
|
1375
|
+
registrationEndpoint: authorizationServer.registration_endpoint,
|
|
1376
|
+
redirectUri: options.redirectUri,
|
|
1377
|
+
scopes,
|
|
1378
|
+
fetch: options.fetch
|
|
1379
|
+
})).client_id;
|
|
1380
|
+
} catch (error) {
|
|
1381
|
+
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 });
|
|
1382
|
+
}
|
|
1383
|
+
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
1384
|
return {
|
|
1393
1385
|
authorizationServer,
|
|
1394
1386
|
authorizationEndpoint,
|
|
1395
|
-
clientId
|
|
1396
|
-
protectedResource,
|
|
1387
|
+
clientId,
|
|
1388
|
+
...protectedResource ? { protectedResource } : {},
|
|
1397
1389
|
resourceUrl,
|
|
1398
|
-
scopes
|
|
1390
|
+
scopes,
|
|
1399
1391
|
tokenEndpoint
|
|
1400
1392
|
};
|
|
1401
1393
|
}
|
|
@@ -1458,7 +1450,7 @@ async function readApiKeyFromStdin() {
|
|
|
1458
1450
|
return Buffer.concat(chunks).toString("utf8").trim();
|
|
1459
1451
|
}
|
|
1460
1452
|
async function promptForApiKey() {
|
|
1461
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("No interactive terminal available.
|
|
1453
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("No interactive terminal available. Pipe the API key to `statelyai login --stdin`.");
|
|
1462
1454
|
const maskedOutput = new Writable({ write(chunk, _encoding, callback) {
|
|
1463
1455
|
if (!maskedOutput.muted) process.stdout.write(chunk);
|
|
1464
1456
|
callback();
|
|
@@ -1564,7 +1556,8 @@ async function buildOAuthLoginStart(options) {
|
|
|
1564
1556
|
const [discovery, pkce] = await Promise.all([discoverOAuthLogin({
|
|
1565
1557
|
baseUrl: options.baseUrl,
|
|
1566
1558
|
clientId: options.clientId,
|
|
1567
|
-
fetch: options.fetch
|
|
1559
|
+
fetch: options.fetch,
|
|
1560
|
+
redirectUri: options.redirectUri
|
|
1568
1561
|
}), createPkcePair()]);
|
|
1569
1562
|
return {
|
|
1570
1563
|
authorizeUrl: buildAuthorizeUrl({
|
|
@@ -1681,8 +1674,8 @@ function formatMissingNewMachineDirMessage(options) {
|
|
|
1681
1674
|
function toMachineFileStem(machineName) {
|
|
1682
1675
|
return (typeof machineName === "string" ? machineName : "").trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "machine";
|
|
1683
1676
|
}
|
|
1684
|
-
function
|
|
1685
|
-
return `No
|
|
1677
|
+
function getMissingCredentialMessage() {
|
|
1678
|
+
return `No Stately credential configured. Run \`statelyai login\`, set \`STATELY_ACCESS_TOKEN\`, or set \`STATELY_API_KEY\`.\nGet or create an API key at ${STATELY_API_KEY_SETTINGS_URL}`;
|
|
1686
1679
|
}
|
|
1687
1680
|
function buildProjectEditorUrl(studioUrl, projectId) {
|
|
1688
1681
|
return `${studioUrl.replace(/\/+$/, "")}/registry/editor/${encodeURIComponent(projectId)}`;
|
|
@@ -1951,7 +1944,6 @@ const sharedFlags = {
|
|
|
1951
1944
|
default: false,
|
|
1952
1945
|
description: "Exit with a nonzero code when differences are found"
|
|
1953
1946
|
}),
|
|
1954
|
-
"api-key": Flags.string({ description: "Stately API key used for remote source or target resolution" }),
|
|
1955
1947
|
"base-url": Flags.string({ description: "Base URL for Stately Studio or a self-hosted deployment" })
|
|
1956
1948
|
};
|
|
1957
1949
|
function formatChangeList(label, items) {
|
|
@@ -2006,7 +1998,7 @@ var PlanCommand = class PlanCommand extends ParsedSyncCommand {
|
|
|
2006
1998
|
static args = sharedArgs;
|
|
2007
1999
|
async run() {
|
|
2008
2000
|
const { args, flags } = await this.parseSync(PlanCommand);
|
|
2009
|
-
const apiKey = (await resolveApiKey(
|
|
2001
|
+
const apiKey = (await resolveApiKey()).apiKey;
|
|
2010
2002
|
const plan = await planSync({
|
|
2011
2003
|
source: args.source,
|
|
2012
2004
|
target: args.target,
|
|
@@ -2023,7 +2015,7 @@ var DiffCommand = class DiffCommand extends ParsedSyncCommand {
|
|
|
2023
2015
|
static args = sharedArgs;
|
|
2024
2016
|
async run() {
|
|
2025
2017
|
const { args, flags } = await this.parseSync(DiffCommand);
|
|
2026
|
-
const apiKey = (await resolveApiKey(
|
|
2018
|
+
const apiKey = (await resolveApiKey()).apiKey;
|
|
2027
2019
|
const plan = await planSync({
|
|
2028
2020
|
source: args.source,
|
|
2029
2021
|
target: args.target,
|
|
@@ -2057,9 +2049,9 @@ var PullCommand = class PullCommand extends ParsedSyncCommand {
|
|
|
2057
2049
|
};
|
|
2058
2050
|
async run() {
|
|
2059
2051
|
const { args, flags } = await this.parseSync(PullCommand);
|
|
2060
|
-
const apiKey = (await resolveApiKey(
|
|
2052
|
+
const apiKey = (await resolveApiKey()).apiKey;
|
|
2061
2053
|
if (!args.source) {
|
|
2062
|
-
if (!apiKey) this.error(
|
|
2054
|
+
if (!apiKey) this.error(getMissingCredentialMessage());
|
|
2063
2055
|
this.log(colorize("Resolving configured project and linked source files...", ANSI_CYAN));
|
|
2064
2056
|
const { config, linkedTargets, project, remoteOnlyTargets, skipped, rewriteStatus } = await resolveConfiguredPullTargets({
|
|
2065
2057
|
apiKey,
|
|
@@ -2138,7 +2130,7 @@ var PullCommand = class PullCommand extends ParsedSyncCommand {
|
|
|
2138
2130
|
target = localCandidate;
|
|
2139
2131
|
}
|
|
2140
2132
|
if (!target) this.error("Missing target path. Pass `statelyai pull <machine-id|url> <file>` or `statelyai pull <linked-file>`.");
|
|
2141
|
-
if (!apiKey) this.error(
|
|
2133
|
+
if (!apiKey) this.error(getMissingCredentialMessage());
|
|
2142
2134
|
if (!flags.force && await fileExists(target) && await isFileGitDirty(target)) this.error(`Refusing to overwrite locally modified file ${target}. Pass --force to overwrite it.`);
|
|
2143
2135
|
this.log(colorize(`Pulling ${source} into ${target}...`, ANSI_CYAN));
|
|
2144
2136
|
const result = await pullSync({
|
|
@@ -2160,7 +2152,6 @@ var PushCommand = class PushCommand extends Command {
|
|
|
2160
2152
|
}) };
|
|
2161
2153
|
static flags = {
|
|
2162
2154
|
help: Flags.help({ char: "h" }),
|
|
2163
|
-
"api-key": Flags.string({ description: "Stately API key used to create or update remote machines" }),
|
|
2164
2155
|
"base-url": Flags.string({ description: "Base URL for Stately Studio or a self-hosted deployment" }),
|
|
2165
2156
|
config: Flags.string({ description: "Path to statelyai.json" }),
|
|
2166
2157
|
"dry-run": Flags.boolean({
|
|
@@ -2170,8 +2161,8 @@ var PushCommand = class PushCommand extends Command {
|
|
|
2170
2161
|
};
|
|
2171
2162
|
async run() {
|
|
2172
2163
|
const { args, flags } = await this.parse(PushCommand);
|
|
2173
|
-
const resolvedApiKey = flags["dry-run"] ? { source: "missing" } : await resolveApiKey(
|
|
2174
|
-
if (!flags["dry-run"] && !resolvedApiKey.apiKey) this.error(
|
|
2164
|
+
const resolvedApiKey = flags["dry-run"] ? { source: "missing" } : await resolveApiKey();
|
|
2165
|
+
if (!flags["dry-run"] && !resolvedApiKey.apiKey) this.error(getMissingCredentialMessage());
|
|
2175
2166
|
this.log(colorize("Resolving configured project and source files...", ANSI_CYAN));
|
|
2176
2167
|
const { client, config, files, rewriteStatus } = flags["dry-run"] ? await (async () => {
|
|
2177
2168
|
const { config, rootDir, rewriteStatus } = await readStatelyProjectConfig({
|
|
@@ -2286,7 +2277,6 @@ var OpenCommand = class OpenCommand extends Command {
|
|
|
2286
2277
|
}) };
|
|
2287
2278
|
static flags = {
|
|
2288
2279
|
help: Flags.help({ char: "h" }),
|
|
2289
|
-
"api-key": Flags.string({ description: "Stately API key used when the editor server requires auth" }),
|
|
2290
2280
|
"editor-url": Flags.string({ description: "Base URL for the Stately editor embed" }),
|
|
2291
2281
|
host: Flags.string({
|
|
2292
2282
|
description: "Local server host",
|
|
@@ -2309,7 +2299,8 @@ var OpenCommand = class OpenCommand extends Command {
|
|
|
2309
2299
|
};
|
|
2310
2300
|
async run() {
|
|
2311
2301
|
const { args, flags } = await this.parse(OpenCommand);
|
|
2312
|
-
const apiKey = (await resolveApiKey(
|
|
2302
|
+
const apiKey = (await resolveApiKey()).apiKey;
|
|
2303
|
+
if (!apiKey) throw new Error("No Stately credential configured. Run `statelyai login` and retry.");
|
|
2313
2304
|
await openEditor({
|
|
2314
2305
|
fileName: path.resolve(args.file),
|
|
2315
2306
|
editorUrl: flags["editor-url"] ?? getDefaultEditorUrl(),
|
|
@@ -2327,7 +2318,6 @@ var InitCommand = class InitCommand extends Command {
|
|
|
2327
2318
|
static description = "Creates or reuses a remote Studio project for the current working directory and writes a local statelyai.json configuration file.";
|
|
2328
2319
|
static flags = {
|
|
2329
2320
|
help: Flags.help({ char: "h" }),
|
|
2330
|
-
"api-key": Flags.string({ description: "Stately API key used to create the remote project" }),
|
|
2331
2321
|
"base-url": Flags.string({ description: "Base URL for Stately Studio or a self-hosted deployment" }),
|
|
2332
2322
|
name: Flags.string({ description: "Project name to create remotely" }),
|
|
2333
2323
|
visibility: Flags.string({
|
|
@@ -2350,8 +2340,8 @@ var InitCommand = class InitCommand extends Command {
|
|
|
2350
2340
|
};
|
|
2351
2341
|
async run() {
|
|
2352
2342
|
const { flags } = await this.parse(InitCommand);
|
|
2353
|
-
const resolvedApiKey = await resolveApiKey(
|
|
2354
|
-
if (!resolvedApiKey.apiKey) this.error(
|
|
2343
|
+
const resolvedApiKey = await resolveApiKey();
|
|
2344
|
+
if (!resolvedApiKey.apiKey) this.error(getMissingCredentialMessage());
|
|
2355
2345
|
this.log("Creating or reusing remote project...");
|
|
2356
2346
|
const result = await initProject({
|
|
2357
2347
|
apiKey: resolvedApiKey.apiKey,
|
|
@@ -2398,8 +2388,8 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
2398
2388
|
static description = "Stores Stately credentials in the OS credential store when available, with a private file fallback.";
|
|
2399
2389
|
static flags = {
|
|
2400
2390
|
help: Flags.help({ char: "h" }),
|
|
2401
|
-
"api-key": Flags.
|
|
2402
|
-
"base-url": Flags.string({ description: "
|
|
2391
|
+
"api-key": Flags.boolean({ description: "Prompt securely for an API key to store" }),
|
|
2392
|
+
"base-url": Flags.string({ description: "OAuth protected-resource URL for custom deployments" }),
|
|
2403
2393
|
stdin: Flags.boolean({
|
|
2404
2394
|
description: "Read the API key from standard input",
|
|
2405
2395
|
default: false
|
|
@@ -2411,7 +2401,7 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
2411
2401
|
};
|
|
2412
2402
|
async run() {
|
|
2413
2403
|
const { flags } = await this.parse(LoginCommand);
|
|
2414
|
-
if (flags["api-key"] && flags.auth === "bearer") this.error("Pass either --
|
|
2404
|
+
if ((flags["api-key"] || flags.stdin) && flags.auth === "bearer") this.error("Pass either --auth bearer or API-key login flags, not both.");
|
|
2415
2405
|
if (flags.auth === "none") {
|
|
2416
2406
|
if (flags.stdin || flags["api-key"]) this.error("Pass either --auth none or an API key, not both.");
|
|
2417
2407
|
const stored = await setStoredNoAuth();
|
|
@@ -2427,34 +2417,30 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
2427
2417
|
return;
|
|
2428
2418
|
}
|
|
2429
2419
|
if (flags.stdin && flags["api-key"]) this.error("Pass either --api-key or --stdin, not both.");
|
|
2430
|
-
|
|
2431
|
-
const apiKey = normalizeApiKey(flags["api-key"] ?? (!process.stdin.isTTY || flags.stdin ? await readApiKeyFromStdin() : await promptForApiKey()));
|
|
2420
|
+
const apiKey = normalizeApiKey(flags.stdin ? await readApiKeyFromStdin() : await promptForApiKey());
|
|
2432
2421
|
if (!apiKey) this.error(`API key cannot be empty.\nGet or create an API key at ${STATELY_API_KEY_SETTINGS_URL}`);
|
|
2433
|
-
const stored =
|
|
2434
|
-
type: "api_key",
|
|
2435
|
-
token: apiKey
|
|
2436
|
-
});
|
|
2422
|
+
const stored = await setStoredApiKey(apiKey);
|
|
2437
2423
|
this.log(`Stored API key in ${describeCredentialBackend(stored.backend, stored.location)}.`);
|
|
2438
2424
|
}
|
|
2439
2425
|
};
|
|
2440
2426
|
var LogoutCommand = class extends Command {
|
|
2441
2427
|
static enableJsonFlag = false;
|
|
2442
|
-
static summary = "Remove
|
|
2443
|
-
static description = "Deletes
|
|
2428
|
+
static summary = "Remove credentials stored by the CLI.";
|
|
2429
|
+
static description = "Deletes locally stored credentials. Environment variables are not changed.";
|
|
2444
2430
|
static flags = { help: Flags.help({ char: "h" }) };
|
|
2445
2431
|
async run() {
|
|
2446
2432
|
const result = await deleteStoredApiKey();
|
|
2447
2433
|
if (!result.deleted) {
|
|
2448
|
-
this.log("No stored
|
|
2434
|
+
this.log("No stored credential found.");
|
|
2449
2435
|
return;
|
|
2450
2436
|
}
|
|
2451
|
-
this.log(`Removed stored
|
|
2437
|
+
this.log(`Removed stored credential from ${result.locations.join(", ")}.`);
|
|
2452
2438
|
}
|
|
2453
2439
|
};
|
|
2454
2440
|
var WhoamiCommand = class extends Command {
|
|
2455
2441
|
static enableJsonFlag = false;
|
|
2456
|
-
static summary = "Show how the CLI would resolve
|
|
2457
|
-
static description = "Reports whether the CLI would use
|
|
2442
|
+
static summary = "Show how the CLI would resolve credentials.";
|
|
2443
|
+
static description = "Reports whether the CLI would use an environment variable or stored credential.";
|
|
2458
2444
|
static flags = { help: Flags.help({ char: "h" }) };
|
|
2459
2445
|
async run() {
|
|
2460
2446
|
const envCredential = getEnvCredential();
|
|
@@ -2473,7 +2459,7 @@ var WhoamiCommand = class extends Command {
|
|
|
2473
2459
|
this.log(formatStoredCredentialSource(storedCredential));
|
|
2474
2460
|
return;
|
|
2475
2461
|
}
|
|
2476
|
-
this.log(
|
|
2462
|
+
this.log(getMissingCredentialMessage());
|
|
2477
2463
|
}
|
|
2478
2464
|
};
|
|
2479
2465
|
const COMMANDS = {
|
package/dist/index.d.mts
CHANGED
|
@@ -100,7 +100,7 @@ interface ResolvedConfiguredPullTargets {
|
|
|
100
100
|
type ApiKeyResolution = {
|
|
101
101
|
apiKey: string;
|
|
102
102
|
detail: string;
|
|
103
|
-
source: 'env' | '
|
|
103
|
+
source: 'env' | 'stored';
|
|
104
104
|
} | {
|
|
105
105
|
apiKey?: undefined;
|
|
106
106
|
detail?: undefined;
|
|
@@ -114,12 +114,12 @@ declare function getEnvCredential(): {
|
|
|
114
114
|
credential: StatelyCredential;
|
|
115
115
|
variable: 'NEXT_PUBLIC_STATELY_API_KEY' | 'STATELY_API_KEY' | 'STATELY_ACCESS_TOKEN';
|
|
116
116
|
} | undefined;
|
|
117
|
-
declare function resolveApiKey(
|
|
117
|
+
declare function resolveApiKey(): Promise<ApiKeyResolution>;
|
|
118
118
|
interface OAuthLoginDiscovery {
|
|
119
119
|
authorizationServer: OAuthAuthorizationServerMetadata;
|
|
120
120
|
authorizationEndpoint: string;
|
|
121
121
|
clientId: string;
|
|
122
|
-
protectedResource
|
|
122
|
+
protectedResource?: OAuthProtectedResourceMetadata;
|
|
123
123
|
resourceUrl: string;
|
|
124
124
|
scopes: string[];
|
|
125
125
|
tokenEndpoint: string;
|
|
@@ -128,6 +128,7 @@ declare function discoverOAuthLogin(options?: {
|
|
|
128
128
|
baseUrl?: string;
|
|
129
129
|
clientId?: string;
|
|
130
130
|
fetch?: typeof fetch;
|
|
131
|
+
redirectUri?: string;
|
|
131
132
|
}): Promise<OAuthLoginDiscovery>;
|
|
132
133
|
declare function inferInitProjectName(cwd: string, repo?: ConnectedRepo): string;
|
|
133
134
|
declare function buildOAuthLoginStart(options: {
|
|
@@ -171,7 +172,6 @@ declare abstract class BaseSyncCommand extends Command {
|
|
|
171
172
|
static flags: {
|
|
172
173
|
help: _oclif_core_interfaces0.BooleanFlag<void>;
|
|
173
174
|
'fail-on-changes': _oclif_core_interfaces0.BooleanFlag<boolean>;
|
|
174
|
-
'api-key': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
175
175
|
'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
176
176
|
};
|
|
177
177
|
}
|
|
@@ -179,7 +179,6 @@ declare abstract class ParsedSyncCommand extends BaseSyncCommand {
|
|
|
179
179
|
protected parseSync<T extends typeof BaseSyncCommand>(command: T): Promise<_oclif_core_interfaces0.ParserOutput<{
|
|
180
180
|
help: void;
|
|
181
181
|
'fail-on-changes': boolean;
|
|
182
|
-
'api-key': string | undefined;
|
|
183
182
|
'base-url': string | undefined;
|
|
184
183
|
}, {
|
|
185
184
|
[flag: string]: any;
|
|
@@ -213,7 +212,6 @@ declare class PullCommand extends ParsedSyncCommand {
|
|
|
213
212
|
force: _oclif_core_interfaces0.BooleanFlag<boolean>;
|
|
214
213
|
help: _oclif_core_interfaces0.BooleanFlag<void>;
|
|
215
214
|
'fail-on-changes': _oclif_core_interfaces0.BooleanFlag<boolean>;
|
|
216
|
-
'api-key': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
217
215
|
'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
218
216
|
};
|
|
219
217
|
static args: {
|
|
@@ -231,7 +229,6 @@ declare class PushCommand extends Command {
|
|
|
231
229
|
};
|
|
232
230
|
static flags: {
|
|
233
231
|
help: _oclif_core_interfaces0.BooleanFlag<void>;
|
|
234
|
-
'api-key': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
235
232
|
'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
236
233
|
config: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
237
234
|
'dry-run': _oclif_core_interfaces0.BooleanFlag<boolean>;
|
|
@@ -247,7 +244,6 @@ declare class OpenCommand extends Command {
|
|
|
247
244
|
};
|
|
248
245
|
static flags: {
|
|
249
246
|
help: _oclif_core_interfaces0.BooleanFlag<void>;
|
|
250
|
-
'api-key': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
251
247
|
'editor-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
252
248
|
host: _oclif_core_interfaces0.OptionFlag<string, _oclif_core_interfaces0.CustomOptions>;
|
|
253
249
|
port: _oclif_core_interfaces0.OptionFlag<number, _oclif_core_interfaces0.CustomOptions>;
|
|
@@ -262,7 +258,6 @@ declare class InitCommand extends Command {
|
|
|
262
258
|
static description: string;
|
|
263
259
|
static flags: {
|
|
264
260
|
help: _oclif_core_interfaces0.BooleanFlag<void>;
|
|
265
|
-
'api-key': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
266
261
|
'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
267
262
|
name: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
268
263
|
visibility: _oclif_core_interfaces0.OptionFlag<string, _oclif_core_interfaces0.CustomOptions>;
|
|
@@ -277,7 +272,7 @@ declare class LoginCommand extends Command {
|
|
|
277
272
|
static description: string;
|
|
278
273
|
static flags: {
|
|
279
274
|
help: _oclif_core_interfaces0.BooleanFlag<void>;
|
|
280
|
-
'api-key': _oclif_core_interfaces0.
|
|
275
|
+
'api-key': _oclif_core_interfaces0.BooleanFlag<boolean>;
|
|
281
276
|
'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
282
277
|
stdin: _oclif_core_interfaces0.BooleanFlag<boolean>;
|
|
283
278
|
auth: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
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-
|
|
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-DR7_Qkzm.mjs";
|
|
2
2
|
|
|
3
3
|
export { COMMANDS, buildOAuthLoginStart, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatMissingNewMachineDirMessage, formatPlanSummary, formatStoredCredentialSource, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, resolveConfiguredPullTargets, run, scanProjectSources };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "statelyai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
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.2"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"tsdown": "0.21.0-beta.2",
|