swallowkit 2.0.0-alpha.1 → 2.0.0-alpha.10

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 (224) hide show
  1. package/README.ja.md +14 -0
  2. package/README.md +14 -0
  3. package/dist/cli/commands/add-auth.js +37 -6
  4. package/dist/cli/commands/add-auth.js.map +1 -1
  5. package/dist/cli/commands/configure-mcp.d.ts +12 -0
  6. package/dist/cli/commands/configure-mcp.d.ts.map +1 -0
  7. package/dist/cli/commands/configure-mcp.js +42 -0
  8. package/dist/cli/commands/configure-mcp.js.map +1 -0
  9. package/dist/cli/commands/dev.d.ts +2 -2
  10. package/dist/cli/commands/dev.d.ts.map +1 -1
  11. package/dist/cli/commands/dev.js +39 -27
  12. package/dist/cli/commands/dev.js.map +1 -1
  13. package/dist/cli/commands/init.d.ts +8 -2
  14. package/dist/cli/commands/init.d.ts.map +1 -1
  15. package/dist/cli/commands/init.js +67 -132
  16. package/dist/cli/commands/init.js.map +1 -1
  17. package/dist/cli/commands/migrations.d.ts.map +1 -1
  18. package/dist/cli/commands/migrations.js +25 -0
  19. package/dist/cli/commands/migrations.js.map +1 -1
  20. package/dist/cli/commands/provision.d.ts +5 -0
  21. package/dist/cli/commands/provision.d.ts.map +1 -1
  22. package/dist/cli/commands/provision.js +278 -55
  23. package/dist/cli/commands/provision.js.map +1 -1
  24. package/dist/cli/commands/scaffold.d.ts +4 -0
  25. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  26. package/dist/cli/commands/scaffold.js +85 -3
  27. package/dist/cli/commands/scaffold.js.map +1 -1
  28. package/dist/cli/index.d.ts +2 -0
  29. package/dist/cli/index.d.ts.map +1 -1
  30. package/dist/cli/index.js +43 -5
  31. package/dist/cli/index.js.map +1 -1
  32. package/dist/core/operations/create-model.d.ts +2 -0
  33. package/dist/core/operations/create-model.d.ts.map +1 -1
  34. package/dist/core/operations/create-model.js +60 -8
  35. package/dist/core/operations/create-model.js.map +1 -1
  36. package/dist/core/operations/migration-generator.d.ts.map +1 -1
  37. package/dist/core/operations/migration-generator.js +12 -1
  38. package/dist/core/operations/migration-generator.js.map +1 -1
  39. package/dist/core/operations/model-migration.d.ts +22 -0
  40. package/dist/core/operations/model-migration.d.ts.map +1 -0
  41. package/dist/core/operations/model-migration.js +36 -0
  42. package/dist/core/operations/model-migration.js.map +1 -0
  43. package/dist/core/operations/provision-deployment.d.ts +52 -0
  44. package/dist/core/operations/provision-deployment.d.ts.map +1 -0
  45. package/dist/core/operations/provision-deployment.js +164 -0
  46. package/dist/core/operations/provision-deployment.js.map +1 -0
  47. package/dist/core/operations/provision-operations.d.ts +62 -6
  48. package/dist/core/operations/provision-operations.d.ts.map +1 -1
  49. package/dist/core/operations/provision-operations.js +653 -115
  50. package/dist/core/operations/provision-operations.js.map +1 -1
  51. package/dist/core/operations/scaffold-apply.d.ts +1 -0
  52. package/dist/core/operations/scaffold-apply.d.ts.map +1 -1
  53. package/dist/core/operations/scaffold-apply.js +1 -0
  54. package/dist/core/operations/scaffold-apply.js.map +1 -1
  55. package/dist/core/operations/scaffold-machine.d.ts +1 -0
  56. package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
  57. package/dist/core/operations/scaffold-machine.js.map +1 -1
  58. package/dist/core/operations/scaffold-plan.d.ts +2 -0
  59. package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
  60. package/dist/core/operations/scaffold-plan.js +3 -1
  61. package/dist/core/operations/scaffold-plan.js.map +1 -1
  62. package/dist/core/project/agent-instructions.d.ts.map +1 -1
  63. package/dist/core/project/agent-instructions.js +4 -1
  64. package/dist/core/project/agent-instructions.js.map +1 -1
  65. package/dist/core/project/boundaries.d.ts +5 -2
  66. package/dist/core/project/boundaries.d.ts.map +1 -1
  67. package/dist/core/project/boundaries.js +51 -20
  68. package/dist/core/project/boundaries.js.map +1 -1
  69. package/dist/core/project/capabilities.d.ts +10 -0
  70. package/dist/core/project/capabilities.d.ts.map +1 -1
  71. package/dist/core/project/capabilities.js +38 -6
  72. package/dist/core/project/capabilities.js.map +1 -1
  73. package/dist/core/project/home-page.d.ts +4 -0
  74. package/dist/core/project/home-page.d.ts.map +1 -0
  75. package/dist/core/project/home-page.js +113 -0
  76. package/dist/core/project/home-page.js.map +1 -0
  77. package/dist/core/project/mcp-config.d.ts +26 -0
  78. package/dist/core/project/mcp-config.d.ts.map +1 -0
  79. package/dist/core/project/mcp-config.js +117 -0
  80. package/dist/core/project/mcp-config.js.map +1 -0
  81. package/dist/core/project/react-project.d.ts +1 -1
  82. package/dist/core/project/react-project.d.ts.map +1 -1
  83. package/dist/core/project/react-project.js +46 -25
  84. package/dist/core/project/react-project.js.map +1 -1
  85. package/dist/core/project/state.d.ts +7 -0
  86. package/dist/core/project/state.d.ts.map +1 -1
  87. package/dist/core/project/state.js +86 -0
  88. package/dist/core/project/state.js.map +1 -1
  89. package/dist/core/project/workflows.d.ts.map +1 -1
  90. package/dist/core/project/workflows.js +53 -7
  91. package/dist/core/project/workflows.js.map +1 -1
  92. package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
  93. package/dist/core/scaffold/auth-generator.js +26 -27
  94. package/dist/core/scaffold/auth-generator.js.map +1 -1
  95. package/dist/core/scaffold/model-parser.d.ts +23 -0
  96. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  97. package/dist/core/scaffold/model-parser.js +407 -137
  98. package/dist/core/scaffold/model-parser.js.map +1 -1
  99. package/dist/core/scaffold/native-schema-generator.d.ts +13 -0
  100. package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
  101. package/dist/core/scaffold/native-schema-generator.js +294 -91
  102. package/dist/core/scaffold/native-schema-generator.js.map +1 -1
  103. package/dist/core/scaffold/openapi-generator.d.ts +5 -0
  104. package/dist/core/scaffold/openapi-generator.d.ts.map +1 -1
  105. package/dist/core/scaffold/openapi-generator.js +53 -3
  106. package/dist/core/scaffold/openapi-generator.js.map +1 -1
  107. package/dist/core/scaffold/ui-generator.d.ts +0 -3
  108. package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
  109. package/dist/core/scaffold/ui-generator.js +68 -7
  110. package/dist/core/scaffold/ui-generator.js.map +1 -1
  111. package/dist/core/verify/index.d.ts +4 -3
  112. package/dist/core/verify/index.d.ts.map +1 -1
  113. package/dist/core/verify/index.js +122 -5
  114. package/dist/core/verify/index.js.map +1 -1
  115. package/dist/machine/contracts.d.ts +1 -0
  116. package/dist/machine/contracts.d.ts.map +1 -1
  117. package/dist/machine/errors.d.ts +3 -2
  118. package/dist/machine/errors.d.ts.map +1 -1
  119. package/dist/machine/errors.js +2 -1
  120. package/dist/machine/errors.js.map +1 -1
  121. package/dist/machine/index.d.ts.map +1 -1
  122. package/dist/machine/index.js +59 -28
  123. package/dist/machine/index.js.map +1 -1
  124. package/dist/mcp/index.d.ts +6 -4
  125. package/dist/mcp/index.d.ts.map +1 -1
  126. package/dist/mcp/index.js +210 -25
  127. package/dist/mcp/index.js.map +1 -1
  128. package/dist/mcp/version-selection.d.ts +55 -0
  129. package/dist/mcp/version-selection.d.ts.map +1 -0
  130. package/dist/mcp/version-selection.js +152 -0
  131. package/dist/mcp/version-selection.js.map +1 -0
  132. package/dist/utils/azure-cli.d.ts +56 -0
  133. package/dist/utils/azure-cli.d.ts.map +1 -0
  134. package/dist/utils/azure-cli.js +326 -0
  135. package/dist/utils/azure-cli.js.map +1 -0
  136. package/dist/utils/azure-diagnostics.d.ts +16 -0
  137. package/dist/utils/azure-diagnostics.d.ts.map +1 -0
  138. package/dist/utils/azure-diagnostics.js +68 -0
  139. package/dist/utils/azure-diagnostics.js.map +1 -0
  140. package/dist/utils/package-manager.js +2 -2
  141. package/dist/utils/package-manager.js.map +1 -1
  142. package/dist/utils/python-uv.d.ts +5 -7
  143. package/dist/utils/python-uv.d.ts.map +1 -1
  144. package/dist/utils/python-uv.js +50 -24
  145. package/dist/utils/python-uv.js.map +1 -1
  146. package/dist/utils/react-dev.d.ts +4 -0
  147. package/dist/utils/react-dev.d.ts.map +1 -0
  148. package/dist/utils/react-dev.js +35 -0
  149. package/dist/utils/react-dev.js.map +1 -0
  150. package/dist/utils/semver.d.ts +14 -0
  151. package/dist/utils/semver.d.ts.map +1 -0
  152. package/dist/utils/semver.js +61 -0
  153. package/dist/utils/semver.js.map +1 -0
  154. package/package.json +6 -3
  155. package/src/__tests__/__snapshots__/boundaries.test.ts.snap +154 -0
  156. package/src/__tests__/agent-instructions.test.ts +5 -0
  157. package/src/__tests__/agent-loop-phase34.test.ts +143 -8
  158. package/src/__tests__/auth.test.ts +9 -0
  159. package/src/__tests__/azure-cli.test.ts +202 -0
  160. package/src/__tests__/boundaries.test.ts +42 -0
  161. package/src/__tests__/csharp-schema.test.ts +261 -0
  162. package/src/__tests__/dev.test.ts +13 -6
  163. package/src/__tests__/fixtures/az wrapper.cmd +2 -0
  164. package/src/__tests__/fixtures/azure-cli.cjs +153 -0
  165. package/src/__tests__/fixtures/provision-runner.cjs +28 -0
  166. package/src/__tests__/generated-project-matrix.test.ts +789 -0
  167. package/src/__tests__/home-page.test.ts +50 -0
  168. package/src/__tests__/init.test.ts +116 -0
  169. package/src/__tests__/mcp-version.test.ts +262 -0
  170. package/src/__tests__/mcp.test.ts +120 -3
  171. package/src/__tests__/multi-auth.test.ts +115 -4
  172. package/src/__tests__/package-manager.test.ts +17 -2
  173. package/src/__tests__/plan-apply.test.ts +179 -0
  174. package/src/__tests__/provision-runtime.test.ts +722 -0
  175. package/src/__tests__/python-uv.test.ts +50 -14
  176. package/src/__tests__/python-venv.test.ts +48 -0
  177. package/src/__tests__/react-dev.test.ts +72 -0
  178. package/src/__tests__/react.test.ts +19 -0
  179. package/src/__tests__/schema-analysis.test.ts +329 -0
  180. package/src/cli/commands/add-auth.ts +36 -6
  181. package/src/cli/commands/configure-mcp.ts +54 -0
  182. package/src/cli/commands/dev.ts +40 -31
  183. package/src/cli/commands/init.ts +73 -138
  184. package/src/cli/commands/migrations.ts +22 -0
  185. package/src/cli/commands/provision.ts +294 -56
  186. package/src/cli/commands/scaffold.ts +105 -3
  187. package/src/cli/index.ts +40 -7
  188. package/src/core/operations/create-model.ts +51 -13
  189. package/src/core/operations/migration-generator.ts +14 -1
  190. package/src/core/operations/model-migration.ts +52 -0
  191. package/src/core/operations/provision-deployment.ts +183 -0
  192. package/src/core/operations/provision-operations.ts +662 -154
  193. package/src/core/operations/scaffold-apply.ts +2 -0
  194. package/src/core/operations/scaffold-machine.ts +1 -0
  195. package/src/core/operations/scaffold-plan.ts +5 -1
  196. package/src/core/project/agent-instructions.ts +4 -1
  197. package/src/core/project/agent-templates/AGENTS.md +89 -6
  198. package/src/core/project/agent-templates/CLAUDE.md +14 -1
  199. package/src/core/project/agent-templates/copilot-instructions.md +15 -1
  200. package/src/core/project/agent-templates/shared-models.instructions.md +11 -3
  201. package/src/core/project/boundaries.ts +62 -20
  202. package/src/core/project/capabilities.ts +49 -6
  203. package/src/core/project/home-page.ts +114 -0
  204. package/src/core/project/mcp-config.ts +84 -0
  205. package/src/core/project/react-project.ts +50 -28
  206. package/src/core/project/state.ts +70 -0
  207. package/src/core/project/workflows.ts +53 -7
  208. package/src/core/scaffold/auth-generator.ts +26 -27
  209. package/src/core/scaffold/model-parser.ts +413 -129
  210. package/src/core/scaffold/native-schema-generator.ts +319 -103
  211. package/src/core/scaffold/openapi-generator.ts +63 -5
  212. package/src/core/scaffold/ui-generator.ts +70 -8
  213. package/src/core/verify/index.ts +118 -6
  214. package/src/machine/contracts.ts +1 -0
  215. package/src/machine/errors.ts +4 -2
  216. package/src/machine/index.ts +65 -34
  217. package/src/mcp/index.ts +221 -31
  218. package/src/mcp/version-selection.ts +163 -0
  219. package/src/utils/azure-cli.ts +315 -0
  220. package/src/utils/azure-diagnostics.ts +75 -0
  221. package/src/utils/package-manager.ts +2 -2
  222. package/src/utils/python-uv.ts +53 -33
  223. package/src/utils/react-dev.ts +31 -0
  224. package/src/utils/semver.ts +59 -0
