@statelyai/sdk 0.6.1 → 0.7.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/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createStatelyClient } from "./studio.mjs";
3
- import "./graphToXStateTS-Gzh0ZqbN.mjs";
4
- import { planSync, pullSync } from "./sync.mjs";
3
+ import { u as getStatelyPragma } from "./graphToXStateTS-Gzh0ZqbN.mjs";
4
+ import { n as pullSync, r as pushLocalMachineLinks, t as planSync } from "./sync-DLkTmSyA.mjs";
5
5
  import fs from "node:fs/promises";
6
6
  import * as path$1 from "node:path";
7
7
  import path from "node:path";
@@ -931,24 +931,27 @@ function describeCredentialBackend(backend, location) {
931
931
  }
932
932
 
933
933
  //#endregion
934
- //#region src/cli.ts
935
- const execFileAsync = promisify(execFile);
934
+ //#region src/projectConfig.ts
936
935
  const STATELY_CONFIG_FILE = "statelyai.json";
937
936
  const STATELY_CONFIG_SCHEMA_URL = "https://stately.ai/schemas/statelyai.json";
938
937
  const STATELY_CONFIG_VERSION = "1.0.0";
939
- function getDefaultSources(defaultXStateVersion) {
940
- return [{
941
- include: ["**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"],
942
- exclude: [
943
- "**/*.test.*",
944
- "**/*.spec.*",
945
- "**/dist/**",
946
- "**/node_modules/**"
947
- ],
948
- format: "xstate",
949
- xstateVersion: defaultXStateVersion
950
- }];
951
- }
938
+ const DEFAULT_SOURCE_EXCLUDES = [
939
+ "**/*.test.*",
940
+ "**/*.spec.*",
941
+ "**/dist/**",
942
+ "**/node_modules/**"
943
+ ];
944
+ const CODE_SOURCE_EXTENSIONS = new Set([
945
+ ".ts",
946
+ ".tsx",
947
+ ".js",
948
+ ".jsx",
949
+ ".mts",
950
+ ".cts",
951
+ ".mjs",
952
+ ".cjs"
953
+ ]);
954
+ const execFileAsync$1 = promisify(execFile);
952
955
  function createStatelyProjectConfig(options) {
953
956
  const defaultXStateVersion = options.defaultXStateVersion ?? 5;
954
957
  return {
@@ -957,9 +960,182 @@ function createStatelyProjectConfig(options) {
957
960
  projectId: options.projectId,
958
961
  studioUrl: options.studioUrl,
959
962
  defaultXStateVersion,
960
- sources: getDefaultSources(defaultXStateVersion)
963
+ sources: []
964
+ };
965
+ }
966
+ function normalizeSourceConfig(source) {
967
+ return {
968
+ include: [...source.include ?? []],
969
+ exclude: source.exclude == null ? [...DEFAULT_SOURCE_EXCLUDES] : [...source.exclude],
970
+ format: source.format ?? "xstate",
971
+ ...source.xstateVersion == null ? {} : { xstateVersion: source.xstateVersion }
972
+ };
973
+ }
974
+ function normalizeProjectConfig(config) {
975
+ return {
976
+ ...config,
977
+ sources: (config.sources ?? []).map((source) => normalizeSourceConfig(source))
978
+ };
979
+ }
980
+ async function readStatelyProjectConfig(options = {}) {
981
+ const rootDir = path.resolve(options.cwd ?? process.cwd());
982
+ const configPath = path.resolve(rootDir, options.configPath ?? STATELY_CONFIG_FILE);
983
+ const raw = await fs.readFile(configPath, "utf8");
984
+ return {
985
+ config: normalizeProjectConfig(JSON.parse(raw)),
986
+ configPath,
987
+ rootDir
988
+ };
989
+ }
990
+ async function walkFiles(rootDir, currentDir = rootDir) {
991
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
992
+ const files = [];
993
+ for (const entry of entries) {
994
+ if (entry.name === ".git") continue;
995
+ const absolutePath = path.join(currentDir, entry.name);
996
+ if (entry.isDirectory()) {
997
+ files.push(...await walkFiles(rootDir, absolutePath));
998
+ continue;
999
+ }
1000
+ if (!entry.isFile()) continue;
1001
+ files.push(path.relative(rootDir, absolutePath).replace(/\\/g, "/"));
1002
+ }
1003
+ return files;
1004
+ }
1005
+ async function filterGitIgnoredPaths(rootDir, relativeFiles) {
1006
+ try {
1007
+ const { stdout } = await execFileAsync$1("git", [
1008
+ "ls-files",
1009
+ "--others",
1010
+ "--ignored",
1011
+ "--exclude-standard",
1012
+ "--directory"
1013
+ ], { cwd: rootDir });
1014
+ const ignoredEntries = stdout.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
1015
+ if (ignoredEntries.length === 0) return relativeFiles;
1016
+ return relativeFiles.filter((relativePath) => {
1017
+ return !ignoredEntries.some((ignoredEntry) => {
1018
+ if (ignoredEntry.endsWith("/")) return relativePath.startsWith(ignoredEntry);
1019
+ return relativePath === ignoredEntry;
1020
+ });
1021
+ });
1022
+ } catch {
1023
+ return relativeFiles;
1024
+ }
1025
+ }
1026
+ function isCodeSourceFile(relativePath) {
1027
+ return CODE_SOURCE_EXTENSIONS.has(path.extname(relativePath).toLowerCase());
1028
+ }
1029
+ function expandBraces(pattern) {
1030
+ const match = pattern.match(/\{([^{}]+)\}/);
1031
+ if (!match || match.index == null) return [pattern];
1032
+ const [token, inner] = match;
1033
+ return inner.split(",").flatMap((variant) => expandBraces(`${pattern.slice(0, match.index)}${variant}${pattern.slice(match.index + token.length)}`));
1034
+ }
1035
+ function globToRegExp(pattern) {
1036
+ let regex = "^";
1037
+ for (let index = 0; index < pattern.length; index += 1) {
1038
+ const char = pattern[index];
1039
+ const next = pattern[index + 1];
1040
+ if (char === "*") {
1041
+ if (next === "*") {
1042
+ const slashAfterGlobstar = pattern[index + 2] === "/";
1043
+ regex += slashAfterGlobstar ? "(?:.*/)?" : ".*";
1044
+ index += slashAfterGlobstar ? 2 : 1;
1045
+ continue;
1046
+ }
1047
+ regex += "[^/]*";
1048
+ continue;
1049
+ }
1050
+ if (char === "?") {
1051
+ regex += "[^/]";
1052
+ continue;
1053
+ }
1054
+ if ("\\.[]{}()+-^$|".includes(char)) {
1055
+ regex += `\\${char}`;
1056
+ continue;
1057
+ }
1058
+ regex += char;
1059
+ }
1060
+ regex += "$";
1061
+ return new RegExp(regex);
1062
+ }
1063
+ function matchesGlob(relativePath, pattern) {
1064
+ return expandBraces(pattern).some((expanded) => globToRegExp(expanded).test(relativePath));
1065
+ }
1066
+ function matchesAny(patterns, relativePath) {
1067
+ return (patterns ?? []).some((pattern) => matchesGlob(relativePath, pattern));
1068
+ }
1069
+ async function discoverCodeSourceFiles(options = {}) {
1070
+ const rootDir = path.resolve(options.cwd ?? process.cwd());
1071
+ return (await filterGitIgnoredPaths(rootDir, await walkFiles(rootDir))).filter((relativePath) => isCodeSourceFile(relativePath)).filter((relativePath) => !matchesAny(DEFAULT_SOURCE_EXCLUDES, relativePath)).sort((left, right) => left.localeCompare(right));
1072
+ }
1073
+ function createSuggestedSource(include) {
1074
+ return {
1075
+ include: [include],
1076
+ exclude: [...DEFAULT_SOURCE_EXCLUDES],
1077
+ format: "xstate",
1078
+ xstateVersion: 5
961
1079
  };
962
1080
  }
1081
+ function suggestStatelySourceConfigs(relativePaths, defaultXStateVersion = 5) {
1082
+ const pending = new Set(relativePaths.map((relativePath) => relativePath.replace(/\\/g, "/")).filter(Boolean));
1083
+ const suggestions = [];
1084
+ const addSuggestion = (include, predicate) => {
1085
+ const matched = [...pending].filter(predicate);
1086
+ if (matched.length === 0) return;
1087
+ suggestions.push({
1088
+ ...createSuggestedSource(include),
1089
+ xstateVersion: defaultXStateVersion
1090
+ });
1091
+ for (const matchedPath of matched) pending.delete(matchedPath);
1092
+ };
1093
+ addSuggestion("**/*.machine.ts", (relativePath) => relativePath.endsWith(".machine.ts"));
1094
+ addSuggestion("src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}", (relativePath) => relativePath.startsWith("src/"));
1095
+ addSuggestion("packages/*/src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}", (relativePath) => /^packages\/[^/]+\/src\//.test(relativePath));
1096
+ addSuggestion("apps/*/src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}", (relativePath) => /^apps\/[^/]+\/src\//.test(relativePath));
1097
+ const byDirectory = /* @__PURE__ */ new Map();
1098
+ for (const relativePath of pending) {
1099
+ const directory = path.posix.dirname(relativePath);
1100
+ const key = directory === "." ? relativePath : directory;
1101
+ const bucket = byDirectory.get(key) ?? [];
1102
+ bucket.push(relativePath);
1103
+ byDirectory.set(key, bucket);
1104
+ }
1105
+ for (const [key, matchedPaths] of [...byDirectory.entries()].sort((left, right) => left[0].localeCompare(right[0]))) {
1106
+ if (matchedPaths.length > 1 && key !== "." && key !== matchedPaths[0]) {
1107
+ addSuggestion(`${key}/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}`, (relativePath) => relativePath.startsWith(`${key}/`));
1108
+ continue;
1109
+ }
1110
+ const exactPath = matchedPaths[0];
1111
+ addSuggestion(exactPath, (relativePath) => relativePath === exactPath);
1112
+ }
1113
+ return suggestions;
1114
+ }
1115
+ async function discoverStatelySourceFiles(options = {}) {
1116
+ const { config, rootDir } = options.config ? {
1117
+ config: options.config,
1118
+ rootDir: path.resolve(options.cwd ?? process.cwd())
1119
+ } : await readStatelyProjectConfig(options);
1120
+ const relativeFiles = await filterGitIgnoredPaths(rootDir, await walkFiles(rootDir));
1121
+ const discovered = /* @__PURE__ */ new Map();
1122
+ for (const source of config.sources) for (const relativePath of relativeFiles) {
1123
+ if (!matchesAny(source.include, relativePath)) continue;
1124
+ if (matchesAny(source.exclude, relativePath)) continue;
1125
+ const filePath = path.join(rootDir, relativePath);
1126
+ if (!discovered.has(filePath)) discovered.set(filePath, {
1127
+ filePath,
1128
+ relativePath,
1129
+ source
1130
+ });
1131
+ }
1132
+ return [...discovered.values()].sort((left, right) => left.relativePath.localeCompare(right.relativePath));
1133
+ }
1134
+
1135
+ //#endregion
1136
+ //#region src/cli.ts
1137
+ const execFileAsync = promisify(execFile);
1138
+ const STATELY_API_KEY_SETTINGS_URL = "https://stately.ai/registry/user/my-settings?tab=API+Key";
963
1139
  function loadLocalEnv() {
964
1140
  if (typeof process.loadEnvFile !== "function") return;
965
1141
  const cwdEnvPath = path.join(process.cwd(), ".env.local");
@@ -1090,10 +1266,32 @@ async function promptForApiKey() {
1090
1266
  rl.close();
1091
1267
  }
1092
1268
  }
1269
+ async function promptYesNo(question, defaultValue = true) {
1270
+ if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("No interactive terminal available.");
1271
+ const rl = createInterface({
1272
+ input: process.stdin,
1273
+ output: process.stdout,
1274
+ terminal: true
1275
+ });
1276
+ try {
1277
+ const suffix = defaultValue ? " [Y/n] " : " [y/N] ";
1278
+ const answer = (await rl.question(`${question}${suffix}`)).trim().toLowerCase();
1279
+ if (!answer) return defaultValue;
1280
+ return answer === "y" || answer === "yes";
1281
+ } finally {
1282
+ rl.close();
1283
+ }
1284
+ }
1093
1285
  function normalizeApiKey(value) {
1094
1286
  const trimmed = value?.trim();
1095
1287
  return trimmed ? trimmed : void 0;
1096
1288
  }
1289
+ function pluralize(count, singular, plural = `${singular}s`) {
1290
+ return count === 1 ? singular : plural;
1291
+ }
1292
+ function getMissingApiKeyMessage() {
1293
+ return `No API key configured. Use \`statelyai login\`, set \`STATELY_API_KEY\`, or pass \`--api-key\`.\nGet or create an API key at ${STATELY_API_KEY_SETTINGS_URL}`;
1294
+ }
1097
1295
  async function fileExists(filePath) {
1098
1296
  try {
1099
1297
  await fs.access(filePath);
@@ -1133,6 +1331,43 @@ async function initProject(options) {
1133
1331
  project
1134
1332
  };
1135
1333
  }
1334
+ async function scanProjectSources(options) {
1335
+ const cwd = path.resolve(options.cwd ?? process.cwd());
1336
+ const candidateRelativePaths = await discoverCodeSourceFiles({ cwd });
1337
+ const machineRelativePaths = [];
1338
+ for (const relativePath of candidateRelativePaths) {
1339
+ const filePath = path.join(cwd, relativePath);
1340
+ if (looksLikeXStateMachineSource(await fs.readFile(filePath, "utf8"))) machineRelativePaths.push(relativePath);
1341
+ }
1342
+ return suggestStatelySourceConfigs(machineRelativePaths, Math.max(5, options.defaultXStateVersion ?? 5));
1343
+ }
1344
+ function looksLikeXStateMachineSource(source) {
1345
+ const hasXStateImport = /from\s+['"]xstate(?:\/[^'"]+)?['"]/.test(source) || /require\(\s*['"]xstate(?:\/[^'"]+)?['"]\s*\)/.test(source);
1346
+ const hasMachineFactory = /\bcreateMachine\s*\(/.test(source) || /\.createMachine\s*\(/.test(source);
1347
+ return hasXStateImport && hasMachineFactory;
1348
+ }
1349
+ function supportsMachineDiscovery(file) {
1350
+ return file.source.format === "xstate" || file.source.format === "auto";
1351
+ }
1352
+ async function resolveConfiguredProject(options) {
1353
+ const { config, configPath, rootDir } = await readStatelyProjectConfig({
1354
+ cwd: options.cwd,
1355
+ configPath: options.configPath
1356
+ });
1357
+ const studioUrl = options.baseUrl ?? config.studioUrl;
1358
+ return {
1359
+ client: options.client ?? createStatelyClient({
1360
+ apiKey: options.apiKey,
1361
+ baseUrl: studioUrl
1362
+ }),
1363
+ config,
1364
+ configPath,
1365
+ files: await discoverStatelySourceFiles({
1366
+ cwd: rootDir,
1367
+ config
1368
+ })
1369
+ };
1370
+ }
1136
1371
  const sharedFlags = {
1137
1372
  help: Flags.help({ char: "h" }),
1138
1373
  "fail-on-changes": Flags.boolean({
@@ -1225,19 +1460,103 @@ var DiffCommand = class DiffCommand extends ParsedSyncCommand {
1225
1460
  var PullCommand = class PullCommand extends ParsedSyncCommand {
1226
1461
  static summary = "Pull a source locator into a local target file.";
1227
1462
  static description = "Resolves the source, materializes it in the target format inferred from the target file, and writes the result locally.";
1228
- static args = sharedArgs;
1463
+ static args = {
1464
+ source: Args.string({
1465
+ required: true,
1466
+ description: "Source locator. Supports a local linked file, machine ID, URL, or local file path."
1467
+ }),
1468
+ target: Args.string({
1469
+ required: false,
1470
+ description: "Optional local target path. Defaults to the source file when pulling a linked local file."
1471
+ })
1472
+ };
1229
1473
  async run() {
1230
1474
  const { args, flags } = await this.parseSync(PullCommand);
1231
1475
  const apiKey = (await resolveApiKey(flags["api-key"])).apiKey;
1476
+ const localCandidate = path.resolve(process.cwd(), args.source);
1477
+ let source = args.source;
1478
+ let target = args.target;
1479
+ if (!target && await fileExists(localCandidate)) {
1480
+ const pragma = getStatelyPragma(await fs.readFile(localCandidate, "utf8"), localCandidate);
1481
+ if (!pragma?.id) this.error(`No @statelyai id found in ${localCandidate}. Pass an explicit source machine ID or URL and target path.`);
1482
+ source = pragma.id;
1483
+ target = localCandidate;
1484
+ }
1485
+ if (!target) this.error("Missing target path. Pass `statelyai pull <machine-id|url> <file>` or `statelyai pull <linked-file>`.");
1486
+ this.log(`Pulling ${source} into ${target}...`);
1232
1487
  const result = await pullSync({
1233
- source: args.source,
1234
- target: args.target,
1488
+ source,
1489
+ target,
1235
1490
  apiKey,
1236
1491
  baseUrl: flags["base-url"] ?? getDefaultBaseUrl()
1237
1492
  });
1238
1493
  this.log(`Pulled: ${result.source.locator} -> ${result.outputPath}\nTarget: ${result.target.kind} (${result.target.format})`);
1239
1494
  }
1240
1495
  };
1496
+ var PushCommand = class PushCommand extends Command {
1497
+ static enableJsonFlag = false;
1498
+ static summary = "Create or update remote machines for local source files.";
1499
+ static description = "Uses statelyai.json source discovery by default, creates remote machines for unlabeled local machine sources, updates already-linked machines, and writes returned @statelyai ids back into source files.";
1500
+ static args = { file: Args.string({
1501
+ required: false,
1502
+ description: "Optional local source file to push instead of scanning statelyai.json."
1503
+ }) };
1504
+ static flags = {
1505
+ help: Flags.help({ char: "h" }),
1506
+ "api-key": Flags.string({ description: "Stately API key used to create or update remote machines" }),
1507
+ "base-url": Flags.string({ description: "Base URL for Stately Studio or a self-hosted deployment" }),
1508
+ config: Flags.string({ description: "Path to statelyai.json" })
1509
+ };
1510
+ async run() {
1511
+ const { args, flags } = await this.parse(PushCommand);
1512
+ const resolvedApiKey = await resolveApiKey(flags["api-key"]);
1513
+ if (!resolvedApiKey.apiKey) this.error(getMissingApiKeyMessage());
1514
+ this.log("Resolving configured project and source files...");
1515
+ const { client, config, files } = await resolveConfiguredProject({
1516
+ apiKey: resolvedApiKey.apiKey,
1517
+ baseUrl: flags["base-url"],
1518
+ configPath: flags.config
1519
+ });
1520
+ const candidateFiles = args.file ? [{
1521
+ filePath: path.resolve(args.file),
1522
+ relativePath: path.relative(process.cwd(), path.resolve(args.file)),
1523
+ source: {
1524
+ include: [args.file],
1525
+ format: "xstate",
1526
+ xstateVersion: config.defaultXStateVersion
1527
+ }
1528
+ }] : files.filter(supportsMachineDiscovery);
1529
+ if (candidateFiles.length === 0) {
1530
+ this.log("No matching local machine source files were discovered.");
1531
+ return;
1532
+ }
1533
+ this.log(`Processing ${candidateFiles.length} ${pluralize(candidateFiles.length, "source file")}...`);
1534
+ const linked = [];
1535
+ const refreshed = [];
1536
+ const skipped = [];
1537
+ for (const file of candidateFiles) {
1538
+ this.log(`Pushing ${file.relativePath}...`);
1539
+ if (!supportsMachineDiscovery(file)) {
1540
+ skipped.push(`${file.relativePath}: unsupported format ${file.source.format}`);
1541
+ continue;
1542
+ }
1543
+ const result = await pushLocalMachineLinks({
1544
+ source: file.filePath,
1545
+ apiKey: resolvedApiKey.apiKey,
1546
+ baseUrl: flags["base-url"] ?? config.studioUrl,
1547
+ client,
1548
+ project: { projectId: config.projectId },
1549
+ xstateVersion: Math.max(5, file.source.xstateVersion ?? config.defaultXStateVersion)
1550
+ });
1551
+ if (result.created.length > 0) linked.push(`${file.relativePath}: ${result.created.map(({ machineIndex, machine }) => `${machine.id} [${machineIndex}]`).join(", ")}`);
1552
+ if (result.updated.length > 0) refreshed.push(`${file.relativePath}: ${result.updated.map(({ machineIndex, machine }) => `${machine.id} [${machineIndex}]`).join(", ")}`);
1553
+ for (const entry of result.skipped) skipped.push(`${file.relativePath} [${entry.machineIndex}]: ${entry.reason}`);
1554
+ }
1555
+ if (linked.length > 0) this.log(`Linked:\n${linked.join("\n")}`);
1556
+ if (refreshed.length > 0) this.log(`Updated:\n${refreshed.join("\n")}`);
1557
+ if (skipped.length > 0) this.log(`Skipped:\n${skipped.join("\n")}`);
1558
+ }
1559
+ };
1241
1560
  var OpenCommand = class OpenCommand extends Command {
1242
1561
  static enableJsonFlag = false;
1243
1562
  static summary = "Open a local file in the Stately visual editor.";
@@ -1304,12 +1623,17 @@ var InitCommand = class InitCommand extends Command {
1304
1623
  force: Flags.boolean({
1305
1624
  description: "Overwrite an existing statelyai.json file",
1306
1625
  default: false
1626
+ }),
1627
+ scan: Flags.boolean({
1628
+ description: "Scan the repo for machine-bearing files and suggest source globs to save into statelyai.json",
1629
+ default: false
1307
1630
  })
1308
1631
  };
1309
1632
  async run() {
1310
1633
  const { flags } = await this.parse(InitCommand);
1311
1634
  const resolvedApiKey = await resolveApiKey(flags["api-key"]);
1312
- if (!resolvedApiKey.apiKey) this.error("No API key configured. Use `statelyai login`, set `STATELY_API_KEY`, or pass `--api-key`.");
1635
+ if (!resolvedApiKey.apiKey) this.error(getMissingApiKeyMessage());
1636
+ this.log("Creating or reusing remote project...");
1313
1637
  const result = await initProject({
1314
1638
  apiKey: resolvedApiKey.apiKey,
1315
1639
  baseUrl: flags["base-url"],
@@ -1319,7 +1643,29 @@ var InitCommand = class InitCommand extends Command {
1319
1643
  visibility: flags.visibility
1320
1644
  }
1321
1645
  });
1322
- this.log(`Initialized project ${result.project.projectId} and wrote ${result.configPath}.`);
1646
+ if (flags.scan) {
1647
+ this.log("Scanning local source files...");
1648
+ const suggestions = await scanProjectSources({
1649
+ cwd: path.dirname(result.configPath),
1650
+ defaultXStateVersion: result.config.defaultXStateVersion
1651
+ });
1652
+ if (suggestions.length === 0) {
1653
+ this.log(`Initialized project ${result.project.projectId} and wrote ${result.configPath}.\nNo machine source globs were suggested. Edit statelyai.json before running statelyai push.`);
1654
+ return;
1655
+ }
1656
+ this.log(`Initialized project ${result.project.projectId} and wrote ${result.configPath}.`);
1657
+ this.log(`Suggested source globs:\n${suggestions.map((source) => `- ${source.include.join(", ")}`).join("\n")}`);
1658
+ if (await promptYesNo("Save these source globs to statelyai.json?", true)) {
1659
+ const nextConfig = {
1660
+ ...result.config,
1661
+ sources: suggestions
1662
+ };
1663
+ await fs.writeFile(result.configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
1664
+ this.log("Saved scanned source globs to statelyai.json.");
1665
+ } else this.log("Left statelyai.json with an empty sources array. Edit it before running statelyai push.");
1666
+ return;
1667
+ }
1668
+ this.log(`Initialized project ${result.project.projectId} and wrote ${result.configPath}.\nNo source globs configured yet. Edit statelyai.json before running statelyai push, or rerun with --scan.`);
1323
1669
  }
1324
1670
  };
1325
1671
  var LoginCommand = class LoginCommand extends Command {
@@ -1337,8 +1683,9 @@ var LoginCommand = class LoginCommand extends Command {
1337
1683
  async run() {
1338
1684
  const { flags } = await this.parse(LoginCommand);
1339
1685
  if (flags.stdin && flags["api-key"]) this.error("Pass either --api-key or --stdin, not both.");
1686
+ if (!flags["api-key"] && !flags.stdin && process.stdin.isTTY && process.stdout.isTTY) this.log(`Get or create an API key at ${STATELY_API_KEY_SETTINGS_URL}`);
1340
1687
  const apiKey = normalizeApiKey(flags["api-key"] ?? (!process.stdin.isTTY || flags.stdin ? await readApiKeyFromStdin() : await promptForApiKey()));
1341
- if (!apiKey) this.error("API key cannot be empty.");
1688
+ if (!apiKey) this.error(`API key cannot be empty.\nGet or create an API key at ${STATELY_API_KEY_SETTINGS_URL}`);
1342
1689
  const stored = await setStoredApiKey(apiKey);
1343
1690
  this.log(`Stored API key in ${describeCredentialBackend(stored.backend, stored.location)}.`);
1344
1691
  }
@@ -1374,13 +1721,14 @@ var AuthStatusCommand = class extends Command {
1374
1721
  this.log(`API key source: stored credential (${describeCredentialBackend(storedApiKey.backend, storedApiKey.location)}).`);
1375
1722
  return;
1376
1723
  }
1377
- this.log("No API key configured. Use `statelyai login`, set `STATELY_API_KEY`, or pass `--api-key`.");
1724
+ this.log(getMissingApiKeyMessage());
1378
1725
  }
1379
1726
  };
1380
1727
  const COMMANDS = {
1381
1728
  plan: PlanCommand,
1382
1729
  diff: DiffCommand,
1383
1730
  pull: PullCommand,
1731
+ push: PushCommand,
1384
1732
  open: OpenCommand,
1385
1733
  init: InitCommand,
1386
1734
  login: LoginCommand,
@@ -1409,4 +1757,4 @@ function isDirectExecution() {
1409
1757
  if (isDirectExecution()) run();
1410
1758
 
1411
1759
  //#endregion
1412
- export { COMMANDS, createStatelyProjectConfig, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, resolveApiKey, run };
1760
+ export { COMMANDS, createStatelyProjectConfig, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, resolveApiKey, run, scanProjectSources };
package/dist/embed.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as EmbedMode, c as ExportFormatMap, d as MachineSourceLocations, f as ProjectEmbedMachine, i as EmbedEventName, l as InitOptions, m as UploadResult, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat, t as CommentsConfig, u as MachineInitOptions } from "./protocol-CDoCcaIP.mjs";
1
+ import { a as EmbedMode, c as ExportFormatMap, d as MachineSourceLocations, f as ProjectEmbedMachine, i as EmbedEventName, l as InitOptions, m as UploadResult, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat, t as CommentsConfig, u as MachineInitOptions } from "./protocol-s9zwsiCW.mjs";
2
2
  import { AssetUploadAdapter } from "./assetStorage.mjs";
3
3
 
4
4
  //#region src/embed.d.ts
package/dist/embed.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as toInitMessage, i as createPendingExportManager, r as createEventRegistry, t as createPostMessageTransport } from "./transport-C0eTgNNu.mjs";
1
+ import { a as toInitMessage, i as createPendingExportManager, r as createEventRegistry, t as createPostMessageTransport } from "./transport-C8UTS3Fa.mjs";
2
2
 
3
3
  //#region src/embed.ts
4
4
  function buildEmbedUrl(options) {
package/dist/graph.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as studioMachineConverter, a as StatelyGraphData, c as StatelyInvoke, d as StudioAction, f as StudioEdge, g as fromStudioMachine, h as StudioNode, i as StatelyGraph, l as StatelyNodeData, m as StudioMachine, n as StatelyActorImplementation, o as StatelyGuard, p as StudioEventTypeData, r as StatelyEdgeData, s as StatelyImplementation, t as StatelyAction, u as StatelyTagImplementation, v as toStudioMachine } from "./graph-DpBGHZwl.mjs";
1
+ import { _ as studioMachineConverter, a as StatelyGraphData, c as StatelyInvoke, d as StudioAction, f as StudioEdge, g as fromStudioMachine, h as StudioNode, i as StatelyGraph, l as StatelyNodeData, m as StudioMachine, n as StatelyActorImplementation, o as StatelyGuard, p as StudioEventTypeData, r as StatelyEdgeData, s as StatelyImplementation, t as StatelyAction, u as StatelyTagImplementation, v as toStudioMachine } from "./graph-zuNj3kfa.mjs";
2
2
  export { StatelyAction, StatelyActorImplementation, StatelyEdgeData, StatelyGraph, StatelyGraphData, StatelyGuard, StatelyImplementation, StatelyInvoke, StatelyNodeData, StatelyTagImplementation, StudioAction, StudioEdge, StudioEventTypeData, StudioMachine, StudioNode, fromStudioMachine, studioMachineConverter, toStudioMachine };
package/dist/index.d.mts CHANGED
@@ -1,14 +1,14 @@
1
1
  import { StatelyApiClientOptions, StatelyApiError, StatelyApiUrlOptions, createStatelyApiClient, createStatelyApiUrl } from "./api.mjs";
2
- import { a as EmbedMode, c as ExportFormatMap, f as ProjectEmbedMachine, i as EmbedEventName, l as InitOptions, m as UploadResult, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat, t as CommentsConfig } from "./protocol-CDoCcaIP.mjs";
2
+ import { a as EmbedMode, c as ExportFormatMap, f as ProjectEmbedMachine, i as EmbedEventName, l as InitOptions, m as UploadResult, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat, t as CommentsConfig } from "./protocol-s9zwsiCW.mjs";
3
3
  import { AssetUploadAdapter, AssetUploadContext, AssetUploadRequest, CreateS3AssetUploadAdapterOptions, CreateSupabaseAssetUploadAdapterOptions, S3UploadTarget, SupabaseStorageClient, createS3AssetUploadAdapter, createSupabaseAssetUploadAdapter } from "./assetStorage.mjs";
4
- import { ConnectedRepo, CreateMachineFromDefinitionInput, CreateMachineFromTemplateInput, CreateMachineInput, CreateMachineTemplate, CreateProjectInput, EnsureProjectInput, ExtractMachinesResponse, ExtractedMachine, GetMachineOptions, ProjectData, ProjectMachine, ProjectVisibility, RepoType, StudioApiError, StudioClient, StudioClientOptions, StudioMachineRecord, VerifyApiKeyResponse, XStateVersion, createStatelyClient } from "./studio.mjs";
5
- import { C as EventTypeData, S as DigraphNodeConfig, _ as studioMachineConverter, a as StatelyGraphData, b as DigraphConfig, c as StatelyInvoke, d as StudioAction, f as StudioEdge, g as fromStudioMachine, h as StudioNode, i as StatelyGraph, l as StatelyNodeData, m as StudioMachine, o as StatelyGuard, r as StatelyEdgeData, t as StatelyAction, v as toStudioMachine, w as StateNodeJSONData, x as DigraphEdgeConfig, y as DigraphAction } from "./graph-DpBGHZwl.mjs";
6
- import { PlanSyncOptions, PullSyncResult, PushSyncOptions, PushSyncProjectOptions, PushSyncResult, ResolvedSyncInput, SyncInputFormat, SyncPlan, SyncPlanSummary } from "./sync.mjs";
4
+ import { ConnectedRepo, CreateMachineFromDefinitionInput, CreateMachineFromTemplateInput, CreateMachineInput, CreateMachineTemplate, CreateProjectInput, EnsureProjectInput, ExtractMachinesResponse, ExtractedMachine, GetMachineOptions, ProjectData, ProjectMachine, ProjectVisibility, RepoType, StudioApiError, StudioClient, StudioClientOptions, StudioMachineRecord, UpdateMachineInput, VerifyApiKeyResponse, XStateVersion, createStatelyClient } from "./studio.mjs";
5
+ import { C as EventTypeData, S as DigraphNodeConfig, _ as studioMachineConverter, a as StatelyGraphData, b as DigraphConfig, c as StatelyInvoke, d as StudioAction, f as StudioEdge, g as fromStudioMachine, h as StudioNode, i as StatelyGraph, l as StatelyNodeData, m as StudioMachine, o as StatelyGuard, r as StatelyEdgeData, t as StatelyAction, v as toStudioMachine, w as StateNodeJSONData, x as DigraphEdgeConfig, y as DigraphAction } from "./graph-zuNj3kfa.mjs";
6
+ import { PlanSyncOptions, PullSyncResult, PushLocalMachineLinksResult, PushSyncOptions, PushSyncProjectOptions, PushSyncResult, ResolvedSyncInput, SyncInputFormat, SyncPlan, SyncPlanSummary } from "./sync.mjs";
7
7
  import { AssetConfig, StatelyEmbed, StatelyEmbedOptions, createStatelyEmbed } from "./embed.mjs";
8
- import { a as ManualActorOptions, c as createPostMessageTransport, i as InspectorEvents, l as createWebSocketTransport, n as CreateInspectorOptions, o as createStatelyInspector, r as Inspector, s as Transport, t as AdoptedActor } from "./inspect-BMIJcsFh.mjs";
8
+ import { a as ManualActorOptions, c as createPostMessageTransport, i as InspectorEvents, l as createWebSocketTransport, n as CreateInspectorOptions, o as createStatelyInspector, r as Inspector, s as Transport, t as AdoptedActor } from "./inspect-YoEwfiKb.mjs";
9
9
  import { ActionLocation, GraphPatch } from "./patchTypes.mjs";
10
- import { JSONSchema7 } from "json-schema";
11
10
  import { UnknownMachineConfig } from "xstate";
11
+ import { JSONSchema7 } from "json-schema";
12
12
 
13
13
  //#region src/statelyPragma.d.ts
14
14
  interface StatelyPragma {
@@ -185,4 +185,4 @@ declare function contextSchemaToTSType(context: Record<string, JSONSchema7> | nu
185
185
  */
186
186
  declare function eventsSchemaToTSType(events: Record<string, JSONSchema7> | null | undefined): string | null;
187
187
  //#endregion
188
- export { type ActionLocation, type AdoptedActor, type AssetConfig, type AssetUploadAdapter, type AssetUploadContext, type AssetUploadRequest, type CodeGenGraph, type CommentsConfig, type ConnectedRepo, type CreateInspectorOptions, type CreateMachineFromDefinitionInput, type CreateMachineFromTemplateInput, type CreateMachineInput, type CreateMachineTemplate, type CreateProjectInput, type CreateS3AssetUploadAdapterOptions, type CreateSupabaseAssetUploadAdapterOptions, type DigraphAction, type DigraphConfig, type DigraphEdgeConfig, type DigraphNodeConfig, type EmbedEventHandler, type EmbedEventMap, type EmbedEventName, type EmbedMode, type EnsureProjectInput, type EventTypeData, type ExportCallOptions, type ExportFormat, type ExportFormatMap, type ExtractMachinesResponse, type ExtractedMachine, type GetMachineOptions, type GraphPatch, type InitOptions, type Inspector, type InspectorEvents, type MachineConfigOptions, type ManualActorOptions, type PlanSyncOptions, type ProjectData, type ProjectEmbedMachine, type ProjectMachine, type ProjectVisibility, type PullSyncResult, type PushSyncOptions, type PushSyncProjectOptions, type PushSyncResult, RawCode, type RepoType, type ResolvedSyncInput, type S3UploadTarget, type StateNodeJSONData, type StatelyAction, type StatelyApiClientOptions, StatelyApiError, type StatelyApiUrlOptions, type StatelyEdgeData, type StatelyEmbed, type StatelyEmbedOptions, type StatelyGraph, type StatelyGraphData, type StatelyGuard, type StatelyInvoke, type StatelyNodeData, type StatelyPragma, type StatelyPragmaAttachment, type StudioAction, StudioApiError, type StudioClient, type StudioClientOptions, type StudioEdge, type StudioMachine, type StudioMachineRecord, type StudioNode, type SupabaseStorageClient, type SyncInputFormat, type SyncPlan, type SyncPlanSummary, type Transport, type UploadResult, type UpsertStatelyPragmaOptions, type VerifyApiKeyResponse, type XStateTSOptions, type XStateVersion, contextSchemaToTSType, createPostMessageTransport, createS3AssetUploadAdapter, createStatelyApiClient, createStatelyApiUrl, createStatelyClient, createStatelyEmbed, createStatelyInspector, createSupabaseAssetUploadAdapter, createWebSocketTransport, eventsSchemaToTSType, findStatelyPragmaAttachments, fromStudioMachine, getStatelyPragma, graphToMachineConfig, graphToXStateTS, jsonSchemaToTSType, raw, serializeJS, studioMachineConverter, toStudioMachine, upsertStatelyPragma };
188
+ export { type ActionLocation, type AdoptedActor, type AssetConfig, type AssetUploadAdapter, type AssetUploadContext, type AssetUploadRequest, type CodeGenGraph, type CommentsConfig, type ConnectedRepo, type CreateInspectorOptions, type CreateMachineFromDefinitionInput, type CreateMachineFromTemplateInput, type CreateMachineInput, type CreateMachineTemplate, type CreateProjectInput, type CreateS3AssetUploadAdapterOptions, type CreateSupabaseAssetUploadAdapterOptions, type DigraphAction, type DigraphConfig, type DigraphEdgeConfig, type DigraphNodeConfig, type EmbedEventHandler, type EmbedEventMap, type EmbedEventName, type EmbedMode, type EnsureProjectInput, type EventTypeData, type ExportCallOptions, type ExportFormat, type ExportFormatMap, type ExtractMachinesResponse, type ExtractedMachine, type GetMachineOptions, type GraphPatch, type InitOptions, type Inspector, type InspectorEvents, type MachineConfigOptions, type ManualActorOptions, type PlanSyncOptions, type ProjectData, type ProjectEmbedMachine, type ProjectMachine, type ProjectVisibility, type PullSyncResult, type PushLocalMachineLinksResult, type PushSyncOptions, type PushSyncProjectOptions, type PushSyncResult, RawCode, type RepoType, type ResolvedSyncInput, type S3UploadTarget, type StateNodeJSONData, type StatelyAction, type StatelyApiClientOptions, StatelyApiError, type StatelyApiUrlOptions, type StatelyEdgeData, type StatelyEmbed, type StatelyEmbedOptions, type StatelyGraph, type StatelyGraphData, type StatelyGuard, type StatelyInvoke, type StatelyNodeData, type StatelyPragma, type StatelyPragmaAttachment, type StudioAction, StudioApiError, type StudioClient, type StudioClientOptions, type StudioEdge, type StudioMachine, type StudioMachineRecord, type StudioNode, type SupabaseStorageClient, type SyncInputFormat, type SyncPlan, type SyncPlanSummary, type Transport, type UpdateMachineInput, type UploadResult, type UpsertStatelyPragmaOptions, type VerifyApiKeyResponse, type XStateTSOptions, type XStateVersion, contextSchemaToTSType, createPostMessageTransport, createS3AssetUploadAdapter, createStatelyApiClient, createStatelyApiUrl, createStatelyClient, createStatelyEmbed, createStatelyInspector, createSupabaseAssetUploadAdapter, createWebSocketTransport, eventsSchemaToTSType, findStatelyPragmaAttachments, fromStudioMachine, getStatelyPragma, graphToMachineConfig, graphToXStateTS, jsonSchemaToTSType, raw, serializeJS, studioMachineConverter, toStudioMachine, upsertStatelyPragma };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as createWebSocketTransport, t as createPostMessageTransport } from "./transport-C0eTgNNu.mjs";
1
+ import { n as createWebSocketTransport, t as createPostMessageTransport } from "./transport-C8UTS3Fa.mjs";
2
2
  import { createStatelyEmbed } from "./embed.mjs";
3
3
  import { createS3AssetUploadAdapter, createSupabaseAssetUploadAdapter } from "./assetStorage.mjs";
4
4
  import { createStatelyInspector } from "./inspect.mjs";
@@ -1,4 +1,4 @@
1
- import { c as ExportFormatMap, o as ExportCallOptions, p as ProtocolMessage, r as EmbedEventMap, s as ExportFormat } from "./protocol-CDoCcaIP.mjs";
1
+ import { c as ExportFormatMap, o as ExportCallOptions, p as ProtocolMessage, r as EmbedEventMap, s as ExportFormat } from "./protocol-s9zwsiCW.mjs";
2
2
 
3
3
  //#region src/transport.d.ts
4
4
  interface Transport {
@@ -1,3 +1,3 @@
1
- import { a as EmbedMode, c as ExportFormatMap, i as EmbedEventName, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat } from "./protocol-CDoCcaIP.mjs";
2
- import { a as ManualActorOptions, i as InspectorEvents, n as CreateInspectorOptions, o as createStatelyInspector, r as Inspector, s as Transport, t as AdoptedActor } from "./inspect-BMIJcsFh.mjs";
1
+ import { a as EmbedMode, c as ExportFormatMap, i as EmbedEventName, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat } from "./protocol-s9zwsiCW.mjs";
2
+ import { a as ManualActorOptions, i as InspectorEvents, n as CreateInspectorOptions, o as createStatelyInspector, r as Inspector, s as Transport, t as AdoptedActor } from "./inspect-YoEwfiKb.mjs";
3
3
  export { AdoptedActor, CreateInspectorOptions, EmbedEventHandler, EmbedEventMap, EmbedEventName, EmbedMode, ExportCallOptions, ExportFormat, ExportFormatMap, Inspector, InspectorEvents, ManualActorOptions, Transport, createStatelyInspector };
package/dist/inspect.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as createPendingExportManager, n as createWebSocketTransport, r as createEventRegistry } from "./transport-C0eTgNNu.mjs";
1
+ import { i as createPendingExportManager, n as createWebSocketTransport, r as createEventRegistry } from "./transport-C8UTS3Fa.mjs";
2
2
 
3
3
  //#region src/inspect.ts
4
4
  const defaultSerializeSnapshot = (snapshot) => ({
@@ -60,9 +60,8 @@ interface ExportFormatMap {
60
60
  };
61
61
  result: string;
62
62
  };
63
- json: {
63
+ 'xstate-json': {
64
64
  options: {
65
- simplifyArrays?: boolean;
66
65
  version?: 4 | 5;
67
66
  };
68
67
  result: Record<string, unknown>;
package/dist/studio.d.mts CHANGED
@@ -75,6 +75,11 @@ interface CreateMachineFromTemplateInput {
75
75
  xstateVersion?: XStateVersion;
76
76
  }
77
77
  type CreateMachineInput<TDefinition = Record<string, unknown>> = CreateMachineFromDefinitionInput<TDefinition> | CreateMachineFromTemplateInput;
78
+ interface UpdateMachineInput<TDefinition = Record<string, unknown>> {
79
+ id: string;
80
+ name?: string;
81
+ definition?: TDefinition;
82
+ }
78
83
  interface StudioMachineRecord<TDefinition = Record<string, unknown>> {
79
84
  id: string;
80
85
  name: string;
@@ -99,6 +104,7 @@ interface StudioClient {
99
104
  machines: {
100
105
  create<TMachine = Record<string, unknown>>(input: CreateMachineInput<TMachine>): Promise<StudioMachineRecord<TMachine>>;
101
106
  createMany<TMachine = Record<string, unknown>>(input: CreateMachineInput<TMachine>): Promise<Array<StudioMachineRecord<TMachine>>>;
107
+ update<TMachine = Record<string, unknown>>(input: UpdateMachineInput<TMachine>): Promise<StudioMachineRecord<TMachine>>;
102
108
  get<TMachine = Record<string, unknown>>(machineId: string, options?: GetMachineOptions): Promise<TMachine>;
103
109
  };
104
110
  code: {
@@ -109,4 +115,4 @@ interface StudioClient {
109
115
  }
110
116
  declare function createStatelyClient(options?: StudioClientOptions): StudioClient;
111
117
  //#endregion
112
- export { ConnectedRepo, CreateMachineFromDefinitionInput, CreateMachineFromTemplateInput, CreateMachineInput, CreateMachineTemplate, CreateProjectInput, EnsureProjectInput, ExtractMachinesResponse, ExtractedMachine, GetMachineOptions, ProjectData, ProjectMachine, ProjectVisibility, RepoType, StudioApiError, StudioClient, StudioClientOptions, StudioMachineRecord, VerifyApiKeyResponse, XStateVersion, createStatelyClient };
118
+ export { ConnectedRepo, CreateMachineFromDefinitionInput, CreateMachineFromTemplateInput, CreateMachineInput, CreateMachineTemplate, CreateProjectInput, EnsureProjectInput, ExtractMachinesResponse, ExtractedMachine, GetMachineOptions, ProjectData, ProjectMachine, ProjectVisibility, RepoType, StudioApiError, StudioClient, StudioClientOptions, StudioMachineRecord, UpdateMachineInput, VerifyApiKeyResponse, XStateVersion, createStatelyClient };