@mmapp/react-compiler 0.1.0-alpha.4 → 0.1.0-alpha.6

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.
Files changed (103) hide show
  1. package/dist/babel/index.d.mts +2 -2
  2. package/dist/babel/index.d.ts +2 -2
  3. package/dist/babel/index.js +5 -5
  4. package/dist/babel/index.mjs +1 -1
  5. package/dist/cli/index.js +797 -1941
  6. package/dist/cli/index.mjs +179 -77
  7. package/dist/codemod/cli.js +1 -1
  8. package/dist/codemod/cli.mjs +1 -1
  9. package/dist/codemod/index.d.mts +3 -3
  10. package/dist/codemod/index.d.ts +3 -3
  11. package/dist/codemod/index.js +1 -1
  12. package/dist/codemod/index.mjs +1 -1
  13. package/dist/dev-server.d.mts +2 -2
  14. package/dist/dev-server.d.ts +2 -2
  15. package/dist/dev-server.js +86 -1218
  16. package/dist/dev-server.mjs +4 -4
  17. package/dist/envelope.d.mts +2 -2
  18. package/dist/envelope.d.ts +2 -2
  19. package/dist/envelope.js +5 -5
  20. package/dist/envelope.mjs +2 -2
  21. package/dist/index.d.mts +8 -8
  22. package/dist/index.d.ts +8 -8
  23. package/dist/index.js +342 -1266
  24. package/dist/index.mjs +8 -8
  25. package/dist/testing/index.d.mts +8 -8
  26. package/dist/testing/index.d.ts +8 -8
  27. package/dist/testing/index.js +5 -5
  28. package/dist/testing/index.mjs +1 -1
  29. package/dist/vite/index.d.mts +1 -1
  30. package/dist/vite/index.d.ts +1 -1
  31. package/dist/vite/index.js +5 -5
  32. package/dist/vite/index.mjs +2 -2
  33. package/examples/authentication/main.workflow.tsx +1 -1
  34. package/examples/authentication/mm.config.ts +1 -1
  35. package/examples/authentication/pages/LoginPage.tsx +2 -2
  36. package/examples/authentication/pages/SignupPage.tsx +2 -2
  37. package/examples/counter.workflow.tsx +1 -1
  38. package/examples/dashboard.workflow.tsx +1 -1
  39. package/examples/invoice-approval/actions/invoice.server.ts +1 -1
  40. package/examples/invoice-approval/main.workflow.tsx +1 -1
  41. package/examples/invoice-approval/mm.config.ts +1 -1
  42. package/examples/invoice-approval/pages/InvoiceDetailPage.tsx +1 -1
  43. package/examples/invoice-approval/pages/InvoiceFormPage.tsx +1 -1
  44. package/examples/invoice-approval/pages/InvoiceListPage.tsx +1 -1
  45. package/examples/todo-app.workflow.tsx +1 -1
  46. package/examples/uber-app/actions/matching.server.ts +1 -1
  47. package/examples/uber-app/actions/notifications.server.ts +1 -1
  48. package/examples/uber-app/actions/payments.server.ts +1 -1
  49. package/examples/uber-app/actions/pricing.server.ts +1 -1
  50. package/examples/uber-app/app/admin/analytics.tsx +2 -2
  51. package/examples/uber-app/app/admin/fleet.tsx +2 -2
  52. package/examples/uber-app/app/admin/surge-pricing.tsx +2 -2
  53. package/examples/uber-app/app/driver/dashboard.tsx +2 -2
  54. package/examples/uber-app/app/driver/earnings.tsx +2 -2
  55. package/examples/uber-app/app/driver/navigation.tsx +2 -2
  56. package/examples/uber-app/app/driver/ride-acceptance.tsx +2 -2
  57. package/examples/uber-app/app/rider/home.tsx +2 -2
  58. package/examples/uber-app/app/rider/payment-methods.tsx +2 -2
  59. package/examples/uber-app/app/rider/ride-history.tsx +2 -2
  60. package/examples/uber-app/app/rider/ride-tracking.tsx +2 -2
  61. package/examples/uber-app/components/DriverCard.tsx +1 -1
  62. package/examples/uber-app/components/MapView.tsx +3 -3
  63. package/examples/uber-app/components/RatingStars.tsx +2 -2
  64. package/examples/uber-app/components/RideCard.tsx +1 -1
  65. package/examples/uber-app/mm.config.ts +1 -1
  66. package/examples/uber-app/workflows/dispute-resolution.workflow.tsx +2 -2
  67. package/examples/uber-app/workflows/driver-onboarding.workflow.tsx +2 -2
  68. package/examples/uber-app/workflows/payment-processing.workflow.tsx +2 -2
  69. package/examples/uber-app/workflows/ride-request.workflow.tsx +2 -2
  70. package/package.json +13 -9
  71. package/dist/auth-3UK75242.mjs +0 -17
  72. package/dist/chunk-5FTDWKHH.mjs +0 -244
  73. package/dist/chunk-7JRAEFRB.mjs +0 -7510
  74. package/dist/chunk-7T6Q5KAA.mjs +0 -7506
  75. package/dist/chunk-ABYPKRSB.mjs +0 -215
  76. package/dist/chunk-BZEXUPDH.mjs +0 -175
  77. package/dist/chunk-HRYR54PT.mjs +0 -175
  78. package/dist/chunk-J3M4GUS7.mjs +0 -161
  79. package/dist/chunk-JRGFBWTN.mjs +0 -2918
  80. package/dist/chunk-O4AUS7EU.mjs +0 -148
  81. package/dist/chunk-R2DD5GTY.mjs +0 -186
  82. package/dist/chunk-UDDTWG5J.mjs +0 -734
  83. package/dist/chunk-VLTKQDJ3.mjs +0 -244
  84. package/dist/chunk-WVYY32LD.mjs +0 -939
  85. package/dist/chunk-XDVM4YHX.mjs +0 -3450
  86. package/dist/chunk-Z2G5RZ4H.mjs +0 -186
  87. package/dist/chunk-ZE3KCHBM.mjs +0 -2918
  88. package/dist/deploy-YAJGW6II.mjs +0 -9
  89. package/dist/dev-server-CrQ041KP.d.mts +0 -79
  90. package/dist/dev-server-CrQ041KP.d.ts +0 -79
  91. package/dist/envelope-ChEkuHij.d.mts +0 -265
  92. package/dist/envelope-ChEkuHij.d.ts +0 -265
  93. package/dist/index-CEKyyazf.d.mts +0 -104
  94. package/dist/index-CEKyyazf.d.ts +0 -104
  95. package/dist/init-7FJENUDK.mjs +0 -407
  96. package/dist/project-compiler-NNK32MPG.mjs +0 -10
  97. package/dist/project-compiler-ZB4RUYVL.mjs +0 -10
  98. package/dist/project-decompiler-U55HQUHW.mjs +0 -7
  99. package/dist/pull-KOL2QAYQ.mjs +0 -109
  100. package/dist/seed-KOGEPGOJ.mjs +0 -154
  101. package/dist/server-VW6UPCHO.mjs +0 -277
  102. package/dist/verify-BYHUKARQ.mjs +0 -1833
  103. package/dist/verify-OQDEQYMS.mjs +0 -1833