@@ -428,7 +428,7 @@ export async function addAuthCommand(options: AddAuthOptions) {
428
428
 
429
429
  // 10. Install dependencies
430
430
  console.log("\n Installing auth dependencies...");
431
- await installAuthDependencies(cwd, backendLanguage, rdbProvider, session);
431
+ installAuthDependenciesSync(cwd, backendLanguage, rdbProvider, session);
432
432
  await finalize();
433
433
 
434
434
  console.log("\n Authentication setup complete!");
@@ -458,7 +458,8 @@ function findObjectEnd(content: string, open: number): number {
458
458
  function updateConfigWithNamedScheme(cwd: string, scheme: string, provider: AuthProvider, session: FileOperationSession, allowedProviders?: string[]): void {
459
459
  const jsPath = path.join(cwd, "swallowkit.config.js");
460
460
  const jsonPath = path.join(cwd, "swallowkit.config.json");
461
- const configPath = session.fileExists(jsPath) ? jsPath : jsonPath;
461
+ // Match loadConfig precedence: swallowkit.config.json wins when both files exist.
462
+ const configPath = session.fileExists(jsonPath) ? jsonPath : jsPath;
462
463
  if (!session.fileExists(configPath)) throw new Error("swallowkit.config.js or swallowkit.config.json is required");
463
464
  const swaProviders = provider === "swa" ? normalizeSwaProviders(allowedProviders) : undefined;
464
465
  if (configPath.endsWith(".json")) {
@@ -510,6 +511,11 @@ function addNamedScheme(cwd: string, scheme: string, provider: AuthProvider, lan
510
511
  // Validate/update config before creating files, so duplicate names stop safely.
511
512
  updateConfigWithNamedScheme(cwd, scheme, provider, session, allowedProviders);
512
513
  const slug = scheme.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/_/g, "-").toLowerCase();
514
+ if (provider === "custom-jwt") {
515
+ // The scheme's JWT helper needs the same token/password/driver packages as single-provider custom-jwt.
516
+ const connector = getConnectorDefinition(defaultCustomJwtConfig().userConnector) as RdbConnectorConfig | undefined;
517
+ installAuthDependenciesSync(cwd, language, connector?.provider ?? "mysql", session);
518
+ }
513
519
  if (language === "typescript") {
514
520
  const dir = path.join(cwd, "functions", "src", "auth", "schemes", slug);
515
521
  if (provider === "external-token") writeIfMissing(session, path.join(dir, "verifier.ts"), generateExternalTokenVerifierTS());
@@ -689,9 +695,14 @@ function generateFunctionsAuth(
689
695
  const functionAppPath = path.join(functionsDir, "function_app.py");
690
696
  if (session.fileExists(functionAppPath)) {
691
697
  const content = session.readFile(functionAppPath);
692
- const authImport = "from blueprints.auth import bp as auth_bp";
698
+ const authImport = "from blueprints.auth import auth_bp";
693
699
  const authRegister = "app.register_blueprint(auth_bp)";
694
- if (!content.includes(authImport)) {
700
+ // Earlier releases imported a non-existent `bp`; repair it in place when re-running.
701
+ const legacyImport = "from blueprints.auth import bp as auth_bp";
702
+ if (content.includes(legacyImport)) {
703
+ session.writeFile(functionAppPath, content.replace(legacyImport, authImport), extensionMeta);
704
+ console.log(` Updated: functions/function_app.py (fixed auth blueprint import)`);
705
+ } else if (!content.includes(authImport)) {
695
706
  const marker = "# SwallowKit scaffold registrations";
696
707
  if (content.includes(marker)) {
697
708
  const updated = content.replace(
@@ -742,6 +753,24 @@ function generateBFFAuth(cwd: string, projectName: string, sharedPackageName: st
742
753
  }
743
754
 
744
755
  function updateConfigWithAuth(cwd: string, provider: AuthProvider, config: CustomJwtConfig, session: FileOperationSession, allowedProviders?: string[]): void {
756
+ const jsonPath = path.join(cwd, "swallowkit.config.json");
757
+ if (session.fileExists(jsonPath)) {
758
+ // loadConfig reads swallowkit.config.json first, so it is the effective configuration.
759
+ const value = JSON.parse(session.readFile(jsonPath));
760
+ if (value.auth) {
761
+ console.log(" 'auth' section already exists in swallowkit.config.json");
762
+ return;
763
+ }
764
+ value.auth = {
765
+ provider,
766
+ ...(provider === "custom-jwt" ? { customJwt: { ...config, jwtSecretEnv: config.jwtSecretEnv || "JWT_SECRET", tokenExpiry: config.tokenExpiry || "24h" } } : {}),
767
+ ...(provider === "swa" ? { swa: { allowedProviders: normalizeSwaProviders(allowedProviders), roleSource: "swa-roles" } } : {}),
768
+ authorization: { defaultPolicy: provider === "none" ? "anonymous" : "authenticated" },
769
+ };
770
+ session.writeFile(jsonPath, JSON.stringify(value, null, 2) + "\n", extensionMeta);
771
+ console.log(` Updated: swallowkit.config.json`);
772
+ return;
773
+ }
745
774
  const configPath = path.join(cwd, "swallowkit.config.js");
746
775
  if (!session.fileExists(configPath)) {
747
776
  console.warn(" swallowkit.config.js not found. Please add auth config manually.");
@@ -786,7 +815,8 @@ function updateConfigWithAuth(cwd: string, provider: AuthProvider, config: Custo
786
815
  provider: '${provider}',
787
816
  ${providerSettings}
788
817
  authorization: {
789
- defaultPolicy: 'authenticated',
818
+ // Disabled auth has no scheme to authenticate against; 'authenticated' would fail validateConfig.
819
+ defaultPolicy: '${provider === "none" ? "anonymous" : "authenticated"}',
790
820
  },
791
821
  },
792
822
  `;
@@ -821,7 +851,7 @@ function updateEnvironmentFiles(cwd: string, session: FileOperationSession): voi
821
851
  }
822
852
  }
823
853
 
824
- async function installAuthDependencies(cwd: string, backendLanguage: BackendLanguage, provider: "mysql" | "postgres" | "sqlserver" = "mysql", session: FileOperationSession): Promise<void> {
854
+ function installAuthDependenciesSync(cwd: string, backendLanguage: BackendLanguage, provider: "mysql" | "postgres" | "sqlserver" = "mysql", session: FileOperationSession): void {
825
855
  if (backendLanguage === "typescript") {
826
856
  const funcPkgPath = path.join(cwd, "functions", "package.json");
827
857
  if (session.fileExists(funcPkgPath)) {
@@ -0,0 +1,54 @@
1
+ import { Command } from "commander";
2
+ import { ensureSwallowKitProject } from "../../core/config";
3
+ import {
4
+ MCP_CONFIG_PATH,
5
+ MCP_LATEST,
6
+ lookupPublishedLatestVersion,
7
+ readMcpConfigVersion,
8
+ resolveMcpPinVersion,
9
+ writeMcpConfigVersion,
10
+ } from "../../core/project/mcp-config";
11
+ import { resolveProjectSwallowKit } from "../../mcp/version-selection";
12
+ import { parseSemver } from "../../utils/semver";
13
+ import { getSwallowKitVersion } from "../../version";
14
+
15
+ export interface ConfigureMcpResult {
16
+ previousVersion: string | null;
17
+ version: string;
18
+ reason: string;
19
+ }
20
+
21
+ export function configureMcp(
22
+ options: { version?: string; cwd?: string },
23
+ lookupLatest: (packageName: string) => string | null = lookupPublishedLatestVersion
24
+ ): ConfigureMcpResult {
25
+ const cwd = options.cwd ?? process.cwd();
26
+ const requested = (options.version ?? "auto").trim();
27
+ const previousVersion = readMcpConfigVersion(cwd);
28
+
29
+ let decision: { version: string; reason: string };
30
+ if (requested === "auto") {
31
+ const projectVersion = resolveProjectSwallowKit(cwd)?.version ?? getSwallowKitVersion();
32
+ decision = resolveMcpPinVersion(projectVersion, lookupLatest("swallowkit"));
33
+ } else if (requested === MCP_LATEST) {
34
+ decision = { version: MCP_LATEST, reason: "Explicitly requested latest." };
35
+ } else if (parseSemver(requested)) {
36
+ decision = { version: requested, reason: "Explicitly pinned version." };
37
+ } else {
38
+ throw new Error(`--version must be "auto", "latest", or a semantic version (got "${requested}").`);
39
+ }
40
+
41
+ writeMcpConfigVersion(cwd, decision.version);
42
+ return { previousVersion, ...decision };
43
+ }
44
+
45
+ export const configureMcpCommand = new Command("configure-mcp")
46
+ .description(`Select the SwallowKit MCP version used by ${MCP_CONFIG_PATH} (auto: latest unless the project is newer)`)
47
+ .option("--version <version>", "auto | latest | <semver>", "auto")
48
+ .action((options: { version?: string }) => {
49
+ ensureSwallowKitProject("configure-mcp");
50
+ const result = configureMcp({ version: options.version });
51
+ console.log(`${MCP_CONFIG_PATH}: SWALLOWKIT_MCP_VERSION ${result.previousVersion ?? "(unset)"} -> ${result.version}`);
52
+ console.log(`Reason: ${result.reason}`);
53
+ console.log("Restart the MCP server (or reload your agent) to apply the change.");
54
+ });
@@ -19,15 +19,15 @@ import {
19
19
  import { BackendLanguage } from '../../types';
20
20
  import { detectFromProject, getCommands } from '../../utils/package-manager';
21
21
  import {
22
- buildProjectLocalUvEnv,
23
22
  buildProjectLocalUvInstallerEnv,
24
23
  buildUvPipInstallArgs,
25
- buildUvVenvArgs,
24
+ ensureUvVirtualEnvironment,
26
25
  getProjectLocalUvInstallerCommand,
27
26
  getProjectLocalUvPaths,
28
27
  getPythonProjectRoot,
29
28
  } from '../../utils/python-uv';
30
29
  import { ConnectorMockServer } from '../../core/mock/connector-mock-server';
30
+ import { buildReactDevConfigSource } from '../../utils/react-dev';
31
31
 
32
32
  export interface DevOptions {
33
33
  port?: string;
@@ -65,8 +65,8 @@ function normalizeParsedDevOptions(options: ParsedDevActionOptions): DevOptions
65
65
  };
66
66
  }
67
67
 
68
- export function buildFunctionsStartArgs(functionsPort: string): string[] {
69
- return ['start', '--port', functionsPort];
68
+ export function buildFunctionsStartArgs(functionsPort: string, backendLanguage: BackendLanguage = 'typescript'): string[] {
69
+ return [...(backendLanguage === 'csharp' ? [] : ['start']), '--port', functionsPort];
70
70
  }
71
71
 
72
72
  export function parseCoreToolsVersion(output: string): string | null {
@@ -105,15 +105,15 @@ export function buildFunctionsCoreToolsCommand(
105
105
 
106
106
  return {
107
107
  command: 'npm',
108
- argsPrefix: ['exec', '--yes', NPM_CORE_TOOLS_PACKAGE, '--'],
109
- label: `npm exec ${NPM_CORE_TOOLS_PACKAGE} (${reason})`,
108
+ argsPrefix: ['exec', '--yes', '--package', NPM_CORE_TOOLS_PACKAGE, '--', 'dotnet', 'run', '--no-launch-profile', '--'],
109
+ label: `dotnet run with npm ${NPM_CORE_TOOLS_PACKAGE} (${reason})`,
110
110
  };
111
111
  }
112
112
 
113
113
  return {
114
- command: 'func',
115
- argsPrefix: [],
116
- label: installedVersion ? `func ${installedVersion}` : 'func',
114
+ command: backendLanguage === 'csharp' ? 'dotnet' : 'func',
115
+ argsPrefix: backendLanguage === 'csharp' ? ['run', '--no-launch-profile', '--'] : [],
116
+ label: backendLanguage === 'csharp' ? 'dotnet run' : installedVersion ? `func ${installedVersion}` : 'func',
117
117
  };
118
118
  }
119
119
 
@@ -138,8 +138,8 @@ export function buildSwaStartArgs(host: string | undefined, nextPort: string, sw
138
138
  ];
139
139
  }
140
140
 
141
- export function buildReactDevArgs(pm: string, port: string, host = 'localhost'): string[] {
142
- const args = ['vite', '--port', port, '--host', host, '--strictPort'];
141
+ export function buildReactDevArgs(pm: string, port: string, host = 'localhost', configPath?: string): string[] {
142
+ const args = ['vite', '--port', port, '--host', host, '--strictPort', ...(configPath ? ['--config', configPath] : [])];
143
143
  return pm === 'pnpm' ? ['exec', ...args] : args;
144
144
  }
145
145
 
@@ -270,7 +270,7 @@ async function checkCommand(
270
270
  }
271
271
 
272
272
  async function resolveProjectLocalUvCommand(projectRoot: string): Promise<{ command: string; env: NodeJS.ProcessEnv }> {
273
- const uvEnv = buildProjectLocalUvEnv(process.env, projectRoot);
273
+ const uvEnv = { ...process.env };
274
274
  const { localUvExecutable } = getProjectLocalUvPaths(projectRoot);
275
275
 
276
276
  if (await checkCommand('uv', ['--version'], { shell: false })) {
@@ -471,22 +471,8 @@ async function bridgePythonCoreToolsForWindowsArm64(
471
471
  async function preparePythonFunctionsEnvironment(functionsDir: string): Promise<NodeJS.ProcessEnv> {
472
472
  const projectRoot = getPythonProjectRoot(functionsDir);
473
473
  const { command: uvCommand, env: uvEnv } = await resolveProjectLocalUvCommand(projectRoot);
474
- const { venvDir, pythonExecutable } = getPythonVirtualEnvPaths(functionsDir);
475
- const hasUsableVirtualEnv = fs.existsSync(pythonExecutable) && await checkCommand(pythonExecutable, ['--version'], {
476
- cwd: functionsDir,
477
- env: uvEnv,
478
- shell: false,
479
- });
480
-
481
- if (!hasUsableVirtualEnv) {
482
- const venvArgs = buildUvVenvArgs('.venv');
483
- if (fs.existsSync(venvDir)) {
484
- venvArgs.push('--clear');
485
- }
486
-
487
- console.log('📦 Creating Python virtual environment with uv...');
488
- await runCommand(uvCommand, venvArgs, functionsDir, 'python virtual environment setup', uvEnv, false);
489
- }
474
+ const { venvDir } = getPythonVirtualEnvPaths(functionsDir);
475
+ const pythonExecutable = await ensureUvVirtualEnvironment(uvCommand, venvDir, functionsDir, uvEnv);
490
476
 
491
477
  console.log('📦 Installing Python Azure Functions dependencies with uv...');
492
478
  await runCommand(
@@ -538,7 +524,7 @@ export async function assertDevPortAvailable(port: number, host: string): Promis
538
524
  }
539
525
 
540
526
  function stopDevProcesses(processes: ChildProcess[]): void {
541
- const active = processes.filter(child => child.pid && !child.killed);
527
+ const active = processes.filter(child => child.pid && !child.killed && child.exitCode === null && child.signalCode === null);
542
528
  if (process.platform === 'win32' && active.length) {
543
529
  // Windows shell termination alone leaves Vite, Core Tools, and SWA running.
544
530
  const ids = active.map(child => child.pid).join(',');
@@ -721,6 +707,14 @@ async function startDevEnvironment(options: DevOptions) {
721
707
  const functionsBaseUrl = buildFunctionsBaseUrl(options.host, functionsPort);
722
708
  let functionsReadinessPromise: Promise<boolean> | null = null;
723
709
  let functionsReady = !!options.noFunctions;
710
+ let reactDevConfigDir: string | undefined;
711
+ const cleanupReactDevConfig = () => {
712
+ if (reactDevConfigDir) {
713
+ fs.rmSync(reactDevConfigDir, { recursive: true, force: true });
714
+ reactDevConfigDir = undefined;
715
+ }
716
+ };
717
+ process.once('exit', cleanupReactDevConfig);
724
718
 
725
719
  // Cleanup processes on Ctrl+C
726
720
  process.on('SIGINT', async () => {
@@ -939,7 +933,7 @@ async function startDevEnvironment(options: DevOptions) {
939
933
 
940
934
  // Azure Functions を起動
941
935
  const functionsCommand = functionsCoreToolsCommand ?? buildFunctionsCoreToolsCommand(backendLanguage, installedCoreToolsVersion);
942
- const funcProcess = spawn(functionsCommand.command, [...functionsCommand.argsPrefix, ...buildFunctionsStartArgs(functionsPort)], {
936
+ const funcProcess = spawn(functionsCommand.command, [...functionsCommand.argsPrefix, ...buildFunctionsStartArgs(functionsPort, backendLanguage)], {
943
937
  cwd: functionsDir,
944
938
  shell: true,
945
939
  stdio: 'pipe', // Always pipe to capture output
@@ -987,6 +981,8 @@ async function startDevEnvironment(options: DevOptions) {
987
981
  console.error('⚠️ Azure Functions startup error:', error.message);
988
982
  console.log('💡 Please ensure Azure Functions Core Tools is installed');
989
983
  console.log(` ${pmCmd.addGlobal} azure-functions-core-tools@4`);
984
+ stopDevProcesses(processes);
985
+ process.exit(1);
990
986
  });
991
987
 
992
988
  funcProcess.on('close', (code) => {
@@ -997,6 +993,8 @@ async function startDevEnvironment(options: DevOptions) {
997
993
  console.log(' Reinstall a matching Core Tools v4 package (Windows users should prefer the official x64/x86 MSI for their machine).');
998
994
  console.log(' SwallowKit local Python dev uses functions/.venv and requirements.txt, but Core Tools still needs its own bundled Python worker.');
999
995
  }
996
+ stopDevProcesses(processes);
997
+ process.exit(code || 1);
1000
998
  }
1001
999
  });
1002
1000
 
@@ -1083,7 +1081,17 @@ async function startDevEnvironment(options: DevOptions) {
1083
1081
  }
1084
1082
 
1085
1083
  // 5. Start Next.js development server
1086
- const nextArgs = react ? buildReactDevArgs(pm, port, options.host) : buildNextDevArgs(pm, port);
1084
+ let reactDevConfigPath: string | undefined;
1085
+ if (react) {
1086
+ const stateDir = path.join(process.cwd(), '.swallowkit');
1087
+ fs.mkdirSync(stateDir, { recursive: true });
1088
+ reactDevConfigDir = fs.mkdtempSync(path.join(stateDir, 'vite-dev-'));
1089
+ reactDevConfigPath = path.join(reactDevConfigDir, 'config.mjs');
1090
+ fs.writeFileSync(reactDevConfigPath, buildReactDevConfigSource());
1091
+ }
1092
+ const nextArgs = react
1093
+ ? buildReactDevArgs(pm, port, options.host, reactDevConfigPath && path.relative(process.cwd(), reactDevConfigPath))
1094
+ : buildNextDevArgs(pm, port);
1087
1095
 
1088
1096
  // Ensure .env.local points to bffTargetPort so Next.js reads the correct backend URL.
1089
1097
  // When --mock-connectors is active, bffTargetPort = mock port (7072); otherwise = Functions port (7071).
@@ -1141,6 +1149,7 @@ async function startDevEnvironment(options: DevOptions) {
1141
1149
 
1142
1150
  nextProcess.on('error', (error) => {
1143
1151
  console.error(`❌ ${frontendLabel} startup error:`, error.message);
1152
+ stopDevProcesses(processes);
1144
1153
  process.exit(1);
1145
1154
  });
1146
1155