swallowkit 2.0.0-alpha.1 → 2.0.0-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +14 -0
- package/README.md +14 -0
- package/dist/cli/commands/add-auth.js +37 -6
- package/dist/cli/commands/add-auth.js.map +1 -1
- package/dist/cli/commands/configure-mcp.d.ts +12 -0
- package/dist/cli/commands/configure-mcp.d.ts.map +1 -0
- package/dist/cli/commands/configure-mcp.js +42 -0
- package/dist/cli/commands/configure-mcp.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +2 -2
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +39 -27
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +8 -2
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +67 -132
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/migrations.d.ts.map +1 -1
- package/dist/cli/commands/migrations.js +25 -0
- package/dist/cli/commands/migrations.js.map +1 -1
- package/dist/cli/commands/provision.d.ts +5 -0
- package/dist/cli/commands/provision.d.ts.map +1 -1
- package/dist/cli/commands/provision.js +278 -55
- package/dist/cli/commands/provision.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +4 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +85 -3
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +43 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/core/operations/create-model.d.ts +2 -0
- package/dist/core/operations/create-model.d.ts.map +1 -1
- package/dist/core/operations/create-model.js +60 -8
- package/dist/core/operations/create-model.js.map +1 -1
- package/dist/core/operations/migration-generator.d.ts.map +1 -1
- package/dist/core/operations/migration-generator.js +12 -1
- package/dist/core/operations/migration-generator.js.map +1 -1
- package/dist/core/operations/model-migration.d.ts +22 -0
- package/dist/core/operations/model-migration.d.ts.map +1 -0
- package/dist/core/operations/model-migration.js +36 -0
- package/dist/core/operations/model-migration.js.map +1 -0
- package/dist/core/operations/provision-deployment.d.ts +52 -0
- package/dist/core/operations/provision-deployment.d.ts.map +1 -0
- package/dist/core/operations/provision-deployment.js +164 -0
- package/dist/core/operations/provision-deployment.js.map +1 -0
- package/dist/core/operations/provision-operations.d.ts +62 -6
- package/dist/core/operations/provision-operations.d.ts.map +1 -1
- package/dist/core/operations/provision-operations.js +653 -115
- package/dist/core/operations/provision-operations.js.map +1 -1
- package/dist/core/operations/scaffold-apply.d.ts +1 -0
- package/dist/core/operations/scaffold-apply.d.ts.map +1 -1
- package/dist/core/operations/scaffold-apply.js +1 -0
- package/dist/core/operations/scaffold-apply.js.map +1 -1
- package/dist/core/operations/scaffold-machine.d.ts +1 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
- package/dist/core/operations/scaffold-machine.js.map +1 -1
- package/dist/core/operations/scaffold-plan.d.ts +2 -0
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
- package/dist/core/operations/scaffold-plan.js +3 -1
- package/dist/core/operations/scaffold-plan.js.map +1 -1
- package/dist/core/project/agent-instructions.d.ts.map +1 -1
- package/dist/core/project/agent-instructions.js +4 -1
- package/dist/core/project/agent-instructions.js.map +1 -1
- package/dist/core/project/boundaries.d.ts +5 -2
- package/dist/core/project/boundaries.d.ts.map +1 -1
- package/dist/core/project/boundaries.js +51 -20
- package/dist/core/project/boundaries.js.map +1 -1
- package/dist/core/project/capabilities.d.ts +10 -0
- package/dist/core/project/capabilities.d.ts.map +1 -1
- package/dist/core/project/capabilities.js +38 -6
- package/dist/core/project/capabilities.js.map +1 -1
- package/dist/core/project/home-page.d.ts +4 -0
- package/dist/core/project/home-page.d.ts.map +1 -0
- package/dist/core/project/home-page.js +113 -0
- package/dist/core/project/home-page.js.map +1 -0
- package/dist/core/project/mcp-config.d.ts +26 -0
- package/dist/core/project/mcp-config.d.ts.map +1 -0
- package/dist/core/project/mcp-config.js +117 -0
- package/dist/core/project/mcp-config.js.map +1 -0
- package/dist/core/project/react-project.d.ts +1 -1
- package/dist/core/project/react-project.d.ts.map +1 -1
- package/dist/core/project/react-project.js +46 -25
- package/dist/core/project/react-project.js.map +1 -1
- package/dist/core/project/state.d.ts +7 -0
- package/dist/core/project/state.d.ts.map +1 -1
- package/dist/core/project/state.js +86 -0
- package/dist/core/project/state.js.map +1 -1
- package/dist/core/project/workflows.d.ts.map +1 -1
- package/dist/core/project/workflows.js +72 -12
- package/dist/core/project/workflows.js.map +1 -1
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
- package/dist/core/scaffold/auth-generator.js +26 -27
- package/dist/core/scaffold/auth-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +23 -0
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +407 -137
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +13 -0
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
- package/dist/core/scaffold/native-schema-generator.js +294 -91
- package/dist/core/scaffold/native-schema-generator.js.map +1 -1
- package/dist/core/scaffold/openapi-generator.d.ts +5 -0
- package/dist/core/scaffold/openapi-generator.d.ts.map +1 -1
- package/dist/core/scaffold/openapi-generator.js +53 -3
- package/dist/core/scaffold/openapi-generator.js.map +1 -1
- package/dist/core/scaffold/ui-generator.d.ts +0 -3
- package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
- package/dist/core/scaffold/ui-generator.js +68 -7
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts +4 -3
- package/dist/core/verify/index.d.ts.map +1 -1
- package/dist/core/verify/index.js +122 -5
- package/dist/core/verify/index.js.map +1 -1
- package/dist/machine/contracts.d.ts +1 -0
- package/dist/machine/contracts.d.ts.map +1 -1
- package/dist/machine/errors.d.ts +3 -2
- package/dist/machine/errors.d.ts.map +1 -1
- package/dist/machine/errors.js +2 -1
- package/dist/machine/errors.js.map +1 -1
- package/dist/machine/index.d.ts.map +1 -1
- package/dist/machine/index.js +59 -28
- package/dist/machine/index.js.map +1 -1
- package/dist/mcp/index.d.ts +6 -4
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +210 -25
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/version-selection.d.ts +55 -0
- package/dist/mcp/version-selection.d.ts.map +1 -0
- package/dist/mcp/version-selection.js +152 -0
- package/dist/mcp/version-selection.js.map +1 -0
- package/dist/utils/azure-cli.d.ts +56 -0
- package/dist/utils/azure-cli.d.ts.map +1 -0
- package/dist/utils/azure-cli.js +326 -0
- package/dist/utils/azure-cli.js.map +1 -0
- package/dist/utils/azure-diagnostics.d.ts +16 -0
- package/dist/utils/azure-diagnostics.d.ts.map +1 -0
- package/dist/utils/azure-diagnostics.js +68 -0
- package/dist/utils/azure-diagnostics.js.map +1 -0
- package/dist/utils/package-manager.js +2 -2
- package/dist/utils/package-manager.js.map +1 -1
- package/dist/utils/python-uv.d.ts +5 -7
- package/dist/utils/python-uv.d.ts.map +1 -1
- package/dist/utils/python-uv.js +50 -24
- package/dist/utils/python-uv.js.map +1 -1
- package/dist/utils/react-dev.d.ts +4 -0
- package/dist/utils/react-dev.d.ts.map +1 -0
- package/dist/utils/react-dev.js +35 -0
- package/dist/utils/react-dev.js.map +1 -0
- package/dist/utils/semver.d.ts +14 -0
- package/dist/utils/semver.d.ts.map +1 -0
- package/dist/utils/semver.js +61 -0
- package/dist/utils/semver.js.map +1 -0
- package/package.json +6 -3
- package/src/__tests__/__snapshots__/boundaries.test.ts.snap +154 -0
- package/src/__tests__/agent-instructions.test.ts +93 -0
- package/src/__tests__/agent-loop-phase34.test.ts +143 -8
- package/src/__tests__/auth.test.ts +9 -0
- package/src/__tests__/azure-cli.test.ts +202 -0
- package/src/__tests__/boundaries.test.ts +42 -0
- package/src/__tests__/csharp-schema.test.ts +261 -0
- package/src/__tests__/dev.test.ts +13 -6
- package/src/__tests__/fixtures/az wrapper.cmd +2 -0
- package/src/__tests__/fixtures/azure-cli.cjs +153 -0
- package/src/__tests__/fixtures/provision-runner.cjs +28 -0
- package/src/__tests__/generated-project-matrix.test.ts +789 -0
- package/src/__tests__/home-page.test.ts +50 -0
- package/src/__tests__/init.test.ts +116 -0
- package/src/__tests__/mcp-version.test.ts +262 -0
- package/src/__tests__/mcp.test.ts +120 -3
- package/src/__tests__/multi-auth.test.ts +115 -4
- package/src/__tests__/package-manager.test.ts +17 -2
- package/src/__tests__/plan-apply.test.ts +179 -0
- package/src/__tests__/provision-runtime.test.ts +722 -0
- package/src/__tests__/python-uv.test.ts +50 -14
- package/src/__tests__/python-venv.test.ts +48 -0
- package/src/__tests__/react-dev.test.ts +72 -0
- package/src/__tests__/react.test.ts +19 -0
- package/src/__tests__/schema-analysis.test.ts +329 -0
- package/src/cli/commands/add-auth.ts +36 -6
- package/src/cli/commands/configure-mcp.ts +54 -0
- package/src/cli/commands/dev.ts +40 -31
- package/src/cli/commands/init.ts +73 -138
- package/src/cli/commands/migrations.ts +22 -0
- package/src/cli/commands/provision.ts +294 -56
- package/src/cli/commands/scaffold.ts +105 -3
- package/src/cli/index.ts +40 -7
- package/src/core/operations/create-model.ts +51 -13
- package/src/core/operations/migration-generator.ts +14 -1
- package/src/core/operations/model-migration.ts +52 -0
- package/src/core/operations/provision-deployment.ts +183 -0
- package/src/core/operations/provision-operations.ts +662 -154
- package/src/core/operations/scaffold-apply.ts +2 -0
- package/src/core/operations/scaffold-machine.ts +1 -0
- package/src/core/operations/scaffold-plan.ts +5 -1
- package/src/core/project/agent-instructions.ts +4 -1
- package/src/core/project/agent-templates/AGENTS.md +126 -6
- package/src/core/project/agent-templates/CLAUDE.md +14 -1
- package/src/core/project/agent-templates/copilot-instructions.md +15 -1
- package/src/core/project/agent-templates/shared-models.instructions.md +23 -3
- package/src/core/project/boundaries.ts +62 -20
- package/src/core/project/capabilities.ts +49 -6
- package/src/core/project/home-page.ts +114 -0
- package/src/core/project/mcp-config.ts +84 -0
- package/src/core/project/react-project.ts +50 -28
- package/src/core/project/state.ts +70 -0
- package/src/core/project/workflows.ts +72 -12
- package/src/core/scaffold/auth-generator.ts +26 -27
- package/src/core/scaffold/model-parser.ts +413 -129
- package/src/core/scaffold/native-schema-generator.ts +319 -103
- package/src/core/scaffold/openapi-generator.ts +63 -5
- package/src/core/scaffold/ui-generator.ts +70 -8
- package/src/core/verify/index.ts +118 -6
- package/src/machine/contracts.ts +1 -0
- package/src/machine/errors.ts +4 -2
- package/src/machine/index.ts +65 -34
- package/src/mcp/index.ts +221 -31
- package/src/mcp/version-selection.ts +163 -0
- package/src/utils/azure-cli.ts +315 -0
- package/src/utils/azure-diagnostics.ts +75 -0
- package/src/utils/package-manager.ts +2 -2
- package/src/utils/python-uv.ts +53 -33
- package/src/utils/react-dev.ts +31 -0
- package/src/utils/semver.ts +59 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { FrontendFramework } from "../../types";
|
|
2
|
+
import { PackageManager, getCommands } from "../../utils/package-manager";
|
|
3
|
+
import { adaptReactPage } from "../scaffold/react-ui-generator";
|
|
4
|
+
|
|
5
|
+
export function buildHomePage(
|
|
6
|
+
frontend: FrontendFramework,
|
|
7
|
+
pm: PackageManager
|
|
8
|
+
): string {
|
|
9
|
+
const framework = frontend === "react" ? "React" : "Next.js";
|
|
10
|
+
const page = `import { useState } from 'react';
|
|
11
|
+
import Link from 'next/link';
|
|
12
|
+
import { scaffoldConfig } from '@/lib/scaffold-config';
|
|
13
|
+
|
|
14
|
+
export default function Home() {
|
|
15
|
+
const [greetingStatus, setGreetingStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
|
16
|
+
const [message, setMessage] = useState('');
|
|
17
|
+
|
|
18
|
+
const testConnection = async () => {
|
|
19
|
+
setGreetingStatus('loading');
|
|
20
|
+
try {
|
|
21
|
+
const response = await fetch('/api/greet?name=SwallowKit');
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
throw new Error(data.error || \`Server error: \${response.status}\`);
|
|
25
|
+
}
|
|
26
|
+
setMessage(data.message);
|
|
27
|
+
setGreetingStatus('success');
|
|
28
|
+
} catch (error) {
|
|
29
|
+
setMessage(error instanceof Error ? error.message : 'Failed to connect to Azure Functions');
|
|
30
|
+
setGreetingStatus('error');
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-gray-800">
|
|
36
|
+
<div className="container mx-auto px-4 py-12">
|
|
37
|
+
<header className="text-center mb-16">
|
|
38
|
+
<h1 className="text-5xl font-bold text-gray-800 dark:text-white mb-4">
|
|
39
|
+
Welcome to SwallowKit
|
|
40
|
+
</h1>
|
|
41
|
+
<p className="text-xl text-gray-600 dark:text-gray-400">
|
|
42
|
+
${framework} on Azure Static Web Apps + Functions + Cosmos DB — Zod schema sharing
|
|
43
|
+
</p>
|
|
44
|
+
</header>
|
|
45
|
+
|
|
46
|
+
<section className="max-w-2xl mx-auto mb-12">
|
|
47
|
+
<div className="bg-white dark:bg-gray-800 rounded-xl p-8 border border-gray-200 dark:border-gray-700">
|
|
48
|
+
<h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
|
|
49
|
+
Test BFF → Functions Connection
|
|
50
|
+
</h2>
|
|
51
|
+
<button
|
|
52
|
+
onClick={testConnection}
|
|
53
|
+
disabled={greetingStatus === 'loading'}
|
|
54
|
+
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded-lg font-medium transition-colors"
|
|
55
|
+
>
|
|
56
|
+
{greetingStatus === 'loading' ? 'Testing...' : 'Test Connection'}
|
|
57
|
+
</button>
|
|
58
|
+
{greetingStatus === 'success' && (
|
|
59
|
+
<div role="status" className="mt-4 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
|
|
60
|
+
<p className="text-green-800 dark:text-green-200 font-medium">✅ Connection successful!</p>
|
|
61
|
+
<p className="text-green-700 dark:text-green-300 text-sm mt-1">{message}</p>
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
{greetingStatus === 'error' && (
|
|
65
|
+
<div role="alert" className="mt-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
|
|
66
|
+
<p className="text-red-800 dark:text-red-200 font-medium">❌ Connection failed</p>
|
|
67
|
+
<p className="text-red-700 dark:text-red-300 text-sm mt-1">{message}</p>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
</section>
|
|
72
|
+
|
|
73
|
+
{scaffoldConfig.models.length > 0 ? (
|
|
74
|
+
<section className="max-w-6xl mx-auto">
|
|
75
|
+
<h2 className="text-3xl font-bold mb-8 text-gray-900 dark:text-gray-100">Your Models</h2>
|
|
76
|
+
<div className="grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
|
77
|
+
{scaffoldConfig.models.map((model) => (
|
|
78
|
+
<Link
|
|
79
|
+
key={model.name}
|
|
80
|
+
href={model.path}
|
|
81
|
+
className="block p-8 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl hover:shadow-lg hover:border-blue-400 dark:hover:border-blue-600 transition-all"
|
|
82
|
+
>
|
|
83
|
+
<h3 className="text-2xl font-semibold mb-2 text-gray-900 dark:text-gray-100">{model.label}</h3>
|
|
84
|
+
<p className="text-gray-600 dark:text-gray-400">Manage {model.label.toLowerCase()}</p>
|
|
85
|
+
</Link>
|
|
86
|
+
))}
|
|
87
|
+
</div>
|
|
88
|
+
</section>
|
|
89
|
+
) : (
|
|
90
|
+
<section className="max-w-2xl mx-auto text-center">
|
|
91
|
+
<div className="bg-white dark:bg-gray-800 rounded-xl p-12 border border-gray-200 dark:border-gray-700">
|
|
92
|
+
<h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">Get Started</h2>
|
|
93
|
+
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
|
94
|
+
Create your first model with Zod and generate CRUD operations automatically.
|
|
95
|
+
</p>
|
|
96
|
+
<code className="block bg-gray-100 dark:bg-gray-900 p-4 rounded text-left text-sm">
|
|
97
|
+
${getCommands(pm).exec} swallowkit scaffold shared/models/your-model.ts
|
|
98
|
+
</code>
|
|
99
|
+
</div>
|
|
100
|
+
</section>
|
|
101
|
+
)}
|
|
102
|
+
|
|
103
|
+
<footer className="mt-16 text-center text-gray-600 dark:text-gray-400 text-sm">
|
|
104
|
+
<p>Built with SwallowKit</p>
|
|
105
|
+
</footer>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
return frontend === "react"
|
|
112
|
+
? adaptReactPage(page)
|
|
113
|
+
: `'use client'\n\nexport const dynamic = 'force-dynamic';\n\n${page}`;
|
|
114
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-scoped MCP launcher version management.
|
|
3
|
+
*
|
|
4
|
+
* `.mcp.json` intentionally resolves `swallowkit@latest` so that projects created
|
|
5
|
+
* with older releases still get the current stable tools. A prerelease project can
|
|
6
|
+
* be newer than `latest`; in that case the launcher is pinned to the project
|
|
7
|
+
* version until a stable release that understands project-aware delegation catches up.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { spawnSync } from "child_process";
|
|
11
|
+
import * as fs from "fs";
|
|
12
|
+
import * as os from "os";
|
|
13
|
+
import * as path from "path";
|
|
14
|
+
import { compareSemver, isPrerelease, parseSemver } from "../../utils/semver";
|
|
15
|
+
|
|
16
|
+
export const MCP_CONFIG_PATH = ".mcp.json";
|
|
17
|
+
export const MCP_VERSION_ENV = "SWALLOWKIT_MCP_VERSION";
|
|
18
|
+
export const MCP_LATEST = "latest";
|
|
19
|
+
|
|
20
|
+
export interface McpPinDecision {
|
|
21
|
+
version: string;
|
|
22
|
+
reason: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Decide the SWALLOWKIT_MCP_VERSION value for a project that uses `projectVersion`.
|
|
27
|
+
* Uses `latest` unless the project version is newer than the published `latest`.
|
|
28
|
+
* When the registry could not be reached, prereleases are pinned conservatively.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveMcpPinVersion(projectVersion: string, publishedLatest: string | null): McpPinDecision {
|
|
31
|
+
if (!parseSemver(projectVersion)) {
|
|
32
|
+
return { version: MCP_LATEST, reason: `Project version ${projectVersion} is not a valid semantic version.` };
|
|
33
|
+
}
|
|
34
|
+
if (publishedLatest && parseSemver(publishedLatest)) {
|
|
35
|
+
const comparison = compareSemver(projectVersion, publishedLatest)!;
|
|
36
|
+
return comparison > 0
|
|
37
|
+
? { version: projectVersion, reason: `Project version ${projectVersion} is newer than published latest ${publishedLatest}.` }
|
|
38
|
+
: { version: MCP_LATEST, reason: `Published latest ${publishedLatest} is the same as or newer than project version ${projectVersion}.` };
|
|
39
|
+
}
|
|
40
|
+
return isPrerelease(projectVersion)
|
|
41
|
+
? { version: projectVersion, reason: `Published latest is unknown; prerelease ${projectVersion} is pinned so MCP matches the project.` }
|
|
42
|
+
: { version: MCP_LATEST, reason: "Published latest is unknown; stable projects keep latest." };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function lookupPublishedLatestVersion(packageName: string, timeoutMs = 15000): string | null {
|
|
46
|
+
// Run outside the project so its devEngines/packageManager policy does not reject npm.
|
|
47
|
+
const result = spawnSync("npm", ["view", `${packageName}@latest`, "version"], {
|
|
48
|
+
cwd: os.tmpdir(),
|
|
49
|
+
encoding: "utf8",
|
|
50
|
+
timeout: timeoutMs,
|
|
51
|
+
shell: process.platform === "win32",
|
|
52
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
53
|
+
});
|
|
54
|
+
if (result.error || result.status !== 0) return null;
|
|
55
|
+
const version = String(result.stdout).trim().split(/\s+/).pop() ?? "";
|
|
56
|
+
return parseSemver(version) ? version : null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function readMcpConfigVersion(projectDir: string): string | null {
|
|
60
|
+
const configPath = path.join(projectDir, MCP_CONFIG_PATH);
|
|
61
|
+
if (!fs.existsSync(configPath)) return null;
|
|
62
|
+
try {
|
|
63
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
64
|
+
const value = config?.mcpServers?.swallowkit?.env?.[MCP_VERSION_ENV];
|
|
65
|
+
return typeof value === "string" ? value : null;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Update only the SwallowKit MCP version in .mcp.json, preserving all other content. */
|
|
72
|
+
export function writeMcpConfigVersion(projectDir: string, version: string): void {
|
|
73
|
+
const configPath = path.join(projectDir, MCP_CONFIG_PATH);
|
|
74
|
+
if (!fs.existsSync(configPath)) {
|
|
75
|
+
throw new Error(`${MCP_CONFIG_PATH} was not found. Run this command from a SwallowKit project root.`);
|
|
76
|
+
}
|
|
77
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
78
|
+
const server = config?.mcpServers?.swallowkit;
|
|
79
|
+
if (!server || typeof server !== "object") {
|
|
80
|
+
throw new Error(`${MCP_CONFIG_PATH} does not define mcpServers.swallowkit.`);
|
|
81
|
+
}
|
|
82
|
+
server.env = { ...(server.env ?? {}), [MCP_VERSION_ENV]: version };
|
|
83
|
+
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
84
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { BackendLanguage } from "../../types";
|
|
4
|
+
import { buildReactDevWatchIgnoredSource } from "../../utils/react-dev";
|
|
5
|
+
import { DEFAULT_AZURE_PYTHON_VERSION } from "../../utils/python-uv";
|
|
6
|
+
import { buildHomePage } from "./home-page";
|
|
4
7
|
import {
|
|
5
8
|
PackageManager,
|
|
6
9
|
getCommands,
|
|
@@ -45,7 +48,8 @@ export function buildReactSwaConfig() {
|
|
|
45
48
|
|
|
46
49
|
export function createReactProject(
|
|
47
50
|
projectDir: string,
|
|
48
|
-
projectName: string
|
|
51
|
+
projectName: string,
|
|
52
|
+
pm: PackageManager = "pnpm"
|
|
49
53
|
): void {
|
|
50
54
|
write(
|
|
51
55
|
projectDir,
|
|
@@ -117,7 +121,11 @@ export default defineConfig({
|
|
|
117
121
|
'@': fileURLToPath(new URL('.', import.meta.url)),
|
|
118
122
|
'@${projectName}/shared': fileURLToPath(new URL('./shared/index.ts', import.meta.url)),
|
|
119
123
|
} },
|
|
120
|
-
server: {
|
|
124
|
+
server: {
|
|
125
|
+
port: 3000,
|
|
126
|
+
strictPort: true,
|
|
127
|
+
watch: { ignored: ${buildReactDevWatchIgnoredSource("fileURLToPath(new URL('.', import.meta.url))")} },
|
|
128
|
+
},
|
|
121
129
|
build: { outDir: 'dist' },
|
|
122
130
|
});
|
|
123
131
|
`
|
|
@@ -126,9 +134,9 @@ export default defineConfig({
|
|
|
126
134
|
projectDir,
|
|
127
135
|
"index.html",
|
|
128
136
|
`<!doctype html>
|
|
129
|
-
<html lang="en">
|
|
137
|
+
<html lang="en" class="h-full antialiased">
|
|
130
138
|
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>SwallowKit</title></head>
|
|
131
|
-
<body><div id="root"></div><script type="module" src="/src/main.tsx"></script></body>
|
|
139
|
+
<body class="min-h-full flex flex-col"><div id="root" class="flex flex-1 flex-col"></div><script type="module" src="/src/main.tsx"></script></body>
|
|
132
140
|
</html>
|
|
133
141
|
`
|
|
134
142
|
);
|
|
@@ -169,31 +177,34 @@ export default function App() {
|
|
|
169
177
|
write(
|
|
170
178
|
projectDir,
|
|
171
179
|
"src\\Home.tsx",
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
buildHomePage("react", pm)
|
|
181
|
+
);
|
|
182
|
+
write(projectDir, "src\\style.css", `@import "tailwindcss";
|
|
183
|
+
|
|
184
|
+
:root {
|
|
185
|
+
--background: #ffffff;
|
|
186
|
+
--foreground: #171717;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@theme inline {
|
|
190
|
+
--color-background: var(--background);
|
|
191
|
+
--color-foreground: var(--foreground);
|
|
192
|
+
--font-sans: Arial, Helvetica, sans-serif;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@media (prefers-color-scheme: dark) {
|
|
196
|
+
:root {
|
|
197
|
+
--background: #0a0a0a;
|
|
198
|
+
--foreground: #ededed;
|
|
185
199
|
}
|
|
186
|
-
return <main className="container mx-auto p-8">
|
|
187
|
-
<h1 className="text-3xl font-bold mb-4">SwallowKit + React</h1>
|
|
188
|
-
<p>React SPA → SWA managed Functions BFF → standalone Functions → Cosmos DB</p>
|
|
189
|
-
<button className="my-4 bg-blue-600 text-white rounded px-4 py-2" onClick={testConnection}>Test backend connection</button>
|
|
190
|
-
<p role="status">{message}</p>
|
|
191
|
-
<nav>{scaffoldConfig.models.map(model => <p key={model.name}><Link to={model.path}>{model.label}</Link></p>)}</nav>
|
|
192
|
-
</main>;
|
|
193
200
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
201
|
+
|
|
202
|
+
body {
|
|
203
|
+
background: var(--background);
|
|
204
|
+
color: var(--foreground);
|
|
205
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
206
|
+
}
|
|
207
|
+
`);
|
|
197
208
|
write(
|
|
198
209
|
projectDir,
|
|
199
210
|
"lib\\scaffold-config.ts",
|
|
@@ -375,6 +386,17 @@ The BFF stays separate even with \`--no-functions\`; \`--no-swa\` starts only th
|
|
|
375
386
|
direct Vite/BFF servers (no same-origin API routing or SWA authentication).
|
|
376
387
|
Core Tools v4 must already be available. Start Cosmos DB Emulator manually if
|
|
377
388
|
this application needs Cosmos; SwallowKit never starts it.
|
|
389
|
+
${backend === "python" ? `
|
|
390
|
+
Python environments use normal uv discovery and user settings, including installed
|
|
391
|
+
Python, \`.python-version\`, and \`UV_PYTHON\`. Local Python is not pinned to the Azure
|
|
392
|
+
deployment runtime (${DEFAULT_AZURE_PYTHON_VERSION}). Functions uses \`functions/.venv\`; scaffold uses
|
|
393
|
+
\`functions/.codegen-venv\`. Existing environments are never cleared automatically.
|
|
394
|
+
When switching interpreters, explicitly recreate them with uv. Do not disable
|
|
395
|
+
Windows application-control policy to run an unsigned interpreter.
|
|
396
|
+
` : backend === "csharp" ? `
|
|
397
|
+
The .NET isolated backend starts with \`dotnet run\` so extensions are loaded from
|
|
398
|
+
the build output. Vite does not watch \`functions/\` or \`bff/\` build artifacts.
|
|
399
|
+
` : ""}
|
|
378
400
|
|
|
379
401
|
Local BFF settings live in \`bff/local.settings.json\` (gitignored). Backend settings
|
|
380
402
|
live in \`functions/local.settings.json\`. Keep their secrets out of source control.
|
|
@@ -385,7 +407,7 @@ live in \`functions/local.settings.json\`. Keep their secrets out of source cont
|
|
|
385
407
|
\`\`\`sh
|
|
386
408
|
${pm} run build
|
|
387
409
|
${cmd.exec} swallowkit machine verify project
|
|
388
|
-
${cmd.exec} swallowkit machine verify project --checks structure,drift,typecheck,build
|
|
410
|
+
${cmd.exec} swallowkit machine verify project --checks "structure,drift,typecheck,backend-build,build"
|
|
389
411
|
\`\`\`
|
|
390
412
|
|
|
391
413
|
The build compiles shared schemas, checks the SPA, builds static assets, and
|
|
@@ -57,6 +57,76 @@ export function deletePlanState(planId: string, projectRoot: string = process.cw
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function provisionRunPath(runId: string, projectRoot: string): string {
|
|
61
|
+
if (!/^[a-f0-9]{12}$/.test(runId)) throw new Error("Invalid provision run ID.");
|
|
62
|
+
return path.join(projectRoot, SWALLOWKIT_STATE_DIR, "provision", `${runId}.json`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function saveProvisionRun<T extends { runId: string }>(run: T, projectRoot: string = process.cwd()): void {
|
|
66
|
+
ensureStateDir(projectRoot);
|
|
67
|
+
const file = provisionRunPath(run.runId, projectRoot);
|
|
68
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
69
|
+
const temporary = `${file}.tmp-${process.pid}`;
|
|
70
|
+
fs.writeFileSync(temporary, `${JSON.stringify(run, null, 2)}\n`, { encoding: "utf-8", mode: 0o600 });
|
|
71
|
+
fs.renameSync(temporary, file);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function loadProvisionRun<T>(runId: string, projectRoot: string = process.cwd()): T | null {
|
|
75
|
+
const file = provisionRunPath(runId, projectRoot);
|
|
76
|
+
if (!fs.existsSync(file)) return null;
|
|
77
|
+
// Unlike a preview, discarding a corrupt execution checkpoint could replay Azure writes.
|
|
78
|
+
return JSON.parse(fs.readFileSync(file, "utf-8")) as T;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function listProvisionRuns<T>(projectRoot: string = process.cwd()): T[] {
|
|
82
|
+
const directory = path.join(projectRoot, SWALLOWKIT_STATE_DIR, "provision");
|
|
83
|
+
if (!fs.existsSync(directory)) return [];
|
|
84
|
+
return fs.readdirSync(directory).filter((name) => /^[a-f0-9]{12}\.json$/.test(name))
|
|
85
|
+
.map((name) => loadProvisionRun<T>(name.slice(0, -5), projectRoot)!);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** A live local executor must not share an Azure target with another executor. */
|
|
89
|
+
export function lockProvisionTarget(key: string, projectRoot: string = process.cwd()): () => void {
|
|
90
|
+
if (!/^[a-f0-9]{64}$/.test(key)) throw new Error("Invalid provision lock key.");
|
|
91
|
+
const directory = path.join(ensureStateDir(projectRoot), "provision");
|
|
92
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
93
|
+
const file = path.join(directory, `${key}.lock`);
|
|
94
|
+
const create = () => {
|
|
95
|
+
const descriptor = fs.openSync(file, "wx", 0o600);
|
|
96
|
+
try { fs.writeFileSync(descriptor, String(process.pid)); } finally { fs.closeSync(descriptor); }
|
|
97
|
+
};
|
|
98
|
+
try {
|
|
99
|
+
create();
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
102
|
+
const recoveryFile = `${file}.recovery`;
|
|
103
|
+
let descriptor: number;
|
|
104
|
+
try {
|
|
105
|
+
descriptor = fs.openSync(recoveryFile, "wx", 0o600);
|
|
106
|
+
} catch (recoveryError) {
|
|
107
|
+
if ((recoveryError as NodeJS.ErrnoException).code !== "EEXIST") throw recoveryError;
|
|
108
|
+
throw new Error("Provision lock recovery is already in progress. Inspect the checkpoint before retrying.");
|
|
109
|
+
}
|
|
110
|
+
fs.closeSync(descriptor);
|
|
111
|
+
try {
|
|
112
|
+
const pid = Number(fs.readFileSync(file, "utf-8"));
|
|
113
|
+
if (!Number.isInteger(pid) || pid < 1) throw new Error("Invalid provision lock; inspect the checkpoint before removing the lock.");
|
|
114
|
+
try {
|
|
115
|
+
process.kill(pid, 0);
|
|
116
|
+
} catch (probeError) {
|
|
117
|
+
if ((probeError as NodeJS.ErrnoException).code !== "ESRCH") throw probeError;
|
|
118
|
+
fs.unlinkSync(file);
|
|
119
|
+
create();
|
|
120
|
+
return () => fs.unlinkSync(file);
|
|
121
|
+
}
|
|
122
|
+
throw new Error("Another local provision executor is using this Azure target. Inspect its run; do not start a second deployment.");
|
|
123
|
+
} finally {
|
|
124
|
+
fs.unlinkSync(recoveryFile);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return () => fs.unlinkSync(file);
|
|
128
|
+
}
|
|
129
|
+
|
|
60
130
|
const LAST_VERIFY_FILE = "last-verify.json";
|
|
61
131
|
|
|
62
132
|
export function saveLastVerifyState(result: unknown, projectRoot: string = process.cwd()): void {
|
|
@@ -36,10 +36,10 @@ export function buildWorkflowDocs(runCmd: string): WorkflowDoc[] {
|
|
|
36
36
|
title: "Add a new model (autonomous loop)",
|
|
37
37
|
skillName: "swallowkit-add-model",
|
|
38
38
|
skillDescription:
|
|
39
|
-
"Add a new
|
|
39
|
+
"Add a new persistent model, including models with custom APIs, and generate required infrastructure. Derive CRUD Functions, BFF routes, and UI only when needed using the plan/apply/verify loop. Use when asked to add a model, entity, resource, table, or a new CRUD feature.",
|
|
40
40
|
content: `# Workflow: Add a new model
|
|
41
41
|
|
|
42
|
-
Goal: add a
|
|
42
|
+
Goal: add a persistent model and generate its required infrastructure, even with custom APIs. A hand-written model alone is not complete.
|
|
43
43
|
|
|
44
44
|
## Responsibility boundary
|
|
45
45
|
|
|
@@ -51,15 +51,43 @@ Goal: add a data model and derive CRUD/BFF/UI/infra deterministically.
|
|
|
51
51
|
|
|
52
52
|
1. Create the model file (deterministic template):
|
|
53
53
|
\`${runCmd} swallowkit machine generate model <name> --overwrite never\`
|
|
54
|
+
- This is the machine equivalent of \`${runCmd} swallowkit create-model <name>\`.
|
|
55
|
+
Preserve existing schemas; model creation alone does not generate a Cosmos DB container.
|
|
54
56
|
2. Edit \`shared/models/<name>.ts\` — add domain fields (free-form authoring).
|
|
55
|
-
|
|
57
|
+
- Native C#/Python types follow Zod, not names: \`z.iso.datetime()\` for dates, \`z.number().int()\` for integers.
|
|
58
|
+
- \`<model>Id\` fields reference a model only when it exists in \`shared/models\`; otherwise declare
|
|
59
|
+
\`export const references = { fieldId: 'Model' }\` (or \`false\` to opt out).
|
|
60
|
+
3. When CRUD is needed, compute the change plan (writes nothing):
|
|
56
61
|
\`${runCmd} swallowkit machine plan scaffold <name>\`
|
|
57
62
|
- \`status: "requires-human"\` → review \`data.conflicts\`; only proceed with \`--approve\` if overwriting is intended.
|
|
58
|
-
|
|
63
|
+
- Must not change Bicep/infrastructure? Add \`--skip-infra\` to plan and apply. Nothing under \`infra/\` changes and
|
|
64
|
+
the plan warns \`infra-skipped\`; later run \`${runCmd} swallowkit migrations add-model <name>\` to add only the container migration.
|
|
65
|
+
State the reason and pending infrastructure work; a passing verify does not mean the model is complete or deployment-ready.
|
|
66
|
+
- Read \`data.warnings\`. \`user-scoped-data-without-auth\` means generated CRUD would expose every user's documents;
|
|
67
|
+
add auth (\`swallowkit-add-auth\`) and owner-scoped ai-authored endpoints before exposing the model.
|
|
68
|
+
- \`auth-policy-without-auth\` blocks generation before file writes: configure authentication with plan/apply auth,
|
|
69
|
+
then re-plan. Do not remove a required policy to bypass this prerequisite.
|
|
70
|
+
For named \`auth.schemes\`, define named authorization policies selecting schemes/roles and reference them from
|
|
71
|
+
\`authPolicy.policy\` or read/write names; role-only declarations block with \`auth-policy-requires-named-policy\`.
|
|
72
|
+
Single-provider \`auth.provider\` supports role lists only; named policies block with \`auth-named-policy-requires-named-scheme\`.
|
|
73
|
+
- Scaffold appends missing exports to \`shared/index.ts\` for hand-authored models too.
|
|
74
|
+
Manual exports are allowed; no managed markers exist in that file.
|
|
75
|
+
- If custom APIs replace CRUD, skip scaffold (including \`--api-only\`, which still generates CRUD).
|
|
76
|
+
Review \`${runCmd} swallowkit migrations add-model <name> --dry-run\`, then run
|
|
77
|
+
\`${runCmd} swallowkit migrations add-model <name>\` to generate only the container migration and generation records.
|
|
78
|
+
Keep the schema exported from \`shared/index.ts\`. External connector models need their connector mapping, not a Cosmos container.
|
|
79
|
+
4. Apply the reviewed CRUD scaffold plan, if needed:
|
|
59
80
|
\`${runCmd} swallowkit machine apply scaffold --plan <planId>\`
|
|
60
81
|
5. Verify:
|
|
61
|
-
|
|
82
|
+
- Inspect \`machine inspect entities\`, \`machine inspect infra\`, \`machine inspect artifacts\`, and \`machine inspect drift\`.
|
|
83
|
+
Match the model's container name and partition key to its Bicep, \`sourceModel\` in
|
|
84
|
+
\`infra/migrations/manifest.json\`, and \`.swallowkit/artifacts.json\`.
|
|
85
|
+
For a baseline-owned container, verify the tracked definition and \`infra/main.bicep\` wiring instead of requiring a new migration.
|
|
86
|
+
Do not hand-edit a checksum-locked baseline or an existing migration.
|
|
87
|
+
- Run \`${runCmd} swallowkit machine verify project\`.
|
|
62
88
|
- \`summary.done: false\` → \`${runCmd} swallowkit machine explain failure\` and repair, then re-verify.
|
|
89
|
+
- Required infrastructure still missing? Report the reason and remaining work, not completion or deployment readiness.
|
|
90
|
+
Local generation does not provision Azure; follow the separate provision workflow and human approval gates.
|
|
63
91
|
6. If \`dev-seeds/\` exists, update seed JSON for the new model. Seeds are only
|
|
64
92
|
applied by \`${runCmd} swallowkit dev --seed-env <environment>\` — editing the
|
|
65
93
|
JSON alone does nothing.
|
|
@@ -153,7 +181,7 @@ the scoping key from the verified principal, never from the request body.
|
|
|
153
181
|
title: "Verify and repair loop",
|
|
154
182
|
skillName: "swallowkit-verify-repair",
|
|
155
183
|
skillDescription:
|
|
156
|
-
"Converge a SwallowKit project to a verified state: run structure/drift/typecheck (and build/lint/test) checks, read evidence with explain failure, repair, and repeat. Use after any code or schema change, when verification fails, when the build/lint/tests are broken, or before finishing a task.",
|
|
184
|
+
"Converge a SwallowKit project to a verified state: run structure/drift/typecheck/backend-build (and build/lint/test) checks, read evidence with explain failure, repair, and repeat. Use after any code or schema change, when verification fails, when the build/lint/tests are broken, or before finishing a task.",
|
|
157
185
|
content: `# Workflow: Verify and repair
|
|
158
186
|
|
|
159
187
|
Goal: converge the project to a verified state after any change.
|
|
@@ -161,14 +189,16 @@ Goal: converge the project to a verified state after any change.
|
|
|
161
189
|
## Loop
|
|
162
190
|
|
|
163
191
|
1. \`${runCmd} swallowkit machine verify project\`
|
|
164
|
-
- default checks: structure, drift, typecheck (+ project-specific checks from swallowkit.config \`verify.checks\`)
|
|
165
|
-
-
|
|
192
|
+
- default checks: structure, drift, typecheck, backend-build (+ project-specific checks from swallowkit.config \`verify.checks\`)
|
|
193
|
+
- backend-build: standalone C# \`dotnet build\`, TypeScript \`tsc --noEmit\`, Python 3 \`compileall\` (syntax only).
|
|
194
|
+
Missing toolchains are skipped with a reason, not proof of a working backend. Restore them and re-run.
|
|
195
|
+
- opt-in heavier checks: \`--checks "structure,drift,typecheck,backend-build,build,lint,test"\` (quote CSV in PowerShell)
|
|
166
196
|
2. If \`summary.done\` is false:
|
|
167
197
|
- \`${runCmd} swallowkit machine explain failure\` → evidence (logTail, findings, violations) and suggestedActions per check.
|
|
168
198
|
- Repair:
|
|
169
199
|
- \`drift\` failures → follow each finding's \`repairAction\` (usually re-run plan/apply scaffold).
|
|
170
200
|
- \`structure\` failures → follow \`suggestedFix\` per violation.
|
|
171
|
-
- \`typecheck\`/\`build\`/\`lint\`/\`test\` failures → fix the code
|
|
201
|
+
- \`typecheck\`/\`backend-build\`/\`build\`/\`lint\`/\`test\` failures → fix the source code, not generated artifacts.
|
|
172
202
|
3. Repeat until \`summary.done: true\`.
|
|
173
203
|
|
|
174
204
|
## Exit criteria
|
|
@@ -195,9 +225,14 @@ Goal: bootstrap Azure once, then apply only pending versioned infrastructure mig
|
|
|
195
225
|
\`status: "requires-human"\` and \`requiresApproval: true\` by design.
|
|
196
226
|
- Never pass \`--approve\` to \`apply provision\` unless a human explicitly
|
|
197
227
|
instructed you to provision in this session.
|
|
228
|
+
- This plan/apply workflow is for agents. Humans normally run \`swallowkit provision\`
|
|
229
|
+
directly; do not switch to that command to bypass the agent approval gate.
|
|
198
230
|
- Inspect infra locally first — no \`az\` calls are needed for inspection.
|
|
199
231
|
- Never edit an applied migration. Create a corrective migration instead.
|
|
200
232
|
- Never use \`--reconcile\` for an ordinary model or resource addition.
|
|
233
|
+
- A local az error/timeout or Ctrl+C does not prove Azure failed. Never
|
|
234
|
+
re-submit, cancel, or delete a deployment to repair a wait or bookkeeping failure.
|
|
235
|
+
- Respect \`status\`, \`outcome\`, and \`nextActions\`, not just \`ok\`.
|
|
201
236
|
|
|
202
237
|
## Steps
|
|
203
238
|
|
|
@@ -205,7 +240,7 @@ Goal: bootstrap Azure once, then apply only pending versioned infrastructure mig
|
|
|
205
240
|
\`${runCmd} swallowkit machine inspect infra\`
|
|
206
241
|
- review parameters, modules, outputs, and container wiring warnings.
|
|
207
242
|
2. Compute the provisioning plan (no resources are created):
|
|
208
|
-
\`${runCmd} swallowkit machine plan provision -g <resource-group> --location <region>\`
|
|
243
|
+
\`${runCmd} swallowkit machine plan provision -g <resource-group> --location <region> --swa-location <swa-region>\`
|
|
209
244
|
- the plan reports \`bootstrap\`, \`migrate\`, \`noop\`, or \`adoption-required\`.
|
|
210
245
|
- it lists only pending migrations and the exact \`az\` commands that would run.
|
|
211
246
|
- for an older untagged environment, first run \`${runCmd} swallowkit migrations init --legacy-baseline\`, then plan with \`--adopt-existing --baseline 0 --what-if\`.
|
|
@@ -213,12 +248,35 @@ Goal: bootstrap Azure once, then apply only pending versioned infrastructure mig
|
|
|
213
248
|
3. Present the plan to the human and wait for explicit approval.
|
|
214
249
|
4. Only after approval:
|
|
215
250
|
\`${runCmd} swallowkit machine apply provision --plan <planId> --approve\`
|
|
216
|
-
5.
|
|
251
|
+
5. If \`in-progress\`, \`unknown\`, or \`partial\`, retain the execution
|
|
252
|
+
checkpoint and read/wait for the original deployment:
|
|
253
|
+
\`${runCmd} swallowkit machine inspect provision --plan <runId> --wait-seconds 300\`.
|
|
254
|
+
Inspection never writes Azure tags or the local ledger.
|
|
255
|
+
6. After fresh explicit human approval, resume:
|
|
256
|
+
\`${runCmd} swallowkit machine apply provision --plan <runId> --approve\`.
|
|
257
|
+
Attempted deployments are not re-submitted; remaining reviewed steps and
|
|
258
|
+
tags/ledger finish only after Azure \`Succeeded\`.
|
|
259
|
+
7. Report completion only when \`status: "complete"\` and
|
|
260
|
+
\`outcome: "succeeded"\`. Never report deployment output secrets.
|
|
261
|
+
|
|
262
|
+
For an older untracked bootstrap, plan with \`--resume-deployment <name>\`
|
|
263
|
+
and the original RG, subscription and regions. Recovery compares its ARM
|
|
264
|
+
template and non-secure parameters to local Bicep before approval. Mismatch,
|
|
265
|
+
unreadable evidence or secure parameters block; do not work around this by
|
|
266
|
+
using reconcile/adoption or replacing the migration manifest.
|
|
217
267
|
|
|
218
268
|
## Terminal states
|
|
219
269
|
|
|
220
270
|
\`apply provision\` without \`--approve\` fails with \`approval-required\` —
|
|
221
271
|
this is the guardrail working as intended, not an error to work around.
|
|
272
|
+
|
|
273
|
+
Azure \`Running\`/\`Accepted\` means \`in-progress\`. An unreadable or missing
|
|
274
|
+
deployment means \`unknown\`, not Azure failure. Azure \`Failed\`/\`Canceled\`
|
|
275
|
+
returns sanitized Azure and operation causes. Azure \`Succeeded\` with incomplete
|
|
276
|
+
tag/ledger writes means \`partial\`; resume bookkeeping without redeploying.
|
|
277
|
+
The default wait is 300 seconds (0-3600 via \`--wait-seconds\`), not unlimited.
|
|
278
|
+
Warning-only stderr with exit 0 is accepted. Diagnostics report local exit,
|
|
279
|
+
signal, elapsed time and truncated/redacted output separately from Azure state.
|
|
222
280
|
`,
|
|
223
281
|
};
|
|
224
282
|
|
|
@@ -247,7 +305,9 @@ under \`.github/skills/\` so skills-aware agents load them automatically.
|
|
|
247
305
|
|
|
248
306
|
- **ai-authored** (write freely): \`shared/models/\`, business logic, custom pages/components, tests.
|
|
249
307
|
- **deterministic** (never hand-edit): artifacts generated by SwallowKit (see \`.swallowkit/artifacts.json\`) and \`.swallowkit/\` metadata. Change models/config and regenerate via plan/apply.
|
|
250
|
-
- **shared
|
|
308
|
+
- **shared**: \`shared/index.ts\` allows manual exports (no markers; SwallowKit appends missing entries).
|
|
309
|
+
For Python \`functions/function_app.py\`, edit outside the registration marker.
|
|
310
|
+
- **infrastructure**: baseline \`infra/\` files are checksum-locked by \`infra/migrations/manifest.json\`; add resources as new migrations (\`swallowkit create-migration <slug>\`, \`swallowkit migrations add-model <model>\`).
|
|
251
311
|
|
|
252
312
|
Full machine-readable contract: \`${runCmd} swallowkit machine inspect boundaries\`
|
|
253
313
|
`,
|