package/dist/cli/index.js CHANGED
@@ -4388,7 +4388,7 @@ function extractImperativeWorkflow(path, state) {
4388
4388
  const source = node.source.value;
4389
4389
  for (const spec of node.specifiers) {
4390
4390
  const localName = spec.local.name;
4391
- if (source.includes("@mmapp/react") || source.includes("@mmapp/react")) {
4391
+ if (source.includes("@mmapp/react") || source.includes("@mindmatrix/react")) {
4392
4392
  stdLibImports.add(localName);
4393
4393
  } else {
4394
4394
  importedFns.set(localName, source);
@@ -7028,7 +7028,7 @@ var import_player_core, PROP_RULES, COMPONENT_RENAMES, RENAME_EXTRA_CONFIG;
7028
7028
  var init_pure_form_emitter = __esm({
7029
7029
  "src/babel/emitters/pure-form-emitter.ts"() {
7030
7030
  "use strict";
7031
- import_player_core = require("@mmapp/player-core");
7031
+ import_player_core = require("@mindmatrix/player-core");
7032
7032
  init_transition_extractor();
7033
7033
  init_experience_transform();
7034
7034
  PROP_RULES = {
@@ -7407,7 +7407,7 @@ function createVisitor(options = {}) {
7407
7407
  if (mode === "strict" && STRICT_BANNED_HOOKS[hookName]) {
7408
7408
  const error = {
7409
7409
  code: STRICT_BANNED_HOOKS[hookName],
7410
- message: `${hookName}() is not allowed in strict mode. Use @mmapp/react effect hooks instead.`,
7410
+ message: `${hookName}() is not allowed in strict mode. Use @mindmatrix/react effect hooks instead.`,
7411
7411
  line: path.node.loc?.start.line,
7412
7412
  column: path.node.loc?.start.column,
7413
7413
  severity: "error"
@@ -7499,12 +7499,12 @@ function createVisitor(options = {}) {
7499
7499
  }
7500
7500
  }
7501
7501
  if (mode !== "strict") return;
7502
- if (source.startsWith("@mmapp/") || source.startsWith("@mmapp/") || source === "react" || source.startsWith("react/") || source.startsWith(".") || source.startsWith("/")) {
7502
+ if (source.startsWith("@mindmatrix/") || source.startsWith("@mmapp/") || source === "react" || source.startsWith("react/") || source.startsWith(".") || source.startsWith("/")) {
7503
7503
  return;
7504
7504
  }
7505
7505
  const error = {
7506
7506
  code: "STRICT_FORBIDDEN_IMPORT",
7507
- message: `Import from '${source}' is not allowed in strict mode. Only @mmapp/* and relative imports are permitted.`,
7507
+ message: `Import from '${source}' is not allowed in strict mode. Only @mindmatrix/* and relative imports are permitted.`,
7508
7508
  line: path.node.loc?.start.line,
7509
7509
  column: path.node.loc?.start.column,
7510
7510
  severity: "error"
@@ -11398,7 +11398,7 @@ function buildJSXTree(node, imports, symbols) {
11398
11398
  }
11399
11399
  const componentName = node.component;
11400
11400
  if (REACT_ATOMS.has(componentName)) {
11401
- addImport(imports, "@mmapp/react", componentName);
11401
+ addImport(imports, "@mindmatrix/react", componentName);
11402
11402
  }
11403
11403
  const attributes = [];
11404
11404
  if (node.config) {
@@ -11443,7 +11443,7 @@ function buildJSXTree(node, imports, symbols) {
11443
11443
  isSelfClosing
11444
11444
  );
11445
11445
  if (node.visible_when) {
11446
- addImport(imports, "@mmapp/react", "Show");
11446
+ addImport(imports, "@mindmatrix/react", "Show");
11447
11447
  return t24.jsxElement(
11448
11448
  t24.jsxOpeningElement(
11449
11449
  t24.jsxIdentifier("Show"),
@@ -11566,7 +11566,7 @@ function fieldTypeAnnotation(field) {
11566
11566
  }
11567
11567
  function emitFieldDeclarations(fields, imports) {
11568
11568
  if (fields.length === 0) return [];
11569
- addImport2(imports, "@mmapp/react", "useState");
11569
+ addImport2(imports, "@mindmatrix/react", "useState");
11570
11570
  const statements = [];
11571
11571
  for (const field of fields) {
11572
11572
  if (field.computed) continue;
@@ -11630,7 +11630,7 @@ function emitStateEffects(states, imports) {
11630
11630
  const statements = [];
11631
11631
  for (const state of states) {
11632
11632
  if (state.on_enter.length > 0) {
11633
- addImport2(imports, "@mmapp/react", "useOnEnter");
11633
+ addImport2(imports, "@mindmatrix/react", "useOnEnter");
11634
11634
  statements.push(
11635
11635
  t25.expressionStatement(
11636
11636
  t25.callExpression(t25.identifier("useOnEnter"), [
@@ -11641,7 +11641,7 @@ function emitStateEffects(states, imports) {
11641
11641
  );
11642
11642
  }
11643
11643
  if (state.on_exit.length > 0) {
11644
- addImport2(imports, "@mmapp/react", "useOnExit");
11644
+ addImport2(imports, "@mindmatrix/react", "useOnExit");
11645
11645
  statements.push(
11646
11646
  t25.expressionStatement(
11647
11647
  t25.callExpression(t25.identifier("useOnExit"), [
@@ -11652,7 +11652,7 @@ function emitStateEffects(states, imports) {
11652
11652
  );
11653
11653
  }
11654
11654
  if (state.during.length > 0) {
11655
- addImport2(imports, "@mmapp/react", "useWhileIn");
11655
+ addImport2(imports, "@mindmatrix/react", "useWhileIn");
11656
11656
  for (const during of state.during) {
11657
11657
  const args2 = [
11658
11658
  t25.stringLiteral(state.name)
@@ -11673,7 +11673,7 @@ function emitStateEffects(states, imports) {
11673
11673
  }
11674
11674
  function emitTransitionDeclarations(transitions, imports) {
11675
11675
  if (transitions.length === 0) return [];
11676
- addImport2(imports, "@mmapp/react", "useTransition");
11676
+ addImport2(imports, "@mindmatrix/react", "useTransition");
11677
11677
  const correctedTransitions = correctFromFields(transitions);
11678
11678
  const statements = [];
11679
11679
  for (const transition of correctedTransitions) {
@@ -11908,7 +11908,7 @@ function emitSingleCondition(cond) {
11908
11908
  function emitRoleDeclarations(metadata, imports) {
11909
11909
  const roleDeps = metadata?.roleDependencies;
11910
11910
  if (!roleDeps || roleDeps.length === 0) return [];
11911
- addImport2(imports, "@mmapp/react", "useRole");
11911
+ addImport2(imports, "@mindmatrix/react", "useRole");
11912
11912
  const statements = [];
11913
11913
  for (const role of roleDeps) {
11914
11914
  const varName = `is${toPascalCase(role)}`;
@@ -11929,7 +11929,7 @@ function emitQueryDeclarations(metadata, imports) {
11929
11929
  const modelImports = metadata?.modelImports;
11930
11930
  const addedModelImports = /* @__PURE__ */ new Set();
11931
11931
  if (dataSources && dataSources.length > 0) {
11932
- addImport2(imports, "@mmapp/react", "useQuery");
11932
+ addImport2(imports, "@mindmatrix/react", "useQuery");
11933
11933
  const usedVarNames = /* @__PURE__ */ new Set();
11934
11934
  for (const ds of dataSources) {
11935
11935
  if (ds.type !== "workflow") continue;
@@ -11996,7 +11996,7 @@ function emitQueryDeclarations(metadata, imports) {
11996
11996
  }
11997
11997
  const mutationTargets = metadata?.mutationTargets;
11998
11998
  if (mutationTargets && mutationTargets.length > 0) {
11999
- addImport2(imports, "@mmapp/react", "useMutation");
11999
+ addImport2(imports, "@mindmatrix/react", "useMutation");
12000
12000
  for (const slug of mutationTargets) {
12001
12001
  const modelPath = modelImports?.[slug];
12002
12002
  const modelVarName2 = modelPath ? `${toCamelCase2(slug.replace(/-/g, "_"))}Model` : void 0;
@@ -12021,7 +12021,7 @@ function emitQueryDeclarations(metadata, imports) {
12021
12021
  function emitChangeWatchers(metadata, imports) {
12022
12022
  const watchers = metadata?.fieldWatchers;
12023
12023
  if (!watchers || watchers.length === 0) return [];
12024
- addImport2(imports, "@mmapp/react", "useOnChange");
12024
+ addImport2(imports, "@mindmatrix/react", "useOnChange");
12025
12025
  const statements = [];
12026
12026
  for (const watcher of watchers) {
12027
12027
  statements.push(
@@ -12037,7 +12037,7 @@ function emitChangeWatchers(metadata, imports) {
12037
12037
  }
12038
12038
  function emitEventSubscriptions(onEvent, imports) {
12039
12039
  if (!onEvent || onEvent.length === 0) return [];
12040
- addImport2(imports, "@mmapp/react", "useOnEvent");
12040
+ addImport2(imports, "@mindmatrix/react", "useOnEvent");
12041
12041
  const statements = [];
12042
12042
  for (const sub of onEvent) {
12043
12043
  const actions = sub.actions.map((a, i) => ({
@@ -12064,7 +12064,7 @@ function emitEventSubscriptions(onEvent, imports) {
12064
12064
  function emitTransitionEffects(metadata, imports) {
12065
12065
  const effects = metadata?.transitionEffects;
12066
12066
  if (!effects || effects.length === 0) return [];
12067
- addImport2(imports, "@mmapp/react", "useOnTransition");
12067
+ addImport2(imports, "@mindmatrix/react", "useOnTransition");
12068
12068
  const statements = [];
12069
12069
  for (const effect of effects) {
12070
12070
  statements.push(
@@ -12095,7 +12095,7 @@ function emitLocalDefaultDeclarations(experience, existingFields, imports) {
12095
12095
  const camelName = toCamelCase2(key);
12096
12096
  if (emittedCamelNames.has(camelName)) continue;
12097
12097
  emittedCamelNames.add(camelName);
12098
- addImport2(imports, "@mmapp/react", "useState");
12098
+ addImport2(imports, "@mindmatrix/react", "useState");
12099
12099
  const setter = `set${toPascalCase(key)}`;
12100
12100
  let defaultExpr;
12101
12101
  if (value === void 0 || value === null) {
@@ -12277,7 +12277,7 @@ function generateMmConfig(config) {
12277
12277
  ` * Auto-generated from workflow definition "${config.slug}".`,
12278
12278
  ` */`,
12279
12279
  ``,
12280
- `import { ${fnName} } from '@mmapp/react';`,
12280
+ `import { ${fnName} } from '@mindmatrix/react';`,
12281
12281
  ``,
12282
12282
  `export default ${fnName}({`,
12283
12283
  ` slug: '${esc(config.slug)}',`,
@@ -12745,7 +12745,7 @@ function generateModelFile(slug, fields, states, transitions, meta) {
12745
12745
  const version = meta?.version || "0.1.0";
12746
12746
  const category = meta?.category || "data";
12747
12747
  const lines = [];
12748
- lines.push(`import { defineModel } from '@mmapp/react';`);
12748
+ lines.push(`import { defineModel } from '@mindmatrix/react';`);
12749
12749
  lines.push(``);
12750
12750
  const sortedFields = [...fields].sort((a, b) => a.name.localeCompare(b.name));
12751
12751
  if (sortedFields.length > 0) {
@@ -12907,7 +12907,7 @@ function generateLayoutFile(slug, roles) {
12907
12907
  ` * ${componentName} \u2014 root layout with auth guards.`,
12908
12908
  ` */`,
12909
12909
  ``,
12910
- `import { useRole, Stack } from '@mmapp/react';`,
12910
+ `import { useRole, Stack } from '@mindmatrix/react';`,
12911
12911
  ``,
12912
12912
  `interface LayoutProps {`,
12913
12913
  ` children: React.ReactNode;`,
@@ -13360,7 +13360,7 @@ function generateServerActionFile(slug, actions) {
13360
13360
  ` * These run server-side during state transitions.`,
13361
13361
  ` */`,
13362
13362
  ``,
13363
- `import type { TransitionContext } from '@mmapp/react';`,
13363
+ `import type { TransitionContext } from '@mindmatrix/react';`,
13364
13364
  ``
13365
13365
  ];
13366
13366
  for (const action of actions) {
@@ -13621,7 +13621,7 @@ function generateServerActionFileFromBodies(filePath, actions) {
13621
13621
  ` * Auto-generated from preserved function bodies.`,
13622
13622
  ` */`,
13623
13623
  ``,
13624
- `import type { TransitionContext, ActionResult } from '@mmapp/react';`,
13624
+ `import type { TransitionContext, ActionResult } from '@mindmatrix/react';`,
13625
13625
  ``
13626
13626
  ];
13627
13627
  for (const action of actions) {
@@ -13917,7 +13917,7 @@ function decompileProjectEnhanced(definition) {
13917
13917
  }
13918
13918
  function generatePackageJson(def) {
13919
13919
  const pkg = {
13920
- name: `@mmapp/blueprint-${def.slug}`,
13920
+ name: `@mindmatrix/blueprint-${def.slug}`,
13921
13921
  version: def.version || "1.0.0",
13922
13922
  description: def.description || "",
13923
13923
  type: "module",
@@ -13929,10 +13929,10 @@ function generatePackageJson(def) {
13929
13929
  },
13930
13930
  peerDependencies: {
13931
13931
  react: ">=18.0.0",
13932
- "@mmapp/react": "workspace:*"
13932
+ "@mindmatrix/react": "workspace:*"
13933
13933
  },
13934
13934
  devDependencies: {
13935
- "@mmapp/react-compiler": "workspace:*",
13935
+ "@mindmatrix/react-compiler": "workspace:*",
13936
13936
  "@types/react": "^19.0.0",
13937
13937
  typescript: "^5.4.0"
13938
13938
  }
@@ -13984,392 +13984,6 @@ var init_project_decompiler = __esm({
13984
13984
  }
13985
13985
  });
13986
13986
 
13987
- // src/cli/deploy.ts
13988
- var deploy_exports = {};
13989
- __export(deploy_exports, {
13990
- deploy: () => deploy,
13991
- syncServices: () => syncServices
13992
- });
13993
- async function deploy(options) {
13994
- const dir = options.dir ?? "dist/workflows";
13995
- const targetLabel = options.targetName ? ` (target: ${options.targetName})` : "";
13996
- console.log(`[mindmatrix-react] Deploying workflows from ${dir} to ${options.apiUrl}${targetLabel}...`);
13997
- const files = await (0, import_glob3.glob)(`${dir}/**/*.workflow.json`);
13998
- if (files.length === 0) {
13999
- console.log(`[mindmatrix-react] No workflow files found in ${dir}`);
14000
- return { created: [], updated: [], versioned: [], skipped: [], failed: [] };
14001
- }
14002
- const result = {
14003
- created: [],
14004
- updated: [],
14005
- versioned: [],
14006
- skipped: [],
14007
- failed: []
14008
- };
14009
- const deployedIRs = [];
14010
- for (const file2 of files) {
14011
- try {
14012
- const ir = JSON.parse((0, import_fs4.readFileSync)(file2, "utf-8"));
14013
- const slug = ir.slug;
14014
- if (options.dryRun) {
14015
- console.log(` [dry-run] Would deploy ${slug} (${(0, import_path3.basename)(file2)})`);
14016
- continue;
14017
- }
14018
- const existing = await fetchExistingDefinition(options.apiUrl, options.token, slug);
14019
- if (!existing) {
14020
- await createDefinition(options.apiUrl, options.token, ir);
14021
- console.log(` \u2713 ${slug} (created)`);
14022
- result.created.push(slug);
14023
- } else if (existing.instanceCount === 0 || options.force) {
14024
- await updateDefinition(options.apiUrl, options.token, existing.id, ir);
14025
- console.log(` \u2713 ${slug} (updated)`);
14026
- result.updated.push(slug);
14027
- } else {
14028
- const newVersion = bumpVersion(existing.version);
14029
- ir.version = newVersion;
14030
- await createDefinition(options.apiUrl, options.token, ir);
14031
- console.log(` \u2713 ${slug} v${newVersion} (new version, ${existing.instanceCount} instances on v${existing.version})`);
14032
- result.versioned.push(slug);
14033
- }
14034
- deployedIRs.push(ir);
14035
- } catch (error) {
14036
- const slug = (0, import_path3.basename)(file2).replace(".workflow.json", "");
14037
- const msg = error.message;
14038
- console.error(` \u2717 ${slug}: ${msg}`);
14039
- result.failed.push({ slug, error: msg });
14040
- }
14041
- }
14042
- if (!options.dryRun) {
14043
- for (const ir of deployedIRs) {
14044
- const serviceResult = await syncServices(options.apiUrl, options.token, ir);
14045
- if (serviceResult) {
14046
- if (!result.services) {
14047
- result.services = { registered: [], updated: [], failed: [] };
14048
- }
14049
- result.services.registered.push(...serviceResult.registered);
14050
- result.services.updated.push(...serviceResult.updated);
14051
- result.services.failed.push(...serviceResult.failed);
14052
- }
14053
- }
14054
- }
14055
- if (options.reportFile) {
14056
- (0, import_fs4.writeFileSync)(options.reportFile, JSON.stringify(result, null, 2), "utf-8");
14057
- console.log(`
14058
- Report written to ${options.reportFile}`);
14059
- }
14060
- const total = result.created.length + result.updated.length + result.versioned.length;
14061
- console.log(
14062
- `
14063
- [mindmatrix-react] Deployed ${total} workflows (${result.created.length} created, ${result.updated.length} updated, ${result.versioned.length} versioned, ${result.failed.length} failed)`
14064
- );
14065
- if (result.services) {
14066
- const svcTotal = result.services.registered.length + result.services.updated.length;
14067
- if (svcTotal > 0 || result.services.failed.length > 0) {
14068
- console.log(
14069
- `[mindmatrix-react] Synced ${svcTotal} services (${result.services.registered.length} registered, ${result.services.updated.length} updated, ${result.services.failed.length} failed)`
14070
- );
14071
- }
14072
- }
14073
- return result;
14074
- }
14075
- async function fetchExistingDefinition(apiUrl, token, slug) {
14076
- try {
14077
- const res = await fetch(`${apiUrl}/workflow/definitions?slug=${encodeURIComponent(slug)}`, {
14078
- headers: { Authorization: `Bearer ${token}` }
14079
- });
14080
- if (!res.ok) return null;
14081
- const data = await res.json();
14082
- const definitions = Array.isArray(data) ? data : data.items ?? data.data;
14083
- if (!definitions || definitions.length === 0) return null;
14084
- const def = definitions[0];
14085
- return {
14086
- id: def.id,
14087
- slug: def.slug,
14088
- version: def.version || "0.1.0",
14089
- instanceCount: def.instanceCount ?? def.instance_count ?? 0
14090
- };
14091
- } catch {
14092
- return null;
14093
- }
14094
- }
14095
- async function createDefinition(apiUrl, token, ir) {
14096
- const res = await fetch(`${apiUrl}/workflow/definitions`, {
14097
- method: "POST",
14098
- headers: {
14099
- "Content-Type": "application/json",
14100
- Authorization: `Bearer ${token}`
14101
- },
14102
- body: JSON.stringify(ir)
14103
- });
14104
- if (!res.ok) {
14105
- const errorText = await res.text();
14106
- throw new Error(`Create failed: ${res.status} ${errorText}`);
14107
- }
14108
- }
14109
- async function updateDefinition(apiUrl, token, id, ir) {
14110
- const { slug: _slug, ...updatePayload } = ir;
14111
- const res = await fetch(`${apiUrl}/workflow/definitions/${id}`, {
14112
- method: "PATCH",
14113
- headers: {
14114
- "Content-Type": "application/json",
14115
- Authorization: `Bearer ${token}`
14116
- },
14117
- body: JSON.stringify(updatePayload)
14118
- });
14119
- if (!res.ok) {
14120
- const errorText = await res.text();
14121
- throw new Error(`Update failed: ${res.status} ${errorText}`);
14122
- }
14123
- }
14124
- function bumpVersion(version) {
14125
- const parts = version.split(".");
14126
- if (parts.length !== 3) return `${version}.1`;
14127
- const patch = parseInt(parts[2], 10) || 0;
14128
- return `${parts[0]}.${parts[1]}.${patch + 1}`;
14129
- }
14130
- async function syncServices(apiUrl, token, ir) {
14131
- const metadata = ir.metadata;
14132
- const orchestration = metadata?.orchestration;
14133
- const services = orchestration?.services;
14134
- if (!services || Object.keys(services).length === 0) return null;
14135
- const result = { registered: [], updated: [], failed: [] };
14136
- for (const [name, config] of Object.entries(services)) {
14137
- try {
14138
- const registration = {
14139
- name,
14140
- connection: {
14141
- type: config.type || "webhook",
14142
- url: config.url,
14143
- queue: config.queue
14144
- },
14145
- actions: config.actions || [],
14146
- labels: config.labels || {}
14147
- };
14148
- const existing = await fetchExistingService(apiUrl, token, name);
14149
- if (existing) {
14150
- await fetch(`${apiUrl}/services/${existing.id}`, {
14151
- method: "PATCH",
14152
- headers: {
14153
- "Content-Type": "application/json",
14154
- Authorization: `Bearer ${token}`
14155
- },
14156
- body: JSON.stringify(registration)
14157
- });
14158
- console.log(` \u2713 service: ${name} (updated)`);
14159
- result.updated.push(name);
14160
- } else {
14161
- const res = await fetch(`${apiUrl}/services`, {
14162
- method: "POST",
14163
- headers: {
14164
- "Content-Type": "application/json",
14165
- Authorization: `Bearer ${token}`
14166
- },
14167
- body: JSON.stringify(registration)
14168
- });
14169
- if (!res.ok) {
14170
- const errorText = await res.text();
14171
- throw new Error(`Register failed: ${res.status} ${errorText}`);
14172
- }
14173
- console.log(` \u2713 service: ${name} (registered)`);
14174
- result.registered.push(name);
14175
- }
14176
- } catch (error) {
14177
- const msg = error.message;
14178
- console.error(` \u2717 service: ${name}: ${msg}`);
14179
- result.failed.push(name);
14180
- }
14181
- }
14182
- return result;
14183
- }
14184
- async function fetchExistingService(apiUrl, token, name) {
14185
- try {
14186
- const res = await fetch(`${apiUrl}/services?name=${encodeURIComponent(name)}`, {
14187
- headers: { Authorization: `Bearer ${token}` }
14188
- });
14189
- if (!res.ok) return null;
14190
- const data = await res.json();
14191
- const servicesList = Array.isArray(data) ? data : data.services ?? data.items ?? data.data;
14192
- if (!servicesList || servicesList.length === 0) return null;
14193
- const found = servicesList.find((s) => s.name === name);
14194
- return found ? { id: found.id, name: found.name } : null;
14195
- } catch {
14196
- return null;
14197
- }
14198
- }
14199
- var import_glob3, import_fs4, import_path3;
14200
- var init_deploy = __esm({
14201
- "src/cli/deploy.ts"() {
14202
- "use strict";
14203
- import_glob3 = require("glob");
14204
- import_fs4 = require("fs");
14205
- import_path3 = require("path");
14206
- }
14207
- });
14208
-
14209
- // src/cli/auth.ts
14210
- var auth_exports = {};
14211
- __export(auth_exports, {
14212
- loadCredentials: () => loadCredentials,
14213
- login: () => login,
14214
- logout: () => logout,
14215
- resolveToken: () => resolveToken,
14216
- saveCredentials: () => saveCredentials,
14217
- whoami: () => whoami
14218
- });
14219
- function loadCredentials() {
14220
- if (!(0, import_fs5.existsSync)(CREDENTIALS_PATH)) return {};
14221
- try {
14222
- return JSON.parse((0, import_fs5.readFileSync)(CREDENTIALS_PATH, "utf-8"));
14223
- } catch {
14224
- return {};
14225
- }
14226
- }
14227
- function saveCredentials(apiUrl, token) {
14228
- (0, import_fs5.mkdirSync)(MMRC_DIR, { recursive: true, mode: 448 });
14229
- const creds = loadCredentials();
14230
- creds[apiUrl] = { token, savedAt: (/* @__PURE__ */ new Date()).toISOString() };
14231
- (0, import_fs5.writeFileSync)(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), "utf-8");
14232
- try {
14233
- const { chmodSync } = require("fs");
14234
- chmodSync(CREDENTIALS_PATH, 384);
14235
- } catch {
14236
- }
14237
- }
14238
- function removeCredentials(apiUrl) {
14239
- if (!(0, import_fs5.existsSync)(CREDENTIALS_PATH)) return;
14240
- if (!apiUrl) {
14241
- (0, import_fs5.unlinkSync)(CREDENTIALS_PATH);
14242
- return;
14243
- }
14244
- const creds = loadCredentials();
14245
- delete creds[apiUrl];
14246
- if (Object.keys(creds).length === 0) {
14247
- (0, import_fs5.unlinkSync)(CREDENTIALS_PATH);
14248
- } else {
14249
- (0, import_fs5.writeFileSync)(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), "utf-8");
14250
- }
14251
- }
14252
- function resolveToken(apiUrl, explicitToken) {
14253
- if (explicitToken) return explicitToken;
14254
- const envToken = process.env.MMRC_TOKEN;
14255
- if (envToken) return envToken;
14256
- const creds = loadCredentials();
14257
- const entry = creds[apiUrl];
14258
- if (entry?.token) return entry.token;
14259
- return null;
14260
- }
14261
- function prompt(question, hidden = false) {
14262
- return new Promise((resolve6) => {
14263
- const rl = (0, import_readline.createInterface)({
14264
- input: process.stdin,
14265
- output: process.stdout
14266
- });
14267
- if (hidden && process.stdin.isTTY) {
14268
- const origWrite = process.stdout.write.bind(process.stdout);
14269
- process.stdout.write = ((chunk, encodingOrCb, cb) => {
14270
- if (typeof chunk === "string" && chunk.includes(question)) {
14271
- return origWrite(chunk, encodingOrCb, cb);
14272
- }
14273
- if (typeof chunk === "string" && !chunk.includes("\n")) {
14274
- return true;
14275
- }
14276
- return origWrite(chunk, encodingOrCb, cb);
14277
- });
14278
- rl.question(question, (answer) => {
14279
- process.stdout.write = origWrite;
14280
- console.log();
14281
- rl.close();
14282
- resolve6(answer.trim());
14283
- });
14284
- } else {
14285
- rl.question(question, (answer) => {
14286
- rl.close();
14287
- resolve6(answer.trim());
14288
- });
14289
- }
14290
- });
14291
- }
14292
- async function login(apiUrl, email, password) {
14293
- if (!email) {
14294
- email = await prompt("Email: ");
14295
- }
14296
- if (!password) {
14297
- password = await prompt("Password: ", true);
14298
- }
14299
- if (!email || !password) {
14300
- throw new Error("Email and password are required");
14301
- }
14302
- console.log(`[mmrc] Logging in to ${apiUrl}...`);
14303
- const res = await fetch(`${apiUrl}/auth/login`, {
14304
- method: "POST",
14305
- headers: { "Content-Type": "application/json" },
14306
- body: JSON.stringify({ email, password })
14307
- });
14308
- if (!res.ok) {
14309
- const text = await res.text();
14310
- throw new Error(`Login failed: ${res.status} ${text}`);
14311
- }
14312
- const data = await res.json();
14313
- const token = data.token ?? data.access_token ?? data.accessToken;
14314
- if (!token) {
14315
- throw new Error("Login succeeded but no token in response");
14316
- }
14317
- saveCredentials(apiUrl, token);
14318
- const userName = data.name ?? data.user?.name ?? data.user?.email ?? email;
14319
- console.log(`[mmrc] Logged in as ${userName}`);
14320
- console.log(`[mmrc] Token saved to ${CREDENTIALS_PATH}`);
14321
- return token;
14322
- }
14323
- function logout(apiUrl) {
14324
- if (apiUrl) {
14325
- removeCredentials(apiUrl);
14326
- console.log(`[mmrc] Credentials removed for ${apiUrl}`);
14327
- } else {
14328
- removeCredentials();
14329
- console.log("[mmrc] All saved credentials removed");
14330
- }
14331
- }
14332
- async function whoami(apiUrl) {
14333
- const token = resolveToken(apiUrl);
14334
- if (!token) {
14335
- console.log("[mmrc] Not logged in. Run `mmrc login` to authenticate.");
14336
- return;
14337
- }
14338
- try {
14339
- const res = await fetch(`${apiUrl}/auth/me`, {
14340
- headers: { Authorization: `Bearer ${token}` }
14341
- });
14342
- if (!res.ok) {
14343
- if (res.status === 401) {
14344
- console.log("[mmrc] Token is expired or invalid. Run `mmrc login` to re-authenticate.");
14345
- return;
14346
- }
14347
- console.log(`[mmrc] Failed to fetch user info: ${res.status}`);
14348
- return;
14349
- }
14350
- const user = await res.json();
14351
- console.log(`[mmrc] Logged in to ${apiUrl}`);
14352
- console.log(` Name: ${user.name ?? "(unknown)"}`);
14353
- console.log(` Email: ${user.email ?? "(unknown)"}`);
14354
- if (user.id) console.log(` ID: ${user.id}`);
14355
- if (user.role) console.log(` Role: ${user.role}`);
14356
- } catch (error) {
14357
- console.log(`[mmrc] Could not connect to ${apiUrl}: ${error.message}`);
14358
- }
14359
- }
14360
- var import_fs5, import_path4, import_os, import_readline, MMRC_DIR, CREDENTIALS_PATH;
14361
- var init_auth = __esm({
14362
- "src/cli/auth.ts"() {
14363
- "use strict";
14364
- import_fs5 = require("fs");
14365
- import_path4 = require("path");
14366
- import_os = require("os");
14367
- import_readline = require("readline");
14368
- MMRC_DIR = (0, import_path4.join)((0, import_os.homedir)(), ".mmrc");
14369
- CREDENTIALS_PATH = (0, import_path4.join)(MMRC_DIR, "credentials.json");
14370
- }
14371
- });
14372
-
14373
13987
  // src/vite/index.ts
14374
13988
  function mindmatrixReact(options) {
14375
13989
  const include = options?.include ?? ["**/*.workflow.tsx"];
@@ -14539,735 +14153,17 @@ var init_vite = __esm({
14539
14153
  "src/vite/index.ts"() {
14540
14154
  "use strict";
14541
14155
  import_core5 = require("@babel/core");
14542
- import_fs6 = require("fs");
14543
- import_path5 = require("path");
14544
- init_babel();
14545
- }
14546
- });
14547
-
14548
- // src/cli/local-server.ts
14549
- async function startLocalServer(options = {}) {
14550
- const { port = 4200, noAuth = true } = options;
14551
- const store = new MemoryStore();
14552
- const startedAt = (/* @__PURE__ */ new Date()).toISOString();
14553
- function json(res, status, body) {
14554
- const data = JSON.stringify(body);
14555
- res.writeHead(status, {
14556
- "Content-Type": "application/json",
14557
- "Access-Control-Allow-Origin": "*",
14558
- "Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
14559
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
14560
- "Content-Length": Buffer.byteLength(data)
14561
- });
14562
- res.end(data);
14563
- }
14564
- function readBody(req) {
14565
- return new Promise((resolve6, reject) => {
14566
- const chunks = [];
14567
- req.on("data", (chunk) => chunks.push(chunk));
14568
- req.on("end", () => resolve6(Buffer.concat(chunks).toString()));
14569
- req.on("error", reject);
14570
- });
14571
- }
14572
- function parseQuery(url) {
14573
- const idx = url.indexOf("?");
14574
- if (idx === -1) return {};
14575
- const params = {};
14576
- const qs = url.slice(idx + 1);
14577
- for (const pair of qs.split("&")) {
14578
- const [k, v] = pair.split("=");
14579
- if (k) params[decodeURIComponent(k)] = decodeURIComponent(v ?? "");
14580
- }
14581
- return params;
14582
- }
14583
- const server = http.createServer(async (req, res) => {
14584
- const method = req.method?.toUpperCase() ?? "GET";
14585
- const rawUrl = req.url ?? "/";
14586
- const queryStart = rawUrl.indexOf("?");
14587
- const path = queryStart >= 0 ? rawUrl.slice(0, queryStart) : rawUrl;
14588
- const query = parseQuery(rawUrl);
14589
- if (method === "OPTIONS") {
14590
- res.writeHead(204, {
14591
- "Access-Control-Allow-Origin": "*",
14592
- "Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
14593
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
14594
- "Access-Control-Max-Age": "86400"
14595
- });
14596
- res.end();
14597
- return;
14598
- }
14599
- try {
14600
- if (path === "/health" && method === "GET") {
14601
- return json(res, 200, {
14602
- status: "ok",
14603
- service: "mm-local-dev",
14604
- mode: "in-memory",
14605
- started_at: startedAt,
14606
- definitions: store.definitions.size,
14607
- instances: store.instances.size
14608
- });
14609
- }
14610
- if (path === "/api/v1/auth/login" && (method === "POST" || method === "GET")) {
14611
- return json(res, 200, {
14612
- token: "dev-token-local",
14613
- user: {
14614
- id: "dev-user-001",
14615
- email: "dev@localhost",
14616
- role: "admin",
14617
- name: "Local Developer"
14618
- }
14619
- });
14620
- }
14621
- if (path === "/api/v1/workflow/definitions" && method === "GET") {
14622
- const result = store.listDefinitions({
14623
- category: query.category,
14624
- limit: query.limit ? parseInt(query.limit, 10) : void 0,
14625
- offset: query.offset ? parseInt(query.offset, 10) : void 0
14626
- });
14627
- if (query.slug) {
14628
- const def = store.getDefinition(query.slug);
14629
- return json(res, 200, { items: def ? [def] : [], total: def ? 1 : 0 });
14630
- }
14631
- return json(res, 200, result);
14632
- }
14633
- const defMatch = path.match(/^\/api\/v1\/workflow\/definitions\/([^/]+)$/);
14634
- if (defMatch && method === "GET") {
14635
- const def = store.getDefinition(defMatch[1]);
14636
- if (!def) return json(res, 404, { error: "Not found" });
14637
- return json(res, 200, def);
14638
- }
14639
- if (path === "/api/v1/workflow/definitions" && method === "POST") {
14640
- const body = JSON.parse(await readBody(req));
14641
- const def = store.createDefinition(body);
14642
- return json(res, 201, def);
14643
- }
14644
- if (defMatch && method === "PATCH") {
14645
- const body = JSON.parse(await readBody(req));
14646
- const updated = store.patchDefinition(defMatch[1], body);
14647
- if (!updated) return json(res, 404, { error: "Not found" });
14648
- return json(res, 200, updated);
14649
- }
14650
- if (defMatch && method === "DELETE") {
14651
- const deleted = store.deleteDefinition(defMatch[1]);
14652
- if (!deleted) return json(res, 404, { error: "Not found" });
14653
- return json(res, 204, null);
14654
- }
14655
- if (path === "/api/v1/workflow/instances" && method === "GET") {
14656
- const result = store.listInstances({
14657
- definition_id: query.definition_id,
14658
- limit: query.limit ? parseInt(query.limit, 10) : void 0,
14659
- offset: query.offset ? parseInt(query.offset, 10) : void 0
14660
- });
14661
- return json(res, 200, result);
14662
- }
14663
- if (path === "/api/v1/workflow/instances" && method === "POST") {
14664
- const body = JSON.parse(await readBody(req));
14665
- const inst = store.createInstance(body);
14666
- if (!inst) return json(res, 404, { error: "Definition not found" });
14667
- return json(res, 201, inst);
14668
- }
14669
- const instMatch = path.match(/^\/api\/v1\/workflow\/instances\/([^/]+)$/);
14670
- if (instMatch && method === "GET") {
14671
- const inst = store.getInstance(instMatch[1]);
14672
- if (!inst) return json(res, 404, { error: "Not found" });
14673
- return json(res, 200, inst);
14674
- }
14675
- if (path === "/api/v1/workflow/execute-action" && method === "POST") {
14676
- const body = JSON.parse(await readBody(req));
14677
- const result = store.executeAction(body);
14678
- return json(res, 200, result);
14679
- }
14680
- const dataMatch = path.match(/^\/api\/v1\/data\/([^/]+)$/);
14681
- if (dataMatch && method === "GET") {
14682
- const def = store.getDefinition(dataMatch[1]);
14683
- if (!def) return json(res, 404, { error: "Not found" });
14684
- const instances = store.listInstances({ definition_id: def.id });
14685
- return json(res, 200, instances);
14686
- }
14687
- if (path.startsWith("/api/v1/")) {
14688
- return json(res, 501, { error: "Not implemented in local dev mode", path, method });
14689
- }
14690
- return json(res, 404, { error: "Not found", path });
14691
- } catch (err) {
14692
- const message = err instanceof Error ? err.message : String(err);
14693
- console.error(`[mm-local] ${method} ${path} \u2014 Error: ${message}`);
14694
- return json(res, 500, { error: message });
14695
- }
14696
- });
14697
- return new Promise((resolve6, reject) => {
14698
- server.on("error", (err) => {
14699
- if (err.code === "EADDRINUSE") {
14700
- reject(new Error(`Port ${port} is already in use. Is another server running?`));
14701
- } else {
14702
- reject(err);
14703
- }
14704
- });
14705
- server.listen(port, () => {
14706
- console.log(`[mm-local] Local API server running at http://localhost:${port}`);
14707
- console.log(`[mm-local] Mode: in-memory (data lost on restart)`);
14708
- console.log(`[mm-local] Auth: disabled (all requests accepted)`);
14709
- resolve6({
14710
- server,
14711
- port,
14712
- store,
14713
- async close() {
14714
- return new Promise((res) => {
14715
- server.close(() => {
14716
- console.log("[mm-local] Local API server stopped");
14717
- res();
14718
- });
14719
- });
14720
- }
14721
- });
14722
- });
14723
- });
14724
- }
14725
- var http, import_node_crypto, MemoryStore;
14726
- var init_local_server = __esm({
14727
- "src/cli/local-server.ts"() {
14728
- "use strict";
14729
- http = __toESM(require("http"));
14730
- import_node_crypto = require("crypto");
14731
- MemoryStore = class {
14732
- constructor() {
14733
- this.definitions = /* @__PURE__ */ new Map();
14734
- this.instances = /* @__PURE__ */ new Map();
14735
- this.slugIndex = /* @__PURE__ */ new Map();
14736
- }
14737
- // slug → id
14738
- // ── Definitions ──────────────────────────────────────────────────────
14739
- createDefinition(input) {
14740
- if (this.slugIndex.has(input.slug)) {
14741
- const existing = this.definitions.get(this.slugIndex.get(input.slug));
14742
- if (existing) return existing;
14743
- }
14744
- const now = (/* @__PURE__ */ new Date()).toISOString();
14745
- const def = {
14746
- id: input.id ?? (0, import_node_crypto.randomUUID)(),
14747
- slug: input.slug,
14748
- name: input.name,
14749
- version: input.version ?? "1.0.0",
14750
- description: input.description ?? null,
14751
- category: input.category ?? "workflow",
14752
- fields: input.fields ?? [],
14753
- states: input.states ?? [],
14754
- transitions: input.transitions ?? [],
14755
- roles: input.roles ?? [],
14756
- experience: input.experience ?? null,
14757
- metadata: input.metadata ?? {},
14758
- child_definitions: input.child_definitions ?? [],
14759
- is_immutable: input.is_immutable ?? false,
14760
- tags: input.tags ?? [],
14761
- inline_tags: input.inline_tags ?? [],
14762
- created_at: now,
14763
- updated_at: now
14764
- };
14765
- this.definitions.set(def.id, def);
14766
- this.slugIndex.set(def.slug, def.id);
14767
- return def;
14768
- }
14769
- getDefinition(idOrSlug) {
14770
- const byId = this.definitions.get(idOrSlug);
14771
- if (byId) return byId;
14772
- const id = this.slugIndex.get(idOrSlug);
14773
- if (id) return this.definitions.get(id);
14774
- return void 0;
14775
- }
14776
- listDefinitions(opts) {
14777
- let items = Array.from(this.definitions.values());
14778
- if (opts?.category) {
14779
- items = items.filter((d) => d.category === opts.category);
14780
- }
14781
- const total = items.length;
14782
- const offset = opts?.offset ?? 0;
14783
- const limit = opts?.limit ?? 50;
14784
- items = items.slice(offset, offset + limit);
14785
- return { items, total };
14786
- }
14787
- patchDefinition(id, patch) {
14788
- const def = this.definitions.get(id);
14789
- if (!def) return void 0;
14790
- Object.assign(def, patch, { updated_at: (/* @__PURE__ */ new Date()).toISOString() });
14791
- return def;
14792
- }
14793
- deleteDefinition(id) {
14794
- const def = this.definitions.get(id);
14795
- if (!def) return false;
14796
- this.slugIndex.delete(def.slug);
14797
- this.definitions.delete(id);
14798
- return true;
14799
- }
14800
- // ── Instances ────────────────────────────────────────────────────────
14801
- createInstance(input) {
14802
- const def = this.getDefinition(input.definition_id) ?? this.getDefinition(input.definition_slug);
14803
- if (!def) return null;
14804
- const initialState = def.states.find((s) => s.type === "START" || s.type === "initial");
14805
- const stateName = initialState?.name ?? "initial";
14806
- const stateData = {};
14807
- for (const field of def.fields) {
14808
- if (field.default_value !== void 0) {
14809
- stateData[field.name] = field.default_value;
14810
- }
14811
- }
14812
- Object.assign(stateData, input.state_data ?? {});
14813
- const now = (/* @__PURE__ */ new Date()).toISOString();
14814
- const inst = {
14815
- id: (0, import_node_crypto.randomUUID)(),
14816
- definition_id: def.id,
14817
- definition_slug: def.slug,
14818
- current_state: stateName,
14819
- state_data: stateData,
14820
- execution_lock_version: 0,
14821
- event_log: [{
14822
- event_type: "transition",
14823
- message: `Instance created in state '${stateName}'`,
14824
- timestamp: now
14825
- }],
14826
- created_at: now,
14827
- updated_at: now
14828
- };
14829
- this.instances.set(inst.id, inst);
14830
- return inst;
14831
- }
14832
- getInstance(id) {
14833
- return this.instances.get(id);
14834
- }
14835
- listInstances(opts) {
14836
- let items = Array.from(this.instances.values());
14837
- if (opts?.definition_id) {
14838
- items = items.filter((i) => i.definition_id === opts.definition_id);
14839
- }
14840
- const total = items.length;
14841
- const offset = opts?.offset ?? 0;
14842
- const limit = opts?.limit ?? 50;
14843
- items = items.slice(offset, offset + limit);
14844
- return { items, total };
14845
- }
14846
- // ── Execute Action (Transition) ──────────────────────────────────────
14847
- executeAction(input) {
14848
- const def = this.getDefinition(input.definition_id);
14849
- if (!def) return { success: false, error: "Definition not found" };
14850
- let inst;
14851
- if (input.instance_id) {
14852
- const existing = this.instances.get(input.instance_id);
14853
- if (!existing) return { success: false, error: "Instance not found" };
14854
- inst = existing;
14855
- } else {
14856
- const created = this.createInstance({
14857
- definition_id: def.id,
14858
- definition_slug: def.slug,
14859
- state_data: input.payload
14860
- });
14861
- if (!created) return { success: false, error: "Failed to create instance" };
14862
- inst = created;
14863
- }
14864
- if (input.payload && input.instance_id) {
14865
- Object.assign(inst.state_data, input.payload);
14866
- }
14867
- const transition = def.transitions.find((t27) => t27.name === input.action_name && t27.from.includes(inst.current_state));
14868
- if (!transition) {
14869
- return {
14870
- success: false,
14871
- instance_id: inst.id,
14872
- from_state: inst.current_state,
14873
- to_state: null,
14874
- state_data: inst.state_data,
14875
- error: `No transition '${input.action_name}' from state '${inst.current_state}'`
14876
- };
14877
- }
14878
- const fromState = inst.current_state;
14879
- const now = (/* @__PURE__ */ new Date()).toISOString();
14880
- const events = [];
14881
- let lastEvalResult = null;
14882
- events.push({ event_type: "transition", message: `Transition '${transition.name}' started: ${fromState} \u2192 ${transition.to}`, timestamp: now });
14883
- for (const action of transition.actions ?? []) {
14884
- try {
14885
- if (action.type === "set_field") {
14886
- const field = action.config?.field;
14887
- if (action.config?.expression) {
14888
- const expr = action.config.expression;
14889
- const result = this.evaluateSimpleExpression(expr, inst.state_data);
14890
- inst.state_data[field] = result;
14891
- } else if (action.config?.value !== void 0) {
14892
- inst.state_data[field] = action.config.value;
14893
- }
14894
- events.push({ event_type: "action_executed", message: `transition action 'set_field' succeeded`, timestamp: now });
14895
- } else if (action.type === "eval") {
14896
- const expr = action.config?.expression;
14897
- lastEvalResult = this.evaluateSimpleExpression(expr, inst.state_data);
14898
- events.push({ event_type: "action_executed", message: `transition action 'eval' succeeded`, timestamp: now });
14899
- } else {
14900
- events.push({ event_type: "action_executed", message: `transition action '${action.type}' succeeded (no-op in local mode)`, timestamp: now });
14901
- }
14902
- } catch (err) {
14903
- const msg = err instanceof Error ? err.message : String(err);
14904
- events.push({ event_type: "action_failed", message: `transition action '${action.type}' failed: ${msg}`, timestamp: now });
14905
- return {
14906
- success: false,
14907
- instance_id: inst.id,
14908
- from_state: fromState,
14909
- to_state: null,
14910
- state_data: inst.state_data,
14911
- event_log: events,
14912
- error: `transition action failed: ${msg}`
14913
- };
14914
- }
14915
- }
14916
- inst.current_state = transition.to;
14917
- inst.execution_lock_version++;
14918
- inst.updated_at = now;
14919
- events.push({ event_type: "transition", message: `State changed: ${fromState} \u2192 ${transition.to}`, timestamp: now });
14920
- inst.event_log.push(...events);
14921
- return {
14922
- success: true,
14923
- instance_id: inst.id,
14924
- from_state: fromState,
14925
- to_state: transition.to,
14926
- state_data: inst.state_data,
14927
- result: lastEvalResult,
14928
- event_log: events
14929
- };
14930
- }
14931
- /**
14932
- * Minimal expression evaluator for local dev mode.
14933
- * Handles: field references, arithmetic, string literals, simple comparisons.
14934
- * Does NOT handle: while loops, if/else, function calls, multi-statement blocks.
14935
- * For full evaluation, use mm-napi when available.
14936
- */
14937
- evaluateSimpleExpression(expr, context) {
14938
- if (context[expr] !== void 0) return context[expr];
14939
- const arithMatch = expr.match(/^(\w+)\s*([+\-*/])\s*(\d+(?:\.\d+)?)$/);
14940
- if (arithMatch) {
14941
- const [, field, op, numStr] = arithMatch;
14942
- const left = Number(context[field] ?? 0);
14943
- const right = Number(numStr);
14944
- switch (op) {
14945
- case "+":
14946
- return left + right;
14947
- case "-":
14948
- return left - right;
14949
- case "*":
14950
- return left * right;
14951
- case "/":
14952
- return right !== 0 ? left / right : 0;
14953
- }
14954
- }
14955
- if (/^\d+(\.\d+)?$/.test(expr.trim())) {
14956
- return Number(expr.trim());
14957
- }
14958
- const strMatch = expr.match(/^["'](.*)["']$/);
14959
- if (strMatch) return strMatch[1];
14960
- try {
14961
- const keys = Object.keys(context);
14962
- const values = Object.values(context);
14963
- const fn = new Function(...keys, `"use strict"; return (${expr});`);
14964
- return fn(...values);
14965
- } catch {
14966
- return null;
14967
- }
14968
- }
14969
- };
14970
- }
14971
- });
14972
-
14973
- // src/cli/seed.ts
14974
- var seed_exports = {};
14975
- __export(seed_exports, {
14976
- generateFieldValue: () => generateFieldValue,
14977
- generateInstanceData: () => generateInstanceData,
14978
- seedInstances: () => seedInstances
14979
- });
14980
- function randomInt(min, max) {
14981
- return Math.floor(Math.random() * (max - min + 1)) + min;
14982
- }
14983
- function randomPick(arr) {
14984
- return arr[Math.floor(Math.random() * arr.length)];
14985
- }
14986
- function loremSentence(words = 5) {
14987
- const result = [];
14988
- for (let i = 0; i < words; i++) result.push(randomPick(LOREM_WORDS));
14989
- const s = result.join(" ");
14990
- return s.charAt(0).toUpperCase() + s.slice(1);
14991
- }
14992
- function generateFieldValue(field, index) {
14993
- if (field.default_value !== void 0 && field.default_value !== null) {
14994
- return field.default_value;
14995
- }
14996
- if (field.options && field.options.length > 0) {
14997
- return field.options[index % field.options.length].value;
14998
- }
14999
- const ft = (field.field_type ?? field.type ?? "text").toLowerCase();
15000
- switch (ft) {
15001
- case "text":
15002
- case "string":
15003
- case "textarea":
15004
- case "rich_text": {
15005
- const name = field.name.toLowerCase();
15006
- if (name.includes("email")) return `${randomPick(FIRST_NAMES).toLowerCase()}${++_counter}@${randomPick(DOMAINS)}`;
15007
- if (name.includes("name") && name.includes("first")) return randomPick(FIRST_NAMES);
15008
- if (name.includes("name") && name.includes("last")) return randomPick(LAST_NAMES);
15009
- if (name.includes("name") || name === "title") return `${randomPick(FIRST_NAMES)} ${randomPick(LAST_NAMES)}`;
15010
- if (name.includes("company") || name.includes("org")) return randomPick(COMPANIES);
15011
- if (name.includes("phone")) return `+1-555-${randomInt(100, 999)}-${randomInt(1e3, 9999)}`;
15012
- if (name.includes("url") || name.includes("website")) return `https://${randomPick(DOMAINS)}/${field.name}`;
15013
- if (name.includes("description") || name.includes("note") || name.includes("comment")) return loremSentence(randomInt(8, 15));
15014
- if (name.includes("address")) return `${randomInt(100, 9999)} ${loremSentence(2)} St`;
15015
- return loremSentence(randomInt(2, 5));
15016
- }
15017
- case "number":
15018
- case "numeric":
15019
- case "integer":
15020
- case "int": {
15021
- const name = field.name.toLowerCase();
15022
- if (name.includes("price") || name.includes("amount") || name.includes("cost") || name.includes("salary"))
15023
- return randomInt(1e3, 99999) / 100;
15024
- if (name.includes("age")) return randomInt(18, 65);
15025
- if (name.includes("quantity") || name.includes("count") || name.includes("qty")) return randomInt(1, 100);
15026
- if (name.includes("percent") || name.includes("rate")) return randomInt(1, 100);
15027
- return randomInt(1, 1e3);
15028
- }
15029
- case "currency":
15030
- case "decimal":
15031
- return randomInt(100, 999999) / 100;
15032
- case "boolean":
15033
- case "bool":
15034
- case "checkbox":
15035
- case "toggle":
15036
- return index % 2 === 0;
15037
- case "date":
15038
- case "datetime":
15039
- case "timestamptz": {
15040
- const d = /* @__PURE__ */ new Date();
15041
- d.setDate(d.getDate() - randomInt(0, 365));
15042
- return d.toISOString().split("T")[0];
15043
- }
15044
- case "select":
15045
- case "enum":
15046
- case "multi_select":
15047
- return field.options?.[index % (field.options?.length || 1)]?.value ?? "option_1";
15048
- case "email":
15049
- return `${randomPick(FIRST_NAMES).toLowerCase()}${++_counter}@${randomPick(DOMAINS)}`;
15050
- case "url":
15051
- return `https://${randomPick(DOMAINS)}/${field.name}`;
15052
- case "json":
15053
- case "object":
15054
- return {};
15055
- case "array":
15056
- return [];
15057
- default:
15058
- return loremSentence(3);
15059
- }
15060
- }
15061
- function generateInstanceData(fields, index) {
15062
- const data = {};
15063
- for (const field of fields) {
15064
- if (field.name === "id" || field.name === "created_at" || field.name === "updated_at") continue;
15065
- data[field.name] = generateFieldValue(field, index);
15066
- }
15067
- return data;
15068
- }
15069
- async function seedInstances(options) {
15070
- const dir = options.dir ?? "dist/workflows";
15071
- const count = options.count ?? 7;
15072
- const result = { created: 0, definitions: 0, errors: [] };
15073
- const files = await (0, import_glob4.glob)(`${dir}/**/*.workflow.json`);
15074
- if (files.length === 0) return result;
15075
- for (const file2 of files) {
15076
- let def;
15077
- try {
15078
- def = JSON.parse((0, import_fs7.readFileSync)(file2, "utf-8"));
15079
- } catch {
15080
- continue;
15081
- }
15082
- if (!def.slug || !def.fields || def.fields.length === 0) continue;
15083
- const startState = def.states?.find((s) => s.state_type === "START")?.name ?? def.states?.[0]?.name ?? "initial";
15084
- let defCreated = 0;
15085
- for (let i = 0; i < count; i++) {
15086
- const stateData = generateInstanceData(def.fields, i);
15087
- try {
15088
- const resp = await fetch(`${options.apiUrl}/workflow/instances`, {
15089
- method: "POST",
15090
- headers: {
15091
- "Content-Type": "application/json",
15092
- "Authorization": `Bearer ${options.token}`
15093
- },
15094
- body: JSON.stringify({
15095
- definition_slug: def.slug,
15096
- initial_state: startState,
15097
- state_data: stateData
15098
- })
15099
- });
15100
- if (resp.ok) {
15101
- defCreated++;
15102
- } else {
15103
- const errText = await resp.text().catch(() => resp.statusText);
15104
- result.errors.push(`${def.slug}[${i}]: ${resp.status} ${errText}`);
15105
- }
15106
- } catch (e) {
15107
- result.errors.push(`${def.slug}[${i}]: ${e instanceof Error ? e.message : String(e)}`);
15108
- }
15109
- }
15110
- if (defCreated > 0) {
15111
- result.definitions++;
15112
- result.created += defCreated;
15113
- console.log(` [seed] ${def.slug}: ${defCreated} instances created`);
15114
- }
15115
- }
15116
- return result;
15117
- }
15118
- var import_fs7, import_glob4, FIRST_NAMES, LAST_NAMES, COMPANIES, LOREM_WORDS, DOMAINS, _counter;
15119
- var init_seed = __esm({
15120
- "src/cli/seed.ts"() {
15121
- "use strict";
15122
- import_fs7 = require("fs");
15123
- import_glob4 = require("glob");
15124
- FIRST_NAMES = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank", "Grace", "Henry"];
15125
- LAST_NAMES = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis"];
15126
- COMPANIES = ["Acme Corp", "TechStart Inc", "GlobalTrade Ltd", "DataFlow Systems", "CloudPeak"];
15127
- LOREM_WORDS = ["lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit", "sed", "do", "eiusmod", "tempor"];
15128
- DOMAINS = ["example.com", "test.org", "demo.io", "sample.net"];
15129
- _counter = 0;
15130
- }
15131
- });
15132
-
15133
- // src/dev-server.ts
15134
- var dev_server_exports = {};
15135
- __export(dev_server_exports, {
15136
- createDevServer: () => createDevServer
15137
- });
15138
- function escapeHtml(s) {
15139
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
15140
- }
15141
- function renderErrorOverlay(errors) {
15142
- const cards = errors.map((err) => {
15143
- const loc = err.line ? `${err.file}:${err.line}${err.column ? ":" + err.column : ""}` : err.file;
15144
- const snippet = err.snippet ? `<pre style="background:#1a1a2e;color:#e0e0e0;padding:12px 16px;border-radius:6px;overflow-x:auto;margin-top:8px;font-size:13px;line-height:1.5">${escapeHtml(err.snippet)}</pre>` : "";
15145
- return `<div style="background:#2d1b1b;border:1px solid #5c2020;border-radius:8px;padding:16px 20px;margin-bottom:12px">
15146
- <div style="color:#ff6b6b;font-family:monospace;font-size:13px;margin-bottom:6px">${escapeHtml(loc)}</div>
15147
- <div style="color:#ffa0a0;font-size:15px;font-weight:500">${escapeHtml(err.message)}</div>${snippet}</div>`;
15148
- }).join("\n");
15149
- return `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
15150
- <title>Compile Error - MindMatrix Dev</title>
15151
- <style>*{box-sizing:border-box;margin:0;padding:0}body{background:#1a1a2e;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;padding:40px 20px;min-height:100vh}</style></head>
15152
- <body><div style="max-width:800px;margin:0 auto">
15153
- <div style="display:flex;align-items:center;gap:12px;margin-bottom:24px">
15154
- <div style="background:#5c2020;color:#ff6b6b;font-size:14px;font-weight:600;padding:4px 10px;border-radius:4px">COMPILE ERROR</div>
15155
- <div style="color:#888;font-size:13px">${errors.length} error${errors.length !== 1 ? "s" : ""}</div></div>
15156
- ${cards}
15157
- <div style="color:#666;font-size:12px;margin-top:24px;text-align:center">Fix the error and save -- the page will reload automatically.</div></div>
15158
- <script>const ws=new WebSocket('ws://'+location.host+'/__mm_dev');ws.onmessage=e=>{const m=JSON.parse(e.data);if(m.type==='workflow:compiled'||m.type==='workflow:rebuild')location.reload()};ws.onclose=()=>setTimeout(()=>location.reload(),2000)</script>
15159
- </body></html>`;
15160
- }
15161
- function errorOverlayMiddleware() {
15162
- return (req, res, next) => {
15163
- if (!currentErrors || !req.url) return next();
15164
- const url = req.url;
15165
- if (url.startsWith("/api") || url.startsWith("/health") || url.startsWith("/ws") || url.startsWith("/__mm_dev") || url.startsWith("/@") || url.startsWith("/node_modules")) return next();
15166
- const accept = req.headers?.accept ?? "";
15167
- if (!accept.includes("text/html")) return next();
15168
- res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
15169
- res.end(renderErrorOverlay(currentErrors));
15170
- };
15171
- }
15172
- async function resolveDevToken(apiUrl, explicit) {
15173
- if (explicit) return explicit;
15174
- try {
15175
- const { resolveToken: resolveToken2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
15176
- const s = resolveToken2(apiUrl);
15177
- if (s) return s;
15178
- } catch {
15179
- }
15180
- try {
15181
- const resp = await fetch(`${apiUrl}/auth/login`, {
15182
- method: "POST",
15183
- headers: { "Content-Type": "application/json" },
15184
- body: JSON.stringify({ email: "admin@mindmatrix.com", password: "Admin123!" })
15185
- });
15186
- if (resp.ok) {
15187
- const data = await resp.json();
15188
- const token = data.token ?? data.access_token;
15189
- if (token) {
15190
- try {
15191
- const { saveCredentials: saveCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
15192
- saveCredentials2(apiUrl, token);
15193
- } catch {
15194
- }
15195
- return token;
15196
- }
15197
- }
15198
- } catch {
15199
- }
15200
- return process.env.MINDMATRIX_TOKEN;
15201
- }
15202
- async function checkBackendHealth(apiUrl) {
15203
- try {
15204
- const base = apiUrl.replace(/\/api\/v1\/?$/, "");
15205
- const resp = await fetch(`${base}/health`, { signal: AbortSignal.timeout(5e3) });
15206
- if (resp.ok) {
15207
- const d = await resp.json();
15208
- return { ok: true, version: d.version, db: d.database };
15209
- }
15210
- return { ok: resp.status < 500 };
15211
- } catch {
15212
- return { ok: false };
15213
- }
15214
- }
15215
- async function initialBuildDeploy(src, outDir, mode, apiUrl, token) {
15216
- console.log("[mm-dev] Compiling project...");
15217
- const buildResult = await build({ src, outDir, mode, skipTypeCheck: true });
15218
- if (buildResult.errors > 0) {
15219
- currentErrors = buildResult.errorDetails.map((e) => ({ file: e.file, message: e.message, line: e.line }));
15220
- console.error(`[mm-dev] Build failed with ${buildResult.errors} error(s) -- error overlay active`);
15221
- return { buildResult, deployed: false, slug: void 0 };
15222
- }
15223
- currentErrors = null;
15224
- if (buildResult.compiled === 0) return { buildResult, deployed: false, slug: void 0 };
15225
- console.log(`[mm-dev] Compiled ${buildResult.compiled} definition(s)`);
15226
- try {
15227
- const { deploy: deploy2 } = await Promise.resolve().then(() => (init_deploy(), deploy_exports));
15228
- const result = await deploy2({ apiUrl, token, dir: outDir });
15229
- const total = result.created.length + result.updated.length + result.versioned.length;
15230
- if (total > 0) console.log(`[mm-dev] Deployed: ${result.created.length} created, ${result.updated.length} updated`);
15231
- return { buildResult, deployed: true, slug: buildResult.definitions?.[0]?.slug };
15232
- } catch (e) {
15233
- console.warn(`[mm-dev] Deploy failed: ${e instanceof Error ? e.message : e}`);
15234
- return { buildResult, deployed: false, slug: void 0 };
15235
- }
15236
- }
15237
- function printBanner(o) {
15238
- const l = "-".repeat(58);
15239
- console.log(`
15240
- ${l}
15241
- MindMatrix Dev Server
15242
- ${l}
15243
- `);
15244
- console.log(` Preview: http://localhost:${o.port}`);
15245
- console.log(` API Proxy: /api/* -> ${o.apiUrl}`);
15246
- console.log(` WebSocket: ws://localhost:${o.port}/__mm_dev`);
15247
- console.log(` Watching: ${o.src} (${o.include.join(", ")})
15248
- `);
15249
- console.log(` Backend: ${o.health.ok ? "connected" : "unreachable"}${o.health.version ? ` (v${o.health.version})` : ""}`);
15250
- if (o.health.db) console.log(` Database: ${o.health.db}`);
15251
- console.log(` Auth: ${o.token ? "authenticated" : "no token"}`);
15252
- if (o.compiled > 0) {
15253
- console.log(` Blueprint: ${o.slug ?? "unknown"} (${o.compiled} defs)`);
15254
- console.log(` Deploy: ${o.deployed ? "synced" : "pending"}`);
15255
- }
15256
- if (o.errors > 0) console.log(` Errors: ${o.errors} compile error(s) -- overlay active`);
15257
- console.log(`
15258
- ${l}
15259
- `);
15260
- }
15261
- function broadcast(clients, data) {
15262
- const msg = JSON.stringify(data);
15263
- for (const c of clients) {
15264
- try {
15265
- if (c.readyState === 1) c.send(msg);
15266
- } catch {
15267
- clients.delete(c);
15268
- }
14156
+ import_fs6 = require("fs");
14157
+ import_path5 = require("path");
14158
+ init_babel();
15269
14159
  }
15270
- }
14160
+ });
14161
+
14162
+ // src/dev-server.ts
14163
+ var dev_server_exports = {};
14164
+ __export(dev_server_exports, {
14165
+ createDevServer: () => createDevServer
14166
+ });
15271
14167
  async function createDevServer(options = {}) {
15272
14168
  const {
15273
14169
  port = 5199,
@@ -15276,87 +14172,44 @@ async function createDevServer(options = {}) {
15276
14172
  include = ["**/*.workflow.tsx"],
15277
14173
  outDir = "dist/workflows",
15278
14174
  seed = false,
15279
- apiUrl: rawApiUrl = "auto",
15280
- authToken: explicitToken,
14175
+ apiUrl = "http://localhost:4200/api/v1",
14176
+ authToken = process.env.MINDMATRIX_TOKEN,
15281
14177
  ws: enableWs = true,
15282
14178
  open = false
15283
14179
  } = options;
15284
14180
  const clients = /* @__PURE__ */ new Set();
15285
- let localServer = null;
15286
- let apiUrl;
15287
- let isLocalMode = false;
15288
- if (rawApiUrl === "local") {
15289
- localServer = await startLocalServer({ port: 4200 });
15290
- apiUrl = "http://localhost:4200/api/v1";
15291
- isLocalMode = true;
15292
- } else if (rawApiUrl === "auto") {
15293
- const defaultRemote = "https://dev.mindmatrix.club/api/v1";
15294
- const remoteHealth = await checkBackendHealth(defaultRemote);
15295
- if (remoteHealth.ok) {
15296
- apiUrl = defaultRemote;
15297
- } else {
15298
- const localHealth = await checkBackendHealth("http://localhost:4200/api/v1");
15299
- if (localHealth.ok) {
15300
- apiUrl = "http://localhost:4200/api/v1";
15301
- } else {
15302
- console.log("[mm-dev] No backend detected \u2014 starting local in-memory API server...");
15303
- localServer = await startLocalServer({ port: 4200 });
15304
- apiUrl = "http://localhost:4200/api/v1";
15305
- isLocalMode = true;
15306
- }
15307
- }
15308
- } else {
15309
- apiUrl = rawApiUrl;
15310
- }
15311
- const token = isLocalMode ? "dev-token-local" : await resolveDevToken(apiUrl, explicitToken);
15312
- const health = isLocalMode ? { ok: true, version: "local", db: "in-memory" } : await checkBackendHealth(apiUrl);
15313
- let initialSlug, initialCompiled = 0, initialDeployed = false, initialErrors = 0;
15314
- if (token && health.ok) {
15315
- const r = await initialBuildDeploy(src, outDir, mode, apiUrl, token);
15316
- initialCompiled = r.buildResult.compiled;
15317
- initialDeployed = r.deployed;
15318
- initialSlug = r.slug;
15319
- initialErrors = r.buildResult.errors;
15320
- }
15321
- if (seed && token && initialDeployed) {
15322
- try {
15323
- const { seedInstances: seedInstances2 } = await Promise.resolve().then(() => (init_seed(), seed_exports));
15324
- console.log("[mm-dev] Seeding sample instances...");
15325
- const sr = await seedInstances2({ apiUrl, token, dir: outDir });
15326
- console.log(`[mm-dev] Seeded ${sr.created} instance(s) across ${sr.definitions} definition(s)`);
15327
- } catch (e) {
15328
- console.warn(`[mm-dev] Seed failed: ${e instanceof Error ? e.message : e}`);
15329
- }
15330
- }
15331
- const pluginOpts = { mode, include, outDir, seedOnCompile: seed, apiUrl, authToken: token };
15332
- const proxyTarget = apiUrl.replace(/\/api\/v1\/?$/, "") || apiUrl;
15333
- let deployInFlight = false;
15334
- const compileDeployPlugin = {
15335
- name: "mindmatrix-dev-compile-deploy",
14181
+ const pluginOptions = {
14182
+ mode,
14183
+ include,
14184
+ outDir,
14185
+ seedOnCompile: seed,
14186
+ apiUrl,
14187
+ authToken
14188
+ };
14189
+ const notifyPlugin = {
14190
+ name: "mindmatrix-dev-notify",
15336
14191
  enforce: "post",
15337
- async handleHotUpdate(ctx) {
15338
- const isWf = include.some((p) => new RegExp(p.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "<<G>>").replace(/\*/g, "[^/]*").replace(/<<G>>/g, ".*").replace(/\?/g, ".")).test(ctx.file));
15339
- if (!isWf) return;
15340
- const fn = ctx.file.split("/").pop() ?? ctx.file;
15341
- console.log(`[mm-dev] Recompiling ${fn}...`);
15342
- const br = await build({ src, outDir, mode, skipTypeCheck: true });
15343
- if (br.errors > 0) {
15344
- currentErrors = br.errorDetails.map((e) => ({ file: e.file, message: e.message, line: e.line }));
15345
- broadcast(clients, { type: "workflow:error", errors: br.errors, timestamp: ctx.timestamp });
15346
- return;
15347
- }
15348
- currentErrors = null;
15349
- if (token && !deployInFlight) {
15350
- deployInFlight = true;
14192
+ handleHotUpdate(ctx) {
14193
+ const isWorkflow = include.some((pattern) => {
14194
+ const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "<<GLOBSTAR>>").replace(/\*/g, "[^/]*").replace(/<<GLOBSTAR>>/g, ".*").replace(/\?/g, ".");
14195
+ return new RegExp(regex).test(ctx.file);
14196
+ });
14197
+ if (!isWorkflow) return;
14198
+ const message = JSON.stringify({
14199
+ type: "workflow:compiled",
14200
+ file: ctx.file,
14201
+ timestamp: ctx.timestamp
14202
+ });
14203
+ for (const client of clients) {
15351
14204
  try {
15352
- const { deploy: d } = await Promise.resolve().then(() => (init_deploy(), deploy_exports));
15353
- await d({ apiUrl, token, dir: outDir });
14205
+ if (client.readyState === 1) {
14206
+ client.send(message);
14207
+ }
15354
14208
  } catch {
15355
- } finally {
15356
- deployInFlight = false;
14209
+ clients.delete(client);
15357
14210
  }
15358
14211
  }
15359
- broadcast(clients, { type: "workflow:compiled", file: ctx.file, compiled: br.compiled, timestamp: ctx.timestamp });
14212
+ console.log(`[mm-dev] Compiled ${ctx.file.split("/").pop()} \u2014 notified ${clients.size} client(s)`);
15360
14213
  }
15361
14214
  };
15362
14215
  const viteConfig = {
@@ -15364,87 +14217,94 @@ async function createDevServer(options = {}) {
15364
14217
  server: {
15365
14218
  port,
15366
14219
  open,
15367
- host: true,
15368
- proxy: {
15369
- "/api": { target: proxyTarget, changeOrigin: true, secure: true, ws: true },
15370
- "/health": { target: proxyTarget, changeOrigin: true, secure: true },
15371
- "/ws": { target: proxyTarget.replace(/^http/, "ws"), changeOrigin: true, ws: true }
15372
- }
14220
+ host: true
15373
14221
  },
15374
14222
  plugins: [
15375
- mindmatrixReact(pluginOpts),
15376
- compileDeployPlugin,
15377
- { name: "mindmatrix-error-overlay", configureServer(server) {
15378
- server.middlewares.use(errorOverlayMiddleware());
15379
- } }
14223
+ mindmatrixReact(pluginOptions),
14224
+ notifyPlugin
15380
14225
  ],
15381
- logLevel: "warn"
14226
+ logLevel: "info"
15382
14227
  };
15383
- const { createServer: createServer2 } = await import("vite");
15384
- const vite = await createServer2(viteConfig);
14228
+ const { createServer } = await import("vite");
14229
+ const vite = await createServer(viteConfig);
15385
14230
  await vite.listen();
15386
14231
  const resolvedPort = vite.config.server.port ?? port;
15387
14232
  if (enableWs && vite.httpServer) {
15388
14233
  try {
15389
- const wsM = await Function('return import("ws")')();
15390
- const WSS = wsM.WebSocketServer ?? wsM.default?.WebSocketServer;
15391
- if (WSS) {
15392
- const wss = new WSS({ server: vite.httpServer, path: "/__mm_dev" });
15393
- wss.on("connection", (s) => {
15394
- clients.add(s);
15395
- s.on("close", () => clients.delete(s));
15396
- s.send(JSON.stringify({ type: "mm:connected", version: "0.1.0", capabilities: ["compile", "deploy", "proxy", "notify", "error-overlay"] }));
14234
+ const wsModule = await Function('return import("ws")')();
14235
+ const WebSocketServer = wsModule.WebSocketServer ?? wsModule.default?.WebSocketServer;
14236
+ if (WebSocketServer) {
14237
+ const wss = new WebSocketServer({
14238
+ server: vite.httpServer,
14239
+ path: "/__mm_dev"
14240
+ });
14241
+ wss.on("connection", (socket) => {
14242
+ clients.add(socket);
14243
+ console.log(`[mm-dev] Editor connected (${clients.size} total)`);
14244
+ socket.on("close", () => {
14245
+ clients.delete(socket);
14246
+ console.log(`[mm-dev] Editor disconnected (${clients.size} total)`);
14247
+ });
14248
+ socket.send(JSON.stringify({
14249
+ type: "mm:connected",
14250
+ version: "0.1.0",
14251
+ capabilities: ["compile", "seed", "notify"]
14252
+ }));
15397
14253
  });
15398
14254
  }
15399
14255
  } catch {
14256
+ console.warn("[mm-dev] ws package not available \u2014 WebSocket notifications disabled");
15400
14257
  }
15401
14258
  }
15402
- printBanner({ port: resolvedPort, apiUrl: isLocalMode ? `${apiUrl} (local in-memory)` : apiUrl, src, include, health, token: !!token, compiled: initialCompiled, deployed: initialDeployed, slug: initialSlug, errors: initialErrors });
14259
+ console.log(`
14260
+ [mm-dev] MindMatrix Dev Server running`);
14261
+ console.log(` Local: http://localhost:${resolvedPort}`);
14262
+ console.log(` WebSocket: ws://localhost:${resolvedPort}/__mm_dev`);
14263
+ console.log(` Watching: ${src} (${include.join(", ")})`);
14264
+ if (seed) {
14265
+ console.log(` Seeding: ${apiUrl}`);
14266
+ }
14267
+ console.log();
15403
14268
  return {
15404
14269
  vite,
15405
14270
  port: resolvedPort,
15406
14271
  clients,
15407
14272
  async rebuild() {
15408
- const r = await build({ src, outDir, mode });
15409
- if (r.errors > 0) {
15410
- currentErrors = r.errorDetails.map((e) => ({ file: e.file, message: e.message, line: e.line }));
15411
- } else {
15412
- currentErrors = null;
15413
- if (token) {
15414
- try {
15415
- const { deploy: d } = await Promise.resolve().then(() => (init_deploy(), deploy_exports));
15416
- await d({ apiUrl, token, dir: outDir });
15417
- } catch {
15418
- }
14273
+ console.log("[mm-dev] Full rebuild triggered...");
14274
+ const result = await build({ src, outDir, mode });
14275
+ const message = JSON.stringify({
14276
+ type: "workflow:rebuild",
14277
+ compiled: result.compiled,
14278
+ errors: result.errors,
14279
+ timestamp: Date.now()
14280
+ });
14281
+ for (const client of clients) {
14282
+ try {
14283
+ if (client.readyState === 1) client.send(message);
14284
+ } catch {
14285
+ clients.delete(client);
15419
14286
  }
15420
14287
  }
15421
- broadcast(clients, { type: "workflow:rebuild", compiled: r.compiled, errors: r.errors, timestamp: Date.now() });
15422
- return r;
14288
+ return result;
15423
14289
  },
15424
14290
  async close() {
15425
- for (const c of clients) {
14291
+ for (const client of clients) {
15426
14292
  try {
15427
- c.close();
14293
+ client.close();
15428
14294
  } catch {
15429
14295
  }
15430
14296
  }
15431
14297
  clients.clear();
15432
- currentErrors = null;
15433
14298
  await vite.close();
15434
- if (localServer) {
15435
- await localServer.close();
15436
- }
14299
+ console.log("[mm-dev] Server stopped");
15437
14300
  }
15438
14301
  };
15439
14302
  }
15440
- var currentErrors;
15441
14303
  var init_dev_server = __esm({
15442
14304
  "src/dev-server.ts"() {
15443
14305
  "use strict";
15444
14306
  init_vite();
15445
14307
  init_build();
15446
- init_local_server();
15447
- currentErrors = null;
15448
14308
  }
15449
14309
  });
15450
14310
 
@@ -15486,7 +14346,7 @@ function findConfigPath(startDir) {
15486
14346
  while (true) {
15487
14347
  for (const name of ["mmrc.config.ts", "mmrc.config.json"]) {
15488
14348
  const candidate = (0, import_path6.join)(dir, name);
15489
- if ((0, import_fs8.existsSync)(candidate)) return candidate;
14349
+ if ((0, import_fs7.existsSync)(candidate)) return candidate;
15490
14350
  }
15491
14351
  const parent = (0, import_path6.resolve)(dir, "..");
15492
14352
  if (parent === dir || parent === root) break;
@@ -15495,7 +14355,7 @@ function findConfigPath(startDir) {
15495
14355
  return null;
15496
14356
  }
15497
14357
  function loadConfigFromFile(configPath) {
15498
- const content = (0, import_fs8.readFileSync)(configPath, "utf-8");
14358
+ const content = (0, import_fs7.readFileSync)(configPath, "utf-8");
15499
14359
  if (configPath.endsWith(".json")) {
15500
14360
  try {
15501
14361
  return JSON.parse(content);
@@ -15576,7 +14436,7 @@ const config = ${json};
15576
14436
 
15577
14437
  export default config;
15578
14438
  `;
15579
- (0, import_fs8.writeFileSync)(configPath, content, "utf-8");
14439
+ (0, import_fs7.writeFileSync)(configPath, content, "utf-8");
15580
14440
  }
15581
14441
  function resolveTarget(targetName, config, fallbackApiUrl) {
15582
14442
  if (!config || !config.environments) {
@@ -15638,11 +14498,11 @@ function loadMmrcConfig(dir) {
15638
14498
  const partial = loadConfigFromFile(path);
15639
14499
  return deepMerge(DEFAULT_CONFIG, partial);
15640
14500
  }
15641
- var import_fs8, import_path6, DEFAULT_CONFIG;
14501
+ var import_fs7, import_path6, DEFAULT_CONFIG;
15642
14502
  var init_config = __esm({
15643
14503
  "src/cli/config.ts"() {
15644
14504
  "use strict";
15645
- import_fs8 = require("fs");
14505
+ import_fs7 = require("fs");
15646
14506
  import_path6 = require("path");
15647
14507
  DEFAULT_CONFIG = {
15648
14508
  dev: {
@@ -15676,9 +14536,7 @@ var init_config = __esm({
15676
14536
  // src/cli/init.ts
15677
14537
  var init_exports = {};
15678
14538
  __export(init_exports, {
15679
- init: () => init,
15680
- isInsideGitRepo: () => isInsideGitRepo,
15681
- scaffoldGit: () => scaffoldGit
14539
+ init: () => init
15682
14540
  });
15683
14541
  function toTitleCase(slug) {
15684
14542
  return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
@@ -15689,7 +14547,7 @@ function toPascalCase2(slug) {
15689
14547
  function generatePackageJson2(name) {
15690
14548
  return JSON.stringify(
15691
14549
  {
15692
- name: `@mmapp/blueprint-${name}`,
14550
+ name: `@mindmatrix/blueprint-${name}`,
15693
14551
  version: "0.1.0",
15694
14552
  private: true,
15695
14553
  type: "module",
@@ -15997,38 +14855,6 @@ export default function ${pascal}Home() {
15997
14855
  }
15998
14856
  `;
15999
14857
  }
16000
- function generateGitignore() {
16001
- return `node_modules
16002
- dist
16003
- dev.db
16004
- .env
16005
- .env.local
16006
- .env.*.local
16007
- *.log
16008
- .DS_Store
16009
- `;
16010
- }
16011
- function isInsideGitRepo(dir) {
16012
- try {
16013
- (0, import_child_process2.execSync)("git rev-parse --git-dir", { cwd: dir, stdio: "pipe" });
16014
- return true;
16015
- } catch {
16016
- return false;
16017
- }
16018
- }
16019
- function scaffoldGit(dir) {
16020
- if (isInsideGitRepo(dir)) {
16021
- return "Skipped git init (inside existing repo)";
16022
- }
16023
- try {
16024
- (0, import_child_process2.execSync)("git init", { cwd: dir, stdio: "pipe" });
16025
- (0, import_child_process2.execSync)("git add -A", { cwd: dir, stdio: "pipe" });
16026
- (0, import_child_process2.execSync)('git commit -m "Initial commit from mmrc init"', { cwd: dir, stdio: "pipe" });
16027
- return "Initialized git repository";
16028
- } catch (e) {
16029
- return `Git init failed: ${e.message}`;
16030
- }
16031
- }
16032
14858
  async function init(options) {
16033
14859
  const { name } = options;
16034
14860
  if (!/^[a-z][a-z0-9-]*$/.test(name)) {
@@ -16036,34 +14862,31 @@ async function init(options) {
16036
14862
  process.exit(1);
16037
14863
  }
16038
14864
  const cwd = process.cwd();
16039
- const packagesDir = (0, import_fs9.existsSync)((0, import_path7.join)(cwd, "packages")) ? (0, import_path7.join)(cwd, "packages") : cwd;
14865
+ const packagesDir = (0, import_fs8.existsSync)((0, import_path7.join)(cwd, "packages")) ? (0, import_path7.join)(cwd, "packages") : cwd;
16040
14866
  const blueprintDir = (0, import_path7.join)(packagesDir, `blueprint-${name}`);
16041
- if ((0, import_fs9.existsSync)(blueprintDir)) {
14867
+ if ((0, import_fs8.existsSync)(blueprintDir)) {
16042
14868
  console.error(`[mmrc] Error: Directory already exists: ${blueprintDir}`);
16043
14869
  process.exit(1);
16044
14870
  }
16045
14871
  console.log(`[mmrc] Creating blueprint: ${name}
16046
14872
  `);
16047
- (0, import_fs9.mkdirSync)((0, import_path7.join)(blueprintDir, "models"), { recursive: true });
16048
- (0, import_fs9.mkdirSync)((0, import_path7.join)(blueprintDir, "app"), { recursive: true });
14873
+ (0, import_fs8.mkdirSync)((0, import_path7.join)(blueprintDir, "models"), { recursive: true });
14874
+ (0, import_fs8.mkdirSync)((0, import_path7.join)(blueprintDir, "app"), { recursive: true });
16049
14875
  const { generateMmrcConfig: generateMmrcConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
16050
14876
  const files = [
16051
14877
  ["package.json", generatePackageJson2(name)],
16052
14878
  ["tsconfig.json", generateTsconfig()],
16053
14879
  ["mm.config.ts", generateMmConfig2(name, options)],
16054
14880
  ["mmrc.config.ts", generateMmrcConfig2()],
16055
- [".gitignore", generateGitignore()],
16056
14881
  ["models/item.ts", generateModel(name)],
16057
14882
  ["app/layout.tsx", generateLayout(name)],
16058
14883
  ["app/page.tsx", generatePage(name)]
16059
14884
  ];
16060
14885
  for (const [relPath, content] of files) {
16061
14886
  const fullPath = (0, import_path7.join)(blueprintDir, relPath);
16062
- (0, import_fs9.writeFileSync)(fullPath, content, "utf-8");
14887
+ (0, import_fs8.writeFileSync)(fullPath, content, "utf-8");
16063
14888
  console.log(` + ${relPath}`);
16064
14889
  }
16065
- const gitMessage = scaffoldGit(blueprintDir);
16066
- console.log(` ${gitMessage}`);
16067
14890
  const title = toTitleCase(name);
16068
14891
  console.log(`
16069
14892
  [mmrc] Blueprint "${title}" created at:
@@ -16076,13 +14899,12 @@ Next steps:
16076
14899
  4. mmrc deploy --build # Build + deploy to backend
16077
14900
  `);
16078
14901
  }
16079
- var import_fs9, import_path7, import_child_process2;
14902
+ var import_fs8, import_path7;
16080
14903
  var init_init = __esm({
16081
14904
  "src/cli/init.ts"() {
16082
14905
  "use strict";
16083
- import_fs9 = require("fs");
14906
+ import_fs8 = require("fs");
16084
14907
  import_path7 = require("path");
16085
- import_child_process2 = require("child_process");
16086
14908
  }
16087
14909
  });
16088
14910
 
@@ -17781,7 +16603,7 @@ function generateTestFile(ir) {
17781
16603
  lines.push(`import { analyzeReachability } from '../verify/reachability';`);
17782
16604
  lines.push(`import { analyzeSafety } from '../verify/safety';`);
17783
16605
  lines.push(`import { analyzeLiveness } from '../verify/liveness';`);
17784
- lines.push(`import type { IRWorkflowDefinition } from '@mmapp/player-core';`);
16606
+ lines.push(`import type { IRWorkflowDefinition } from '@mindmatrix/player-core';`);
17785
16607
  lines.push("");
17786
16608
  lines.push(`const ir: IRWorkflowDefinition = ${JSON.stringify(ir, null, 2)} as unknown as IRWorkflowDefinition;`);
17787
16609
  lines.push("");
@@ -17862,7 +16684,7 @@ __export(verify_exports, {
17862
16684
  verifyCommand: () => verifyCommand
17863
16685
  });
17864
16686
  function compileToIR(filePath) {
17865
- const code = (0, import_fs10.readFileSync)(filePath, "utf-8");
16687
+ const code = (0, import_fs9.readFileSync)(filePath, "utf-8");
17866
16688
  const result = (0, import_core6.transformSync)(code, {
17867
16689
  filename: filePath,
17868
16690
  plugins: [[babelPlugin, { mode: "strict" }]],
@@ -17879,10 +16701,10 @@ function compileToIR(filePath) {
17879
16701
  }
17880
16702
  async function resolveFiles(target) {
17881
16703
  const resolved = (0, import_path8.resolve)(target);
17882
- if (!(0, import_fs10.existsSync)(resolved)) {
16704
+ if (!(0, import_fs9.existsSync)(resolved)) {
17883
16705
  throw new Error(`Path not found: ${resolved}`);
17884
16706
  }
17885
- const stat = (0, import_fs10.statSync)(resolved);
16707
+ const stat = (0, import_fs9.statSync)(resolved);
17886
16708
  if (stat.isFile()) {
17887
16709
  const ext = (0, import_path8.extname)(resolved);
17888
16710
  if (![".ts", ".tsx", ".js", ".jsx"].includes(ext)) {
@@ -17891,7 +16713,7 @@ async function resolveFiles(target) {
17891
16713
  return [resolved];
17892
16714
  }
17893
16715
  if (stat.isDirectory()) {
17894
- const files = await (0, import_glob5.glob)(`${resolved}/**/*.{ts,tsx}`, {
16716
+ const files = await (0, import_glob4.glob)(`${resolved}/**/*.{ts,tsx}`, {
17895
16717
  ignore: ["**/node_modules/**", "**/dist/**", "**/*.test.*", "**/*.d.ts"]
17896
16718
  });
17897
16719
  if (files.length === 0) {
@@ -17966,13 +16788,13 @@ ${"=".repeat(50)}`);
17966
16788
  }
17967
16789
  return { success, filesProcessed, filesFailed, results };
17968
16790
  }
17969
- var import_fs10, import_path8, import_glob5, import_core6;
16791
+ var import_fs9, import_path8, import_glob4, import_core6;
17970
16792
  var init_verify = __esm({
17971
16793
  "src/cli/verify.ts"() {
17972
16794
  "use strict";
17973
- import_fs10 = require("fs");
16795
+ import_fs9 = require("fs");
17974
16796
  import_path8 = require("path");
17975
- import_glob5 = require("glob");
16797
+ import_glob4 = require("glob");
17976
16798
  import_core6 = require("@babel/core");
17977
16799
  init_babel();
17978
16800
  init_cli();
@@ -17995,12 +16817,12 @@ async function pull(options) {
17995
16817
  console.log(` Found: ${ir.name || ir.slug} (${ir.category || "workflow"}, v${ir.version || "1.0.0"})`);
17996
16818
  console.log(` Fields: ${ir.fields?.length ?? 0}, States: ${ir.states?.length ?? 0}, Transitions: ${ir.transitions?.length ?? 0}`);
17997
16819
  const result = decompileProjectEnhanced(ir);
17998
- (0, import_fs11.mkdirSync)(outDir, { recursive: true });
16820
+ (0, import_fs10.mkdirSync)(outDir, { recursive: true });
17999
16821
  const filesWritten = [];
18000
16822
  for (const file2 of result.files) {
18001
16823
  const filePath = (0, import_path9.join)(outDir, file2.path);
18002
- (0, import_fs11.mkdirSync)((0, import_path9.dirname)(filePath), { recursive: true });
18003
- (0, import_fs11.writeFileSync)(filePath, file2.content, "utf-8");
16824
+ (0, import_fs10.mkdirSync)((0, import_path9.dirname)(filePath), { recursive: true });
16825
+ (0, import_fs10.writeFileSync)(filePath, file2.content, "utf-8");
18004
16826
  filesWritten.push(file2.path);
18005
16827
  console.log(` + ${file2.path}`);
18006
16828
  }
@@ -18048,611 +16870,688 @@ async function tryFetch(url, token) {
18048
16870
  if (data.length === 0) return null;
18049
16871
  return normalizeApiResponse(data[0]);
18050
16872
  }
18051
- const items = data.items ?? data.data;
18052
- if (items && Array.isArray(items)) {
18053
- if (items.length === 0) return null;
18054
- return normalizeApiResponse(items[0]);
16873
+ const items = data.items ?? data.data;
16874
+ if (items && Array.isArray(items)) {
16875
+ if (items.length === 0) return null;
16876
+ return normalizeApiResponse(items[0]);
16877
+ }
16878
+ return normalizeApiResponse(data);
16879
+ } catch {
16880
+ return null;
16881
+ }
16882
+ }
16883
+ function normalizeApiResponse(def) {
16884
+ const ir = {
16885
+ slug: def.slug,
16886
+ name: def.name,
16887
+ version: def.version || "1.0.0",
16888
+ description: def.description || "",
16889
+ category: def.category || "workflow",
16890
+ fields: def.fields || [],
16891
+ states: def.states || [],
16892
+ transitions: def.transitions || [],
16893
+ roles: def.roles || [],
16894
+ on_event: def.on_event || [],
16895
+ metadata: {
16896
+ ...def.metadata || {}
16897
+ }
16898
+ };
16899
+ const experience = def.view || def.experience || def.metadata?.experience;
16900
+ if (experience) {
16901
+ ir.experience = experience;
16902
+ ir.metadata.experience = experience;
16903
+ ir.view = experience;
16904
+ }
16905
+ return ir;
16906
+ }
16907
+ var import_fs10, import_path9;
16908
+ var init_pull = __esm({
16909
+ "src/cli/pull.ts"() {
16910
+ "use strict";
16911
+ import_fs10 = require("fs");
16912
+ import_path9 = require("path");
16913
+ init_project_decompiler();
16914
+ }
16915
+ });
16916
+
16917
+ // src/cli/index.ts
16918
+ init_build();
16919
+
16920
+ // src/cli/test.ts
16921
+ var import_glob2 = require("glob");
16922
+ var import_fs3 = require("fs");
16923
+ var import_path2 = require("path");
16924
+ async function test(options = {}) {
16925
+ const srcDir = options.src ?? ".";
16926
+ const label = "[mindmatrix-react]";
16927
+ console.log(`${label} Round-trip fidelity test
16928
+ `);
16929
+ const configPath = (0, import_path2.resolve)(srcDir, "mm.config.ts");
16930
+ const isProject = (0, import_fs3.existsSync)(configPath);
16931
+ if (!isProject) {
16932
+ console.error(`${label} Error: No mm.config.ts found in ${srcDir}`);
16933
+ console.error(` Round-trip test requires a project with mm.config.ts`);
16934
+ process.exit(1);
16935
+ }
16936
+ const allFiles = await (0, import_glob2.glob)(`${srcDir}/**/*.{ts,tsx}`, {
16937
+ ignore: ["**/node_modules/**", "**/dist/**", "**/*.test.*"]
16938
+ });
16939
+ if (allFiles.length === 0) {
16940
+ console.error(`${label} Error: No source files found in ${srcDir}`);
16941
+ process.exit(1);
16942
+ }
16943
+ const fileMap = {};
16944
+ for (const f of allFiles) {
16945
+ const rel = f.startsWith(srcDir + "/") ? f.slice(srcDir.length + 1) : f;
16946
+ fileMap[rel] = (0, import_fs3.readFileSync)(f, "utf-8");
16947
+ }
16948
+ const { compileProject: compileProject2 } = await Promise.resolve().then(() => (init_project_compiler(), project_compiler_exports));
16949
+ const { decompileProjectEnhanced: decompileProjectEnhanced2 } = await Promise.resolve().then(() => (init_project_decompiler(), project_decompiler_exports));
16950
+ process.stdout.write(` Compiling project...`);
16951
+ const t0 = performance.now();
16952
+ let ir1;
16953
+ let childDefs1;
16954
+ try {
16955
+ const result = compileProject2(fileMap);
16956
+ ir1 = result.ir;
16957
+ childDefs1 = result.childDefinitions;
16958
+ const compileErrors = result.errors.filter((e) => e.severity === "error");
16959
+ if (compileErrors.length > 0) {
16960
+ console.log(` FAIL`);
16961
+ for (const err of compileErrors) {
16962
+ console.error(` ${err.file}: ${err.message}`);
16963
+ }
16964
+ process.exit(1);
16965
+ }
16966
+ } catch (err) {
16967
+ console.log(` FAIL`);
16968
+ console.error(` ${err.message}`);
16969
+ process.exit(1);
16970
+ }
16971
+ const t1 = performance.now();
16972
+ console.log(` \u2713 (${((t1 - t0) / 1e3).toFixed(1)}s)`);
16973
+ process.stdout.write(` Decompiling IR...`);
16974
+ const t27 = performance.now();
16975
+ let decompFiles;
16976
+ try {
16977
+ const decompInput = {
16978
+ ...ir1,
16979
+ childDefinitions: childDefs1,
16980
+ experience: ir1.experience
16981
+ };
16982
+ const decompResult = decompileProjectEnhanced2(decompInput);
16983
+ decompFiles = {};
16984
+ for (const file2 of decompResult.files) {
16985
+ decompFiles[file2.path] = file2.content;
16986
+ }
16987
+ } catch (err) {
16988
+ console.log(` FAIL`);
16989
+ console.error(` ${err.message}`);
16990
+ process.exit(1);
16991
+ }
16992
+ const t32 = performance.now();
16993
+ console.log(` \u2713 (${((t32 - t27) / 1e3).toFixed(1)}s)`);
16994
+ process.stdout.write(` Recompiling...`);
16995
+ const t42 = performance.now();
16996
+ let ir2;
16997
+ try {
16998
+ const result2 = compileProject2(decompFiles);
16999
+ ir2 = result2.ir;
17000
+ const recompileErrors = result2.errors.filter((e) => e.severity === "error");
17001
+ if (recompileErrors.length > 0) {
17002
+ console.log(` FAIL`);
17003
+ for (const err of recompileErrors) {
17004
+ console.error(` ${err.file}: ${err.message}`);
17005
+ }
17006
+ process.exit(1);
17007
+ }
17008
+ } catch (err) {
17009
+ console.log(` FAIL`);
17010
+ console.error(` ${err.message}`);
17011
+ process.exit(1);
17012
+ }
17013
+ const t52 = performance.now();
17014
+ console.log(` \u2713 (${((t52 - t42) / 1e3).toFixed(1)}s)`);
17015
+ process.stdout.write(` Comparing IR...
17016
+ `);
17017
+ const t62 = performance.now();
17018
+ const diffs = [];
17019
+ if (ir1.slug !== ir2.slug) {
17020
+ diffs.push({ path: "slug", expected: ir1.slug, actual: ir2.slug, severity: "error" });
17021
+ }
17022
+ if (ir1.version !== ir2.version) {
17023
+ diffs.push({ path: "version", expected: ir1.version, actual: ir2.version, severity: "error" });
17024
+ }
17025
+ if (JSON.stringify(ir1.category) !== JSON.stringify(ir2.category)) {
17026
+ diffs.push({ path: "category", expected: ir1.category, actual: ir2.category, severity: "error" });
17027
+ }
17028
+ const { fieldMatch, fieldCount } = compareFields(diffs, ir1.fields, ir2.fields);
17029
+ const fieldOk = diffs.filter((d) => d.path.startsWith("fields") && d.severity === "error").length === 0;
17030
+ console.log(` Fields: ${fieldMatch}/${fieldCount} match ${fieldOk ? "\u2713" : "FAIL"}`);
17031
+ const { stateMatch, stateCount } = compareStates(diffs, ir1.states, ir2.states);
17032
+ const stateOk = diffs.filter((d) => d.path.startsWith("states") && d.severity === "error").length === 0;
17033
+ console.log(` States: ${stateMatch}/${stateCount} match ${stateOk ? "\u2713" : "FAIL"}`);
17034
+ const { transitionMatch, transitionCount } = compareTransitions(diffs, ir1.transitions, ir2.transitions);
17035
+ const transitionOk = diffs.filter((d) => d.path.startsWith("transitions") && d.severity === "error").length === 0;
17036
+ console.log(` Transitions: ${transitionMatch}/${transitionCount} match ${transitionOk ? "\u2713" : "FAIL"}`);
17037
+ const t72 = performance.now();
17038
+ const errors = diffs.filter((d) => d.severity === "error");
17039
+ const success = errors.length === 0;
17040
+ if (success) {
17041
+ console.log(` PASS \u2014 round-trip produces identical IR`);
17042
+ } else {
17043
+ console.log(` FAIL \u2014 ${errors.length} difference(s) found:`);
17044
+ for (const diff of errors) {
17045
+ console.log(` ${diff.path}: expected ${JSON.stringify(diff.expected)}, got ${JSON.stringify(diff.actual)}`);
17046
+ }
17047
+ }
17048
+ return {
17049
+ success,
17050
+ fieldCount,
17051
+ fieldMatch,
17052
+ stateCount,
17053
+ stateMatch,
17054
+ transitionCount,
17055
+ transitionMatch,
17056
+ diffs,
17057
+ timings: {
17058
+ compile: t1 - t0,
17059
+ decompile: t32 - t27,
17060
+ recompile: t52 - t42,
17061
+ compare: t72 - t62
17062
+ }
17063
+ };
17064
+ }
17065
+ function compareFields(diffs, fields1, fields2) {
17066
+ const map1 = new Map(fields1.map((f) => [f.name, f]));
17067
+ const map2 = new Map(fields2.map((f) => [f.name, f]));
17068
+ const fieldCount = map1.size;
17069
+ let fieldMatch = 0;
17070
+ for (const [name, f1] of map1) {
17071
+ const f2 = map2.get(name);
17072
+ if (!f2) {
17073
+ diffs.push({ path: `fields[${name}]`, expected: name, actual: void 0, severity: "error" });
17074
+ continue;
17075
+ }
17076
+ if (f1.type !== f2.type && !isCompatibleType(f1.type, f2.type)) {
17077
+ diffs.push({ path: `fields[${name}].type`, expected: f1.type, actual: f2.type, severity: "warning" });
18055
17078
  }
18056
- return normalizeApiResponse(data);
18057
- } catch {
18058
- return null;
17079
+ fieldMatch++;
18059
17080
  }
18060
- }
18061
- function normalizeApiResponse(def) {
18062
- const ir = {
18063
- slug: def.slug,
18064
- name: def.name,
18065
- version: def.version || "1.0.0",
18066
- description: def.description || "",
18067
- category: def.category || "workflow",
18068
- fields: def.fields || [],
18069
- states: def.states || [],
18070
- transitions: def.transitions || [],
18071
- roles: def.roles || [],
18072
- on_event: def.on_event || [],
18073
- metadata: {
18074
- ...def.metadata || {}
17081
+ for (const [name] of map2) {
17082
+ if (!map1.has(name)) {
17083
+ diffs.push({ path: `fields[${name}]`, expected: void 0, actual: name, severity: "warning" });
18075
17084
  }
18076
- };
18077
- const experience = def.view || def.experience || def.metadata?.experience;
18078
- if (experience) {
18079
- ir.experience = experience;
18080
- ir.metadata.experience = experience;
18081
- ir.view = experience;
18082
17085
  }
18083
- return ir;
17086
+ return { fieldMatch, fieldCount };
18084
17087
  }
18085
- var import_fs11, import_path9;
18086
- var init_pull = __esm({
18087
- "src/cli/pull.ts"() {
18088
- "use strict";
18089
- import_fs11 = require("fs");
18090
- import_path9 = require("path");
18091
- init_project_decompiler();
18092
- }
18093
- });
18094
-
18095
- // src/cli/server.ts
18096
- var server_exports = {};
18097
- __export(server_exports, {
18098
- findServerBinary: () => findServerBinary,
18099
- generateServerToml: () => generateServerToml,
18100
- serverConfig: () => serverConfig,
18101
- serverInit: () => serverInit,
18102
- serverMigrate: () => serverMigrate,
18103
- serverStart: () => serverStart,
18104
- serverStatus: () => serverStatus
18105
- });
18106
- function findServerBinary(explicitPath) {
18107
- if (explicitPath) {
18108
- return (0, import_fs12.existsSync)(explicitPath) ? explicitPath : null;
18109
- }
18110
- for (const rel of BINARY_SEARCH_PATHS) {
18111
- const abs = (0, import_path10.resolve)(process.cwd(), rel);
18112
- if ((0, import_fs12.existsSync)(abs)) return abs;
18113
- }
18114
- try {
18115
- const { execSync: execSync3 } = require("child_process");
18116
- const result = execSync3("which mm-server 2>/dev/null || which mm-api 2>/dev/null", {
18117
- encoding: "utf-8",
18118
- timeout: 5e3
18119
- }).trim();
18120
- if (result) return result;
18121
- } catch {
18122
- }
18123
- return null;
17088
+ function isCompatibleType(t1, t27) {
17089
+ const stringTypes = /* @__PURE__ */ new Set(["text", "rich_text", "email", "url", "phone", "color", "select"]);
17090
+ const numberTypes = /* @__PURE__ */ new Set(["number", "currency", "percentage", "rating", "duration"]);
17091
+ if (stringTypes.has(t1) && stringTypes.has(t27)) return true;
17092
+ if (numberTypes.has(t1) && numberTypes.has(t27)) return true;
17093
+ return false;
18124
17094
  }
18125
- async function serverStart(options) {
18126
- const binary = findServerBinary(options.binary);
18127
- if (!binary) {
18128
- console.error("[mmrc] Error: MindMatrix server binary not found.\n");
18129
- console.error("To get the server binary, choose one of:");
18130
- console.error("");
18131
- console.error(" 1. Build from source:");
18132
- console.error(" cd engine && cargo build --release -p mm-api");
18133
- console.error("");
18134
- console.error(" 2. Docker:");
18135
- console.error(" docker pull mindmatrix/mm-server:latest");
18136
- console.error(" docker run -p 4200:4200 mindmatrix/mm-server:latest");
18137
- console.error("");
18138
- console.error(" 3. Download from releases:");
18139
- console.error(" https://github.com/mindmatrix/mm-server/releases");
18140
- console.error("");
18141
- console.error(" 4. Specify path directly:");
18142
- console.error(" mmrc server start --binary /path/to/mm-server");
18143
- process.exit(1);
17095
+ function compareStates(diffs, states1, states2) {
17096
+ const map1 = new Map(states1.map((s) => [s.name, s]));
17097
+ const map2 = new Map(states2.map((s) => [s.name, s]));
17098
+ const stateCount = map1.size;
17099
+ let stateMatch = 0;
17100
+ for (const [name, s1] of map1) {
17101
+ const s2 = map2.get(name);
17102
+ const hasActions = s1.on_enter.length > 0 || s1.on_exit.length > 0 || s1.during.length > 0;
17103
+ if (!s2 && hasActions) {
17104
+ diffs.push({ path: `states[${name}]`, expected: name, actual: void 0, severity: "error" });
17105
+ continue;
17106
+ }
17107
+ if (!s2) {
17108
+ stateMatch++;
17109
+ continue;
17110
+ }
17111
+ let matched = true;
17112
+ if (s1.on_enter.length !== s2.on_enter.length) {
17113
+ diffs.push({
17114
+ path: `states[${name}].on_enter.length`,
17115
+ expected: s1.on_enter.length,
17116
+ actual: s2.on_enter.length,
17117
+ severity: s1.on_enter.length > 0 ? "error" : "warning"
17118
+ });
17119
+ if (s1.on_enter.length > 0) matched = false;
17120
+ }
17121
+ if (s1.on_exit.length !== s2.on_exit.length) {
17122
+ diffs.push({
17123
+ path: `states[${name}].on_exit.length`,
17124
+ expected: s1.on_exit.length,
17125
+ actual: s2.on_exit.length,
17126
+ severity: s1.on_exit.length > 0 ? "error" : "warning"
17127
+ });
17128
+ if (s1.on_exit.length > 0) matched = false;
17129
+ }
17130
+ if (s1.during.length !== s2.during.length) {
17131
+ diffs.push({
17132
+ path: `states[${name}].during.length`,
17133
+ expected: s1.during.length,
17134
+ actual: s2.during.length,
17135
+ severity: s1.during.length > 0 ? "error" : "warning"
17136
+ });
17137
+ if (s1.during.length > 0) matched = false;
17138
+ }
17139
+ if (matched) stateMatch++;
18144
17140
  }
18145
- console.log(`[mmrc] Starting server: ${binary}`);
18146
- const args2 = [];
18147
- const configPath = options.config ?? findConfigFile();
18148
- if (configPath) {
18149
- args2.push("--config", configPath);
18150
- console.log(`[mmrc] Config: ${configPath}`);
18151
- }
18152
- if (options.port) {
18153
- process.env.PORT = String(options.port);
18154
- console.log(`[mmrc] Port: ${options.port}`);
18155
- }
18156
- if (options.migrate) {
18157
- console.log("[mmrc] Running migrations...");
18158
- process.env.MM_RUN_MIGRATIONS = "true";
18159
- }
18160
- const { spawn } = require("child_process");
18161
- const child = spawn(binary, args2, {
18162
- stdio: "inherit",
18163
- env: { ...process.env, MM_CONFIG_PATH: configPath ?? "" }
18164
- });
18165
- child.on("error", (err) => {
18166
- console.error(`[mmrc] Failed to start server: ${err.message}`);
18167
- process.exit(1);
18168
- });
18169
- child.on("exit", (code) => {
18170
- process.exit(code ?? 0);
18171
- });
18172
- const forward = (signal) => {
18173
- process.on(signal, () => child.kill(signal));
18174
- };
18175
- forward("SIGINT");
18176
- forward("SIGTERM");
17141
+ return { stateMatch, stateCount };
18177
17142
  }
18178
- async function serverMigrate(options) {
18179
- const apiUrl = options.apiUrl ?? "http://localhost:4200";
18180
- try {
18181
- console.log(`[mmrc] Requesting migration on ${apiUrl}...`);
18182
- const res = await fetch(`${apiUrl}/api/v1/admin/migrate`, {
18183
- method: "POST",
18184
- headers: { "Content-Type": "application/json" }
17143
+ function compareTransitions(diffs, transitions1, transitions2) {
17144
+ const map1 = new Map(transitions1.map((t27) => [t27.name, t27]));
17145
+ const map2 = new Map(transitions2.map((t27) => [t27.name, t27]));
17146
+ const transitionCount = map1.size;
17147
+ let transitionMatch = 0;
17148
+ if (transitions1.length !== transitions2.length) {
17149
+ diffs.push({
17150
+ path: "transitions.length",
17151
+ expected: transitions1.length,
17152
+ actual: transitions2.length,
17153
+ severity: "error"
18185
17154
  });
18186
- if (res.ok) {
18187
- const data = await res.json();
18188
- console.log(`[mmrc] Migration complete: ${JSON.stringify(data)}`);
18189
- return;
17155
+ }
17156
+ for (const [name, t1] of map1) {
17157
+ const t27 = map2.get(name);
17158
+ if (!t27) {
17159
+ diffs.push({ path: `transitions[${name}]`, expected: name, actual: void 0, severity: "error" });
17160
+ continue;
18190
17161
  }
18191
- if (res.status !== 404) {
18192
- console.warn(`[mmrc] Migration request failed: ${res.status}`);
17162
+ let matched = true;
17163
+ if (t1.to !== t27.to) {
17164
+ diffs.push({ path: `transitions[${name}].to`, expected: t1.to, actual: t27.to, severity: "error" });
17165
+ matched = false;
18193
17166
  }
18194
- } catch {
17167
+ const from1 = [...t1.from].sort();
17168
+ const from2 = [...t27.from].sort();
17169
+ if (JSON.stringify(from1) !== JSON.stringify(from2)) {
17170
+ diffs.push({ path: `transitions[${name}].from`, expected: from1, actual: from2, severity: "error" });
17171
+ matched = false;
17172
+ }
17173
+ const roles1 = [...t1.roles ?? []].sort();
17174
+ const roles2 = [...t27.roles ?? []].sort();
17175
+ if (JSON.stringify(roles1) !== JSON.stringify(roles2)) {
17176
+ diffs.push({ path: `transitions[${name}].roles`, expected: roles1, actual: roles2, severity: "error" });
17177
+ matched = false;
17178
+ }
17179
+ const req1 = [...t1.required_fields ?? []].sort();
17180
+ const req2 = [...t27.required_fields ?? []].sort();
17181
+ if (JSON.stringify(req1) !== JSON.stringify(req2)) {
17182
+ diffs.push({ path: `transitions[${name}].required_fields`, expected: req1, actual: req2, severity: "error" });
17183
+ matched = false;
17184
+ }
17185
+ if (!!t1.auto !== !!t27.auto) {
17186
+ diffs.push({ path: `transitions[${name}].auto`, expected: t1.auto, actual: t27.auto, severity: "error" });
17187
+ matched = false;
17188
+ }
17189
+ if (matched) transitionMatch++;
18195
17190
  }
18196
- const binary = findServerBinary();
18197
- if (!binary) {
18198
- console.error("[mmrc] Error: Server not running and binary not found.");
18199
- console.error(" Start the server first: mmrc server start");
18200
- console.error(" Or build from source: cd engine && cargo build --release -p mm-api");
18201
- process.exit(1);
17191
+ return { transitionMatch, transitionCount };
17192
+ }
17193
+
17194
+ // src/cli/deploy.ts
17195
+ var import_glob3 = require("glob");
17196
+ var import_fs4 = require("fs");
17197
+ var import_path3 = require("path");
17198
+ async function deploy(options) {
17199
+ const dir = options.dir ?? "dist/workflows";
17200
+ const targetLabel = options.targetName ? ` (target: ${options.targetName})` : "";
17201
+ console.log(`[mindmatrix-react] Deploying workflows from ${dir} to ${options.apiUrl}${targetLabel}...`);
17202
+ const files = await (0, import_glob3.glob)(`${dir}/**/*.workflow.json`);
17203
+ if (files.length === 0) {
17204
+ console.log(`[mindmatrix-react] No workflow files found in ${dir}`);
17205
+ return { created: [], updated: [], versioned: [], skipped: [], failed: [] };
18202
17206
  }
18203
- console.log(`[mmrc] Running migrations via ${binary}...`);
18204
- const { execSync: execSync3 } = require("child_process");
18205
- try {
18206
- const configPath = options.config ?? findConfigFile();
18207
- const env = { ...process.env, MM_RUN_MIGRATIONS: "true" };
18208
- if (configPath) env.MM_CONFIG_PATH = configPath;
18209
- execSync3(`${binary} --migrate-only`, { stdio: "inherit", env });
18210
- console.log("[mmrc] Migrations complete.");
18211
- } catch (e) {
18212
- console.error(`[mmrc] Migration failed: ${e.message}`);
18213
- process.exit(1);
17207
+ const result = {
17208
+ created: [],
17209
+ updated: [],
17210
+ versioned: [],
17211
+ skipped: [],
17212
+ failed: []
17213
+ };
17214
+ const deployedIRs = [];
17215
+ for (const file2 of files) {
17216
+ try {
17217
+ const ir = JSON.parse((0, import_fs4.readFileSync)(file2, "utf-8"));
17218
+ const slug = ir.slug;
17219
+ if (options.dryRun) {
17220
+ console.log(` [dry-run] Would deploy ${slug} (${(0, import_path3.basename)(file2)})`);
17221
+ continue;
17222
+ }
17223
+ const existing = await fetchExistingDefinition(options.apiUrl, options.token, slug);
17224
+ if (!existing) {
17225
+ await createDefinition(options.apiUrl, options.token, ir);
17226
+ console.log(` \u2713 ${slug} (created)`);
17227
+ result.created.push(slug);
17228
+ } else if (existing.instanceCount === 0 || options.force) {
17229
+ await updateDefinition(options.apiUrl, options.token, existing.id, ir);
17230
+ console.log(` \u2713 ${slug} (updated)`);
17231
+ result.updated.push(slug);
17232
+ } else {
17233
+ const newVersion = bumpVersion(existing.version);
17234
+ ir.version = newVersion;
17235
+ await createDefinition(options.apiUrl, options.token, ir);
17236
+ console.log(` \u2713 ${slug} v${newVersion} (new version, ${existing.instanceCount} instances on v${existing.version})`);
17237
+ result.versioned.push(slug);
17238
+ }
17239
+ deployedIRs.push(ir);
17240
+ } catch (error) {
17241
+ const slug = (0, import_path3.basename)(file2).replace(".workflow.json", "");
17242
+ const msg = error.message;
17243
+ console.error(` \u2717 ${slug}: ${msg}`);
17244
+ result.failed.push({ slug, error: msg });
17245
+ }
18214
17246
  }
18215
- }
18216
- function serverInit(options) {
18217
- const output = options.output ?? "mm-server.toml";
18218
- const mode = options.mode ?? "standalone";
18219
- const db = options.db ?? "$env:DATABASE_URL";
18220
- if ((0, import_fs12.existsSync)(output) && output !== "/dev/stdout") {
18221
- console.error(`[mmrc] Error: ${output} already exists. Use --output to specify a different path.`);
18222
- process.exit(1);
17247
+ if (!options.dryRun) {
17248
+ for (const ir of deployedIRs) {
17249
+ const serviceResult = await syncServices(options.apiUrl, options.token, ir);
17250
+ if (serviceResult) {
17251
+ if (!result.services) {
17252
+ result.services = { registered: [], updated: [], failed: [] };
17253
+ }
17254
+ result.services.registered.push(...serviceResult.registered);
17255
+ result.services.updated.push(...serviceResult.updated);
17256
+ result.services.failed.push(...serviceResult.failed);
17257
+ }
17258
+ }
18223
17259
  }
18224
- const content = generateServerToml(mode, db);
18225
- (0, import_fs12.writeFileSync)(output, content, "utf-8");
18226
- console.log(`[mmrc] Generated ${output} (mode: ${mode})`);
18227
- console.log(`[mmrc] Edit the file, then run: mmrc server start`);
18228
- }
18229
- function generateServerToml(mode = "standalone", db = "$env:DATABASE_URL") {
18230
- const lines = [];
18231
- lines.push(`# MindMatrix Server Configuration`);
18232
- lines.push(`# Generated by: mmrc server init --mode ${mode}`);
18233
- lines.push(`# Docs: https://docs.mindmatrix.dev/server/configuration`);
18234
- lines.push(`#`);
18235
- lines.push(`# Environment variables can be referenced as $env:VAR_NAME`);
18236
- lines.push(``);
18237
- lines.push(`[server]`);
18238
- lines.push(`# Server mode: "standalone" (all-in-one), "gateway" (API + routing), "worker" (action execution)`);
18239
- lines.push(`mode = "${mode}"`);
18240
- lines.push(`# Listen port (also configurable via PORT env var)`);
18241
- lines.push(`port = 4200`);
18242
- lines.push(`# Bind address`);
18243
- lines.push(`host = "0.0.0.0"`);
18244
- lines.push(``);
18245
- lines.push(`[database]`);
18246
- lines.push(`# PostgreSQL connection URL`);
18247
- lines.push(`url = "${db}"`);
18248
- lines.push(`# Connection pool size`);
18249
- lines.push(`max_connections = 10`);
18250
- lines.push(``);
18251
- lines.push(`[auth]`);
18252
- lines.push(`# JWT signing secret (also configurable via JWT_SECRET env var)`);
18253
- lines.push(`jwt_secret = "$env:JWT_SECRET"`);
18254
- lines.push(``);
18255
- lines.push(`[telemetry]`);
18256
- lines.push(`# Log output format: "pretty" (human-readable) or "json" (structured)`);
18257
- lines.push(`log_format = "pretty"`);
18258
- lines.push(`# Log level filter (RUST_LOG syntax)`);
18259
- lines.push(`log_level = "mm_api=info,mm_storage=info,mm_engine=info"`);
18260
- lines.push(`# OpenTelemetry OTLP endpoint (optional \u2014 enables distributed tracing)`);
18261
- lines.push(`# otlp_endpoint = "http://localhost:4317"`);
18262
- lines.push(``);
18263
- if (mode === "gateway") {
18264
- lines.push(`[gateway]`);
18265
- lines.push(`# Worker pool for routing action execution`);
18266
- lines.push(`pool = "default"`);
18267
- lines.push(`# Maximum concurrent actions per worker`);
18268
- lines.push(`max_concurrent = 16`);
18269
- lines.push(`# Worker heartbeat interval`);
18270
- lines.push(`heartbeat = "30s"`);
18271
- lines.push(``);
17260
+ if (options.reportFile) {
17261
+ (0, import_fs4.writeFileSync)(options.reportFile, JSON.stringify(result, null, 2), "utf-8");
17262
+ console.log(`
17263
+ Report written to ${options.reportFile}`);
18272
17264
  }
18273
- if (mode !== "standalone") {
18274
- lines.push(`[redis]`);
18275
- lines.push(`# Redis URL for cross-instance communication`);
18276
- lines.push(`url = "$env:REDIS_URL"`);
18277
- lines.push(``);
17265
+ const total = result.created.length + result.updated.length + result.versioned.length;
17266
+ console.log(
17267
+ `
17268
+ [mindmatrix-react] Deployed ${total} workflows (${result.created.length} created, ${result.updated.length} updated, ${result.versioned.length} versioned, ${result.failed.length} failed)`
17269
+ );
17270
+ if (result.services) {
17271
+ const svcTotal = result.services.registered.length + result.services.updated.length;
17272
+ if (svcTotal > 0 || result.services.failed.length > 0) {
17273
+ console.log(
17274
+ `[mindmatrix-react] Synced ${svcTotal} services (${result.services.registered.length} registered, ${result.services.updated.length} updated, ${result.services.failed.length} failed)`
17275
+ );
17276
+ }
18278
17277
  }
18279
- lines.push(`[secrets]`);
18280
- lines.push(`# Secret provider: "env" (environment variables) or "vault" (HashiCorp Vault)`);
18281
- lines.push(`provider = "env"`);
18282
- lines.push(``);
18283
- lines.push(`# \u2500\u2500 Services \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
18284
- lines.push(`# Register external services for action routing.`);
18285
- lines.push(`# Each [[services]] block declares a service that can handle workflow actions.`);
18286
- lines.push(`#`);
18287
- lines.push(`# [[services]]`);
18288
- lines.push(`# name = "email"`);
18289
- lines.push(`# type = "webhook"`);
18290
- lines.push(`# url = "https://hooks.example.com/email"`);
18291
- lines.push(`# actions = ["send_email", "send_sms"]`);
18292
- lines.push(`# [services.labels]`);
18293
- lines.push(`# tier = "essential"`);
18294
- lines.push(``);
18295
- return lines.join("\n");
17278
+ return result;
18296
17279
  }
18297
- async function serverStatus(options) {
18298
- const apiUrl = options.apiUrl ?? "http://localhost:4200";
18299
- console.log(`[mmrc] Checking server at ${apiUrl}...`);
17280
+ async function fetchExistingDefinition(apiUrl, token, slug) {
18300
17281
  try {
18301
- const res = await fetch(`${apiUrl}/health`, { signal: AbortSignal.timeout(5e3) });
18302
- if (!res.ok) {
18303
- const result2 = {
18304
- reachable: true,
18305
- status: `unhealthy (HTTP ${res.status})`
18306
- };
18307
- console.log(`[mmrc] Server: ${result2.status}`);
18308
- return result2;
18309
- }
17282
+ const res = await fetch(`${apiUrl}/workflow/definitions?slug=${encodeURIComponent(slug)}`, {
17283
+ headers: { Authorization: `Bearer ${token}` }
17284
+ });
17285
+ if (!res.ok) return null;
18310
17286
  const data = await res.json();
18311
- const result = {
18312
- reachable: true,
18313
- status: data.status ?? "ok",
18314
- version: data.version,
18315
- uptime: data.uptime,
18316
- database: data.database ?? data.db ?? void 0
17287
+ const definitions = Array.isArray(data) ? data : data.items ?? data.data;
17288
+ if (!definitions || definitions.length === 0) return null;
17289
+ const def = definitions[0];
17290
+ return {
17291
+ id: def.id,
17292
+ slug: def.slug,
17293
+ version: def.version || "0.1.0",
17294
+ instanceCount: def.instanceCount ?? def.instance_count ?? 0
18317
17295
  };
18318
- console.log(`[mmrc] Server: ${result.status}`);
18319
- if (result.version) console.log(`[mmrc] Version: ${result.version}`);
18320
- if (result.uptime) console.log(`[mmrc] Uptime: ${result.uptime}`);
18321
- if (result.database) console.log(`[mmrc] Database: ${result.database}`);
18322
- return result;
18323
- } catch (e) {
18324
- const error = e.message;
18325
- const result = { reachable: false, error };
18326
- console.error(`[mmrc] Server unreachable: ${error}`);
18327
- console.error(`[mmrc] Is the server running? Try: mmrc server start`);
18328
- return result;
17296
+ } catch {
17297
+ return null;
18329
17298
  }
18330
17299
  }
18331
- function serverConfig(options) {
18332
- const configPath = options.config ?? findConfigFile();
18333
- if (!configPath) {
18334
- console.log("[mmrc] No mm-server.toml found.");
18335
- console.log("[mmrc] Generate one with: mmrc server init");
18336
- return;
18337
- }
18338
- console.log(`[mmrc] Config file: ${configPath}
18339
- `);
18340
- const raw = (0, import_fs12.readFileSync)(configPath, "utf-8");
18341
- const resolved = raw.replace(/\$env:(\w+)/g, (_match, varName) => {
18342
- const val = process.env[varName];
18343
- return val ? `${val} # \u2190 $env:${varName}` : `(unset) # \u2190 $env:${varName}`;
17300
+ async function createDefinition(apiUrl, token, ir) {
17301
+ const res = await fetch(`${apiUrl}/workflow/definitions`, {
17302
+ method: "POST",
17303
+ headers: {
17304
+ "Content-Type": "application/json",
17305
+ Authorization: `Bearer ${token}`
17306
+ },
17307
+ body: JSON.stringify(ir)
18344
17308
  });
18345
- console.log(resolved);
18346
- }
18347
- function findConfigFile() {
18348
- const candidates = [
18349
- "mm-server.toml",
18350
- "engine/mm-server.toml",
18351
- "mm-core/mm-server.toml"
18352
- ];
18353
- for (const c of candidates) {
18354
- const abs = (0, import_path10.resolve)(process.cwd(), c);
18355
- if ((0, import_fs12.existsSync)(abs)) return abs;
17309
+ if (!res.ok) {
17310
+ const errorText = await res.text();
17311
+ throw new Error(`Create failed: ${res.status} ${errorText}`);
18356
17312
  }
18357
- return null;
18358
17313
  }
18359
- var import_fs12, import_path10, BINARY_SEARCH_PATHS;
18360
- var init_server = __esm({
18361
- "src/cli/server.ts"() {
18362
- "use strict";
18363
- import_fs12 = require("fs");
18364
- import_path10 = require("path");
18365
- BINARY_SEARCH_PATHS = [
18366
- "engine/target/release/mm-server",
18367
- "engine/target/debug/mm-server",
18368
- "mm-core/target/release/mm-server",
18369
- "mm-core/target/debug/mm-server",
18370
- "target/release/mm-server",
18371
- "target/debug/mm-server"
18372
- ];
18373
- }
18374
- });
18375
-
18376
- // src/cli/index.ts
18377
- init_build();
18378
-
18379
- // src/cli/test.ts
18380
- var import_glob2 = require("glob");
18381
- var import_fs3 = require("fs");
18382
- var import_path2 = require("path");
18383
- async function test(options = {}) {
18384
- const srcDir = options.src ?? ".";
18385
- const label = "[mindmatrix-react]";
18386
- console.log(`${label} Round-trip fidelity test
18387
- `);
18388
- const configPath = (0, import_path2.resolve)(srcDir, "mm.config.ts");
18389
- const isProject = (0, import_fs3.existsSync)(configPath);
18390
- if (!isProject) {
18391
- console.error(`${label} Error: No mm.config.ts found in ${srcDir}`);
18392
- console.error(` Round-trip test requires a project with mm.config.ts`);
18393
- process.exit(1);
18394
- }
18395
- const allFiles = await (0, import_glob2.glob)(`${srcDir}/**/*.{ts,tsx}`, {
18396
- ignore: ["**/node_modules/**", "**/dist/**", "**/*.test.*"]
17314
+ async function updateDefinition(apiUrl, token, id, ir) {
17315
+ const { slug: _slug, ...updatePayload } = ir;
17316
+ const res = await fetch(`${apiUrl}/workflow/definitions/${id}`, {
17317
+ method: "PATCH",
17318
+ headers: {
17319
+ "Content-Type": "application/json",
17320
+ Authorization: `Bearer ${token}`
17321
+ },
17322
+ body: JSON.stringify(updatePayload)
18397
17323
  });
18398
- if (allFiles.length === 0) {
18399
- console.error(`${label} Error: No source files found in ${srcDir}`);
18400
- process.exit(1);
18401
- }
18402
- const fileMap = {};
18403
- for (const f of allFiles) {
18404
- const rel = f.startsWith(srcDir + "/") ? f.slice(srcDir.length + 1) : f;
18405
- fileMap[rel] = (0, import_fs3.readFileSync)(f, "utf-8");
17324
+ if (!res.ok) {
17325
+ const errorText = await res.text();
17326
+ throw new Error(`Update failed: ${res.status} ${errorText}`);
18406
17327
  }
18407
- const { compileProject: compileProject2 } = await Promise.resolve().then(() => (init_project_compiler(), project_compiler_exports));
18408
- const { decompileProjectEnhanced: decompileProjectEnhanced2 } = await Promise.resolve().then(() => (init_project_decompiler(), project_decompiler_exports));
18409
- process.stdout.write(` Compiling project...`);
18410
- const t0 = performance.now();
18411
- let ir1;
18412
- let childDefs1;
18413
- try {
18414
- const result = compileProject2(fileMap);
18415
- ir1 = result.ir;
18416
- childDefs1 = result.childDefinitions;
18417
- const compileErrors = result.errors.filter((e) => e.severity === "error");
18418
- if (compileErrors.length > 0) {
18419
- console.log(` FAIL`);
18420
- for (const err of compileErrors) {
18421
- console.error(` ${err.file}: ${err.message}`);
17328
+ }
17329
+ function bumpVersion(version) {
17330
+ const parts = version.split(".");
17331
+ if (parts.length !== 3) return `${version}.1`;
17332
+ const patch = parseInt(parts[2], 10) || 0;
17333
+ return `${parts[0]}.${parts[1]}.${patch + 1}`;
17334
+ }
17335
+ async function syncServices(apiUrl, token, ir) {
17336
+ const metadata = ir.metadata;
17337
+ const orchestration = metadata?.orchestration;
17338
+ const services = orchestration?.services;
17339
+ if (!services || Object.keys(services).length === 0) return null;
17340
+ const result = { registered: [], updated: [], failed: [] };
17341
+ for (const [name, config] of Object.entries(services)) {
17342
+ try {
17343
+ const registration = {
17344
+ name,
17345
+ connection: {
17346
+ type: config.type || "webhook",
17347
+ url: config.url,
17348
+ queue: config.queue
17349
+ },
17350
+ actions: config.actions || [],
17351
+ labels: config.labels || {}
17352
+ };
17353
+ const existing = await fetchExistingService(apiUrl, token, name);
17354
+ if (existing) {
17355
+ await fetch(`${apiUrl}/services/${existing.id}`, {
17356
+ method: "PATCH",
17357
+ headers: {
17358
+ "Content-Type": "application/json",
17359
+ Authorization: `Bearer ${token}`
17360
+ },
17361
+ body: JSON.stringify(registration)
17362
+ });
17363
+ console.log(` \u2713 service: ${name} (updated)`);
17364
+ result.updated.push(name);
17365
+ } else {
17366
+ const res = await fetch(`${apiUrl}/services`, {
17367
+ method: "POST",
17368
+ headers: {
17369
+ "Content-Type": "application/json",
17370
+ Authorization: `Bearer ${token}`
17371
+ },
17372
+ body: JSON.stringify(registration)
17373
+ });
17374
+ if (!res.ok) {
17375
+ const errorText = await res.text();
17376
+ throw new Error(`Register failed: ${res.status} ${errorText}`);
17377
+ }
17378
+ console.log(` \u2713 service: ${name} (registered)`);
17379
+ result.registered.push(name);
18422
17380
  }
18423
- process.exit(1);
17381
+ } catch (error) {
17382
+ const msg = error.message;
17383
+ console.error(` \u2717 service: ${name}: ${msg}`);
17384
+ result.failed.push(name);
18424
17385
  }
18425
- } catch (err) {
18426
- console.log(` FAIL`);
18427
- console.error(` ${err.message}`);
18428
- process.exit(1);
18429
17386
  }
18430
- const t1 = performance.now();
18431
- console.log(` \u2713 (${((t1 - t0) / 1e3).toFixed(1)}s)`);
18432
- process.stdout.write(` Decompiling IR...`);
18433
- const t27 = performance.now();
18434
- let decompFiles;
17387
+ return result;
17388
+ }
17389
+ async function fetchExistingService(apiUrl, token, name) {
18435
17390
  try {
18436
- const decompInput = {
18437
- ...ir1,
18438
- childDefinitions: childDefs1,
18439
- experience: ir1.experience
18440
- };
18441
- const decompResult = decompileProjectEnhanced2(decompInput);
18442
- decompFiles = {};
18443
- for (const file2 of decompResult.files) {
18444
- decompFiles[file2.path] = file2.content;
18445
- }
18446
- } catch (err) {
18447
- console.log(` FAIL`);
18448
- console.error(` ${err.message}`);
18449
- process.exit(1);
17391
+ const res = await fetch(`${apiUrl}/services?name=${encodeURIComponent(name)}`, {
17392
+ headers: { Authorization: `Bearer ${token}` }
17393
+ });
17394
+ if (!res.ok) return null;
17395
+ const data = await res.json();
17396
+ const servicesList = Array.isArray(data) ? data : data.services ?? data.items ?? data.data;
17397
+ if (!servicesList || servicesList.length === 0) return null;
17398
+ const found = servicesList.find((s) => s.name === name);
17399
+ return found ? { id: found.id, name: found.name } : null;
17400
+ } catch {
17401
+ return null;
18450
17402
  }
18451
- const t32 = performance.now();
18452
- console.log(` \u2713 (${((t32 - t27) / 1e3).toFixed(1)}s)`);
18453
- process.stdout.write(` Recompiling...`);
18454
- const t42 = performance.now();
18455
- let ir2;
17403
+ }
17404
+
17405
+ // src/cli/auth.ts
17406
+ var import_fs5 = require("fs");
17407
+ var import_path4 = require("path");
17408
+ var import_os = require("os");
17409
+ var import_readline = require("readline");
17410
+ var MMRC_DIR = (0, import_path4.join)((0, import_os.homedir)(), ".mmrc");
17411
+ var CREDENTIALS_PATH = (0, import_path4.join)(MMRC_DIR, "credentials.json");
17412
+ function loadCredentials() {
17413
+ if (!(0, import_fs5.existsSync)(CREDENTIALS_PATH)) return {};
18456
17414
  try {
18457
- const result2 = compileProject2(decompFiles);
18458
- ir2 = result2.ir;
18459
- const recompileErrors = result2.errors.filter((e) => e.severity === "error");
18460
- if (recompileErrors.length > 0) {
18461
- console.log(` FAIL`);
18462
- for (const err of recompileErrors) {
18463
- console.error(` ${err.file}: ${err.message}`);
18464
- }
18465
- process.exit(1);
18466
- }
18467
- } catch (err) {
18468
- console.log(` FAIL`);
18469
- console.error(` ${err.message}`);
18470
- process.exit(1);
18471
- }
18472
- const t52 = performance.now();
18473
- console.log(` \u2713 (${((t52 - t42) / 1e3).toFixed(1)}s)`);
18474
- process.stdout.write(` Comparing IR...
18475
- `);
18476
- const t62 = performance.now();
18477
- const diffs = [];
18478
- if (ir1.slug !== ir2.slug) {
18479
- diffs.push({ path: "slug", expected: ir1.slug, actual: ir2.slug, severity: "error" });
18480
- }
18481
- if (ir1.version !== ir2.version) {
18482
- diffs.push({ path: "version", expected: ir1.version, actual: ir2.version, severity: "error" });
18483
- }
18484
- if (JSON.stringify(ir1.category) !== JSON.stringify(ir2.category)) {
18485
- diffs.push({ path: "category", expected: ir1.category, actual: ir2.category, severity: "error" });
17415
+ return JSON.parse((0, import_fs5.readFileSync)(CREDENTIALS_PATH, "utf-8"));
17416
+ } catch {
17417
+ return {};
18486
17418
  }
18487
- const { fieldMatch, fieldCount } = compareFields(diffs, ir1.fields, ir2.fields);
18488
- const fieldOk = diffs.filter((d) => d.path.startsWith("fields") && d.severity === "error").length === 0;
18489
- console.log(` Fields: ${fieldMatch}/${fieldCount} match ${fieldOk ? "\u2713" : "FAIL"}`);
18490
- const { stateMatch, stateCount } = compareStates(diffs, ir1.states, ir2.states);
18491
- const stateOk = diffs.filter((d) => d.path.startsWith("states") && d.severity === "error").length === 0;
18492
- console.log(` States: ${stateMatch}/${stateCount} match ${stateOk ? "\u2713" : "FAIL"}`);
18493
- const { transitionMatch, transitionCount } = compareTransitions(diffs, ir1.transitions, ir2.transitions);
18494
- const transitionOk = diffs.filter((d) => d.path.startsWith("transitions") && d.severity === "error").length === 0;
18495
- console.log(` Transitions: ${transitionMatch}/${transitionCount} match ${transitionOk ? "\u2713" : "FAIL"}`);
18496
- const t72 = performance.now();
18497
- const errors = diffs.filter((d) => d.severity === "error");
18498
- const success = errors.length === 0;
18499
- if (success) {
18500
- console.log(` PASS \u2014 round-trip produces identical IR`);
18501
- } else {
18502
- console.log(` FAIL \u2014 ${errors.length} difference(s) found:`);
18503
- for (const diff of errors) {
18504
- console.log(` ${diff.path}: expected ${JSON.stringify(diff.expected)}, got ${JSON.stringify(diff.actual)}`);
18505
- }
17419
+ }
17420
+ function saveCredentials(apiUrl, token) {
17421
+ (0, import_fs5.mkdirSync)(MMRC_DIR, { recursive: true, mode: 448 });
17422
+ const creds = loadCredentials();
17423
+ creds[apiUrl] = { token, savedAt: (/* @__PURE__ */ new Date()).toISOString() };
17424
+ (0, import_fs5.writeFileSync)(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), "utf-8");
17425
+ try {
17426
+ const { chmodSync } = require("fs");
17427
+ chmodSync(CREDENTIALS_PATH, 384);
17428
+ } catch {
18506
17429
  }
18507
- return {
18508
- success,
18509
- fieldCount,
18510
- fieldMatch,
18511
- stateCount,
18512
- stateMatch,
18513
- transitionCount,
18514
- transitionMatch,
18515
- diffs,
18516
- timings: {
18517
- compile: t1 - t0,
18518
- decompile: t32 - t27,
18519
- recompile: t52 - t42,
18520
- compare: t72 - t62
18521
- }
18522
- };
18523
17430
  }
18524
- function compareFields(diffs, fields1, fields2) {
18525
- const map1 = new Map(fields1.map((f) => [f.name, f]));
18526
- const map2 = new Map(fields2.map((f) => [f.name, f]));
18527
- const fieldCount = map1.size;
18528
- let fieldMatch = 0;
18529
- for (const [name, f1] of map1) {
18530
- const f2 = map2.get(name);
18531
- if (!f2) {
18532
- diffs.push({ path: `fields[${name}]`, expected: name, actual: void 0, severity: "error" });
18533
- continue;
18534
- }
18535
- if (f1.type !== f2.type && !isCompatibleType(f1.type, f2.type)) {
18536
- diffs.push({ path: `fields[${name}].type`, expected: f1.type, actual: f2.type, severity: "warning" });
18537
- }
18538
- fieldMatch++;
17431
+ function removeCredentials(apiUrl) {
17432
+ if (!(0, import_fs5.existsSync)(CREDENTIALS_PATH)) return;
17433
+ if (!apiUrl) {
17434
+ (0, import_fs5.unlinkSync)(CREDENTIALS_PATH);
17435
+ return;
18539
17436
  }
18540
- for (const [name] of map2) {
18541
- if (!map1.has(name)) {
18542
- diffs.push({ path: `fields[${name}]`, expected: void 0, actual: name, severity: "warning" });
18543
- }
17437
+ const creds = loadCredentials();
17438
+ delete creds[apiUrl];
17439
+ if (Object.keys(creds).length === 0) {
17440
+ (0, import_fs5.unlinkSync)(CREDENTIALS_PATH);
17441
+ } else {
17442
+ (0, import_fs5.writeFileSync)(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), "utf-8");
18544
17443
  }
18545
- return { fieldMatch, fieldCount };
18546
17444
  }
18547
- function isCompatibleType(t1, t27) {
18548
- const stringTypes = /* @__PURE__ */ new Set(["text", "rich_text", "email", "url", "phone", "color", "select"]);
18549
- const numberTypes = /* @__PURE__ */ new Set(["number", "currency", "percentage", "rating", "duration"]);
18550
- if (stringTypes.has(t1) && stringTypes.has(t27)) return true;
18551
- if (numberTypes.has(t1) && numberTypes.has(t27)) return true;
18552
- return false;
17445
+ function resolveToken(apiUrl, explicitToken) {
17446
+ if (explicitToken) return explicitToken;
17447
+ const envToken = process.env.MMRC_TOKEN;
17448
+ if (envToken) return envToken;
17449
+ const creds = loadCredentials();
17450
+ const entry = creds[apiUrl];
17451
+ if (entry?.token) return entry.token;
17452
+ return null;
18553
17453
  }
18554
- function compareStates(diffs, states1, states2) {
18555
- const map1 = new Map(states1.map((s) => [s.name, s]));
18556
- const map2 = new Map(states2.map((s) => [s.name, s]));
18557
- const stateCount = map1.size;
18558
- let stateMatch = 0;
18559
- for (const [name, s1] of map1) {
18560
- const s2 = map2.get(name);
18561
- const hasActions = s1.on_enter.length > 0 || s1.on_exit.length > 0 || s1.during.length > 0;
18562
- if (!s2 && hasActions) {
18563
- diffs.push({ path: `states[${name}]`, expected: name, actual: void 0, severity: "error" });
18564
- continue;
18565
- }
18566
- if (!s2) {
18567
- stateMatch++;
18568
- continue;
18569
- }
18570
- let matched = true;
18571
- if (s1.on_enter.length !== s2.on_enter.length) {
18572
- diffs.push({
18573
- path: `states[${name}].on_enter.length`,
18574
- expected: s1.on_enter.length,
18575
- actual: s2.on_enter.length,
18576
- severity: s1.on_enter.length > 0 ? "error" : "warning"
17454
+ function prompt(question, hidden = false) {
17455
+ return new Promise((resolve5) => {
17456
+ const rl = (0, import_readline.createInterface)({
17457
+ input: process.stdin,
17458
+ output: process.stdout
17459
+ });
17460
+ if (hidden && process.stdin.isTTY) {
17461
+ const origWrite = process.stdout.write.bind(process.stdout);
17462
+ process.stdout.write = ((chunk, encodingOrCb, cb) => {
17463
+ if (typeof chunk === "string" && chunk.includes(question)) {
17464
+ return origWrite(chunk, encodingOrCb, cb);
17465
+ }
17466
+ if (typeof chunk === "string" && !chunk.includes("\n")) {
17467
+ return true;
17468
+ }
17469
+ return origWrite(chunk, encodingOrCb, cb);
18577
17470
  });
18578
- if (s1.on_enter.length > 0) matched = false;
18579
- }
18580
- if (s1.on_exit.length !== s2.on_exit.length) {
18581
- diffs.push({
18582
- path: `states[${name}].on_exit.length`,
18583
- expected: s1.on_exit.length,
18584
- actual: s2.on_exit.length,
18585
- severity: s1.on_exit.length > 0 ? "error" : "warning"
17471
+ rl.question(question, (answer) => {
17472
+ process.stdout.write = origWrite;
17473
+ console.log();
17474
+ rl.close();
17475
+ resolve5(answer.trim());
18586
17476
  });
18587
- if (s1.on_exit.length > 0) matched = false;
18588
- }
18589
- if (s1.during.length !== s2.during.length) {
18590
- diffs.push({
18591
- path: `states[${name}].during.length`,
18592
- expected: s1.during.length,
18593
- actual: s2.during.length,
18594
- severity: s1.during.length > 0 ? "error" : "warning"
17477
+ } else {
17478
+ rl.question(question, (answer) => {
17479
+ rl.close();
17480
+ resolve5(answer.trim());
18595
17481
  });
18596
- if (s1.during.length > 0) matched = false;
18597
17482
  }
18598
- if (matched) stateMatch++;
17483
+ });
17484
+ }
17485
+ async function login(apiUrl, email, password) {
17486
+ if (!email) {
17487
+ email = await prompt("Email: ");
18599
17488
  }
18600
- return { stateMatch, stateCount };
17489
+ if (!password) {
17490
+ password = await prompt("Password: ", true);
17491
+ }
17492
+ if (!email || !password) {
17493
+ throw new Error("Email and password are required");
17494
+ }
17495
+ console.log(`[mmrc] Logging in to ${apiUrl}...`);
17496
+ const res = await fetch(`${apiUrl}/auth/login`, {
17497
+ method: "POST",
17498
+ headers: { "Content-Type": "application/json" },
17499
+ body: JSON.stringify({ email, password })
17500
+ });
17501
+ if (!res.ok) {
17502
+ const text = await res.text();
17503
+ throw new Error(`Login failed: ${res.status} ${text}`);
17504
+ }
17505
+ const data = await res.json();
17506
+ const token = data.token ?? data.access_token ?? data.accessToken;
17507
+ if (!token) {
17508
+ throw new Error("Login succeeded but no token in response");
17509
+ }
17510
+ saveCredentials(apiUrl, token);
17511
+ const userName = data.name ?? data.user?.name ?? data.user?.email ?? email;
17512
+ console.log(`[mmrc] Logged in as ${userName}`);
17513
+ console.log(`[mmrc] Token saved to ${CREDENTIALS_PATH}`);
17514
+ return token;
18601
17515
  }
18602
- function compareTransitions(diffs, transitions1, transitions2) {
18603
- const map1 = new Map(transitions1.map((t27) => [t27.name, t27]));
18604
- const map2 = new Map(transitions2.map((t27) => [t27.name, t27]));
18605
- const transitionCount = map1.size;
18606
- let transitionMatch = 0;
18607
- if (transitions1.length !== transitions2.length) {
18608
- diffs.push({
18609
- path: "transitions.length",
18610
- expected: transitions1.length,
18611
- actual: transitions2.length,
18612
- severity: "error"
18613
- });
17516
+ function logout(apiUrl) {
17517
+ if (apiUrl) {
17518
+ removeCredentials(apiUrl);
17519
+ console.log(`[mmrc] Credentials removed for ${apiUrl}`);
17520
+ } else {
17521
+ removeCredentials();
17522
+ console.log("[mmrc] All saved credentials removed");
18614
17523
  }
18615
- for (const [name, t1] of map1) {
18616
- const t27 = map2.get(name);
18617
- if (!t27) {
18618
- diffs.push({ path: `transitions[${name}]`, expected: name, actual: void 0, severity: "error" });
18619
- continue;
18620
- }
18621
- let matched = true;
18622
- if (t1.to !== t27.to) {
18623
- diffs.push({ path: `transitions[${name}].to`, expected: t1.to, actual: t27.to, severity: "error" });
18624
- matched = false;
18625
- }
18626
- const from1 = [...t1.from].sort();
18627
- const from2 = [...t27.from].sort();
18628
- if (JSON.stringify(from1) !== JSON.stringify(from2)) {
18629
- diffs.push({ path: `transitions[${name}].from`, expected: from1, actual: from2, severity: "error" });
18630
- matched = false;
18631
- }
18632
- const roles1 = [...t1.roles ?? []].sort();
18633
- const roles2 = [...t27.roles ?? []].sort();
18634
- if (JSON.stringify(roles1) !== JSON.stringify(roles2)) {
18635
- diffs.push({ path: `transitions[${name}].roles`, expected: roles1, actual: roles2, severity: "error" });
18636
- matched = false;
18637
- }
18638
- const req1 = [...t1.required_fields ?? []].sort();
18639
- const req2 = [...t27.required_fields ?? []].sort();
18640
- if (JSON.stringify(req1) !== JSON.stringify(req2)) {
18641
- diffs.push({ path: `transitions[${name}].required_fields`, expected: req1, actual: req2, severity: "error" });
18642
- matched = false;
18643
- }
18644
- if (!!t1.auto !== !!t27.auto) {
18645
- diffs.push({ path: `transitions[${name}].auto`, expected: t1.auto, actual: t27.auto, severity: "error" });
18646
- matched = false;
17524
+ }
17525
+ async function whoami(apiUrl) {
17526
+ const token = resolveToken(apiUrl);
17527
+ if (!token) {
17528
+ console.log("[mmrc] Not logged in. Run `mmrc login` to authenticate.");
17529
+ return;
17530
+ }
17531
+ try {
17532
+ const res = await fetch(`${apiUrl}/auth/me`, {
17533
+ headers: { Authorization: `Bearer ${token}` }
17534
+ });
17535
+ if (!res.ok) {
17536
+ if (res.status === 401) {
17537
+ console.log("[mmrc] Token is expired or invalid. Run `mmrc login` to re-authenticate.");
17538
+ return;
17539
+ }
17540
+ console.log(`[mmrc] Failed to fetch user info: ${res.status}`);
17541
+ return;
18647
17542
  }
18648
- if (matched) transitionMatch++;
17543
+ const user = await res.json();
17544
+ console.log(`[mmrc] Logged in to ${apiUrl}`);
17545
+ console.log(` Name: ${user.name ?? "(unknown)"}`);
17546
+ console.log(` Email: ${user.email ?? "(unknown)"}`);
17547
+ if (user.id) console.log(` ID: ${user.id}`);
17548
+ if (user.role) console.log(` Role: ${user.role}`);
17549
+ } catch (error) {
17550
+ console.log(`[mmrc] Could not connect to ${apiUrl}: ${error.message}`);
18649
17551
  }
18650
- return { transitionMatch, transitionCount };
18651
17552
  }
18652
17553
 
18653
17554
  // src/cli/index.ts
18654
- init_deploy();
18655
- init_auth();
18656
17555
  var import_meta = {};
18657
17556
  var args = process.argv.slice(2);
18658
17557
  var command = args[0];
@@ -18671,11 +17570,11 @@ function getPositional() {
18671
17570
  }
18672
17571
  async function loadDevConfig() {
18673
17572
  try {
18674
- const { existsSync: existsSync8, readFileSync: readFileSync10 } = await import("fs");
18675
- const { resolve: resolve6 } = await import("path");
18676
- const configPath = resolve6(process.cwd(), "mm.config.ts");
18677
- if (!existsSync8(configPath)) return {};
18678
- const content = readFileSync10(configPath, "utf-8");
17573
+ const { existsSync: existsSync7, readFileSync: readFileSync8 } = await import("fs");
17574
+ const { resolve: resolve5 } = await import("path");
17575
+ const configPath = resolve5(process.cwd(), "mm.config.ts");
17576
+ if (!existsSync7(configPath)) return {};
17577
+ const content = readFileSync8(configPath, "utf-8");
18679
17578
  const devMatch = content.match(/dev\s*:\s*\{([^}]*)\}/s);
18680
17579
  if (!devMatch) return {};
18681
17580
  const block = devMatch[1];
@@ -18703,7 +17602,7 @@ async function main() {
18703
17602
  const src = getFlag("--src") ?? devCfg.src;
18704
17603
  const mode = getFlag("--mode") ?? devCfg.mode;
18705
17604
  const seed = hasFlag("--seed") || devCfg.seed === true;
18706
- const apiUrl = hasFlag("--local") ? "local" : getFlag("--api-url") ?? devCfg.apiUrl;
17605
+ const apiUrl = getFlag("--api-url") ?? devCfg.apiUrl;
18707
17606
  const authToken = getFlag("--token") ?? devCfg.token;
18708
17607
  const open = hasFlag("--open") || devCfg.open === true;
18709
17608
  const { createDevServer: createDevServer2 } = await Promise.resolve().then(() => (init_dev_server(), dev_server_exports));
@@ -18774,19 +17673,19 @@ async function main() {
18774
17673
  const srcDir = src ?? ".";
18775
17674
  const outDir = dir ?? "dist/workflows";
18776
17675
  console.log("[mmrc] Building before deploy...\n");
18777
- const { existsSync: existsSync8, readFileSync: readFileSync10, writeFileSync: writeFileSync9, mkdirSync: mkdirSync6 } = await import("fs");
18778
- const { resolve: resolve6, join: join7 } = await import("path");
18779
- const { glob: glob6 } = await import("glob");
18780
- const configPath = resolve6(srcDir, "mm.config.ts");
18781
- if (existsSync8(configPath)) {
17676
+ const { existsSync: existsSync7, readFileSync: readFileSync8, writeFileSync: writeFileSync8, mkdirSync: mkdirSync6 } = await import("fs");
17677
+ const { resolve: resolve5, join: join7 } = await import("path");
17678
+ const { glob: glob5 } = await import("glob");
17679
+ const configPath = resolve5(srcDir, "mm.config.ts");
17680
+ if (existsSync7(configPath)) {
18782
17681
  const { compileProject: compileProject2 } = await Promise.resolve().then(() => (init_project_compiler(), project_compiler_exports));
18783
- const allFiles = await glob6(`${srcDir}/**/*.{ts,tsx}`, {
17682
+ const allFiles = await glob5(`${srcDir}/**/*.{ts,tsx}`, {
18784
17683
  ignore: ["**/node_modules/**", "**/dist/**", "**/__tests__/**", "**/*.test.*"]
18785
17684
  });
18786
17685
  const fileMap = {};
18787
17686
  for (const f of allFiles) {
18788
17687
  const rel = f.startsWith(srcDir + "/") ? f.slice(srcDir.length + 1) : f;
18789
- fileMap[rel] = readFileSync10(f, "utf-8");
17688
+ fileMap[rel] = readFileSync8(f, "utf-8");
18790
17689
  }
18791
17690
  console.log(` Compiling project (${Object.keys(fileMap).length} files)...`);
18792
17691
  const result = compileProject2(fileMap);
@@ -18801,14 +17700,14 @@ async function main() {
18801
17700
  }
18802
17701
  mkdirSync6(outDir, { recursive: true });
18803
17702
  const irPath = join7(outDir, `${result.ir.slug}.workflow.json`);
18804
- writeFileSync9(irPath, JSON.stringify(result.ir, null, 2), "utf-8");
17703
+ writeFileSync8(irPath, JSON.stringify(result.ir, null, 2), "utf-8");
18805
17704
  console.log(` + ${result.ir.slug}.workflow.json`);
18806
17705
  const seenSlugs = /* @__PURE__ */ new Set([result.ir.slug]);
18807
17706
  for (const child of result.childDefinitions) {
18808
17707
  if (seenSlugs.has(child.slug)) continue;
18809
17708
  seenSlugs.add(child.slug);
18810
17709
  const childPath = join7(outDir, `${child.slug}.workflow.json`);
18811
- writeFileSync9(childPath, JSON.stringify(child, null, 2), "utf-8");
17710
+ writeFileSync8(childPath, JSON.stringify(child, null, 2), "utf-8");
18812
17711
  console.log(` + ${child.slug}.workflow.json`);
18813
17712
  }
18814
17713
  console.log(` Compiled ${1 + result.childDefinitions.length} definitions.
@@ -18884,22 +17783,6 @@ async function main() {
18884
17783
  }
18885
17784
  const { pull: pull2 } = await Promise.resolve().then(() => (init_pull(), pull_exports));
18886
17785
  await pull2({ slug, apiUrl, token, outDir });
18887
- } else if (command === "seed") {
18888
- const apiUrl = getFlag("--api-url") ?? "http://localhost:4200/api/v1";
18889
- const token = resolveToken(apiUrl, getFlag("--token"));
18890
- const dir = getFlag("--dir");
18891
- const count = getFlag("--count") ? parseInt(getFlag("--count"), 10) : void 0;
18892
- if (!token) {
18893
- console.error("[mmrc] Error: No auth token found. Run `mmrc login` first.");
18894
- process.exit(1);
18895
- }
18896
- const { seedInstances: seedInstances2 } = await Promise.resolve().then(() => (init_seed(), seed_exports));
18897
- const result = await seedInstances2({ apiUrl, token, dir, count });
18898
- console.log(`
18899
- [mmrc] Seed complete: ${result.created} instances across ${result.definitions} definitions`);
18900
- if (result.errors.length > 0) {
18901
- console.warn(` ${result.errors.length} error(s)`);
18902
- }
18903
17786
  } else if (command === "config") {
18904
17787
  const subcommand = args[1];
18905
17788
  const { getConfig: getConfig2, getConfigValue: getConfigValue2, setConfigValue: setConfigValue2, flattenConfig: flattenConfig2, findConfigPath: findConfigPath2, writeConfigFile: writeConfigFile2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -18944,44 +17827,17 @@ async function main() {
18944
17827
  console.error(" Usage: mmrc config [get <key> | set <key> <value> | list]");
18945
17828
  process.exit(1);
18946
17829
  }
18947
- } else if (command === "server") {
18948
- const subcommand = args[1];
18949
- const { serverStart: serverStart2, serverMigrate: serverMigrate2, serverInit: serverInit2, serverStatus: serverStatus2, serverConfig: serverConfig2 } = await Promise.resolve().then(() => (init_server(), server_exports));
18950
- if (subcommand === "start") {
18951
- await serverStart2({
18952
- config: getFlag("--config"),
18953
- port: getFlag("--port") ? parseInt(getFlag("--port"), 10) : void 0,
18954
- migrate: hasFlag("--migrate"),
18955
- binary: getFlag("--binary")
18956
- });
18957
- } else if (subcommand === "migrate") {
18958
- await serverMigrate2({ apiUrl: getFlag("--api-url"), config: getFlag("--config") });
18959
- } else if (subcommand === "init") {
18960
- serverInit2({
18961
- mode: getFlag("--mode"),
18962
- db: getFlag("--db"),
18963
- output: getFlag("--output")
18964
- });
18965
- } else if (subcommand === "status") {
18966
- await serverStatus2({ apiUrl: getFlag("--api-url") });
18967
- } else if (subcommand === "config") {
18968
- serverConfig2({ config: getFlag("--config") });
18969
- } else {
18970
- console.error(`[mmrc] Unknown server subcommand: ${subcommand ?? "(none)"}`);
18971
- console.error(" Usage: mmrc server [start|migrate|init|status|config]");
18972
- process.exit(1);
18973
- }
18974
17830
  } else if (command === "version" || command === "-v" || command === "--version" || hasFlag("--version") || hasFlag("-v")) {
18975
- const { readFileSync: readFileSync10 } = await import("fs");
18976
- const { resolve: resolve6, dirname: dirname4 } = await import("path");
17831
+ const { readFileSync: readFileSync8 } = await import("fs");
17832
+ const { resolve: resolve5, dirname: dirname4 } = await import("path");
18977
17833
  try {
18978
- const pkgPath = resolve6(dirname4(new URL(import_meta.url).pathname), "../../package.json");
18979
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
17834
+ const pkgPath = resolve5(dirname4(new URL(import_meta.url).pathname), "../../package.json");
17835
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
18980
17836
  console.log(`mmrc ${pkg.version}`);
18981
17837
  } catch {
18982
17838
  try {
18983
- const pkgPath = resolve6(__dirname, "../../package.json");
18984
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
17839
+ const pkgPath = resolve5(__dirname, "../../package.json");
17840
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
18985
17841
  console.log(`mmrc ${pkg.version}`);
18986
17842
  } catch {
18987
17843
  console.log("mmrc (version unknown)");
@@ -18990,10 +17846,10 @@ async function main() {
18990
17846
  } else {
18991
17847
  let version = "";
18992
17848
  try {
18993
- const { readFileSync: readFileSync10 } = await import("fs");
18994
- const { resolve: resolve6 } = await import("path");
18995
- const pkgPath = resolve6(__dirname, "../../package.json");
18996
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
17849
+ const { readFileSync: readFileSync8 } = await import("fs");
17850
+ const { resolve: resolve5 } = await import("path");
17851
+ const pkgPath = resolve5(__dirname, "../../package.json");
17852
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
18997
17853
  version = ` v${pkg.version}`;
18998
17854
  } catch {
18999
17855
  }