swallowkit 1.1.2 → 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.
- package/README.ja.md +61 -7
- package/README.md +61 -7
- package/dist/cli/commands/add-auth.d.ts.map +1 -1
- package/dist/cli/commands/add-auth.js +103 -27
- 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 +5 -2
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +143 -67
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +14 -7
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +149 -170
- 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 +5 -1
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +123 -18
- 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 +49 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +2 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +19 -0
- package/dist/core/config.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 +2 -1
- package/dist/core/project/agent-instructions.d.ts.map +1 -1
- package/dist/core/project/agent-instructions.js +15 -0
- 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 +17 -0
- package/dist/core/project/capabilities.d.ts.map +1 -1
- package/dist/core/project/capabilities.js +45 -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/manifest.d.ts +3 -1
- package/dist/core/project/manifest.d.ts.map +1 -1
- package/dist/core/project/manifest.js +47 -25
- package/dist/core/project/manifest.js.map +1 -1
- 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 +26 -0
- package/dist/core/project/react-project.d.ts.map +1 -0
- package/dist/core/project/react-project.js +443 -0
- package/dist/core/project/react-project.js.map +1 -0
- 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/validation.d.ts.map +1 -1
- package/dist/core/project/validation.js +32 -10
- package/dist/core/project/validation.js.map +1 -1
- package/dist/core/project/workflows.d.ts.map +1 -1
- package/dist/core/project/workflows.js +53 -7
- 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/react-bff-generator.d.ts +10 -0
- package/dist/core/scaffold/react-bff-generator.d.ts.map +1 -0
- package/dist/core/scaffold/react-bff-generator.js +252 -0
- package/dist/core/scaffold/react-bff-generator.js.map +1 -0
- package/dist/core/scaffold/react-ui-generator.d.ts +6 -0
- package/dist/core/scaffold/react-ui-generator.d.ts.map +1 -0
- package/dist/core/scaffold/react-ui-generator.js +41 -0
- package/dist/core/scaffold/react-ui-generator.js.map +1 -0
- 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/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/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 +61 -30
- 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/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- 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.d.ts +2 -2
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +6 -3
- 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 +7 -4
- package/src/__tests__/__snapshots__/boundaries.test.ts.snap +154 -0
- package/src/__tests__/agent-instructions.test.ts +6 -1
- 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 +145 -5
- 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 +838 -0
- package/src/__tests__/release-metadata.test.ts +121 -0
- package/src/__tests__/schema-analysis.test.ts +329 -0
- package/src/cli/commands/add-auth.ts +103 -29
- package/src/cli/commands/configure-mcp.ts +54 -0
- package/src/cli/commands/dev.ts +145 -73
- package/src/cli/commands/init.ts +147 -172
- package/src/cli/commands/migrations.ts +22 -0
- package/src/cli/commands/provision.ts +294 -56
- package/src/cli/commands/scaffold.ts +145 -22
- package/src/cli/index.ts +46 -10
- package/src/core/config.ts +21 -1
- 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 +17 -1
- package/src/core/project/agent-templates/AGENTS.md +113 -15
- package/src/core/project/agent-templates/CLAUDE.md +14 -1
- package/src/core/project/agent-templates/bff-routes.instructions.md +5 -3
- package/src/core/project/agent-templates/copilot-instructions.md +15 -1
- package/src/core/project/agent-templates/shared-models.instructions.md +11 -3
- package/src/core/project/boundaries.ts +62 -20
- package/src/core/project/capabilities.ts +63 -6
- package/src/core/project/home-page.ts +114 -0
- package/src/core/project/manifest.ts +52 -27
- package/src/core/project/mcp-config.ts +84 -0
- package/src/core/project/react-project.ts +529 -0
- package/src/core/project/state.ts +70 -0
- package/src/core/project/validation.ts +32 -10
- package/src/core/project/workflows.ts +53 -7
- 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/react-bff-generator.ts +292 -0
- package/src/core/scaffold/react-ui-generator.ts +51 -0
- package/src/core/scaffold/ui-generator.ts +70 -8
- package/src/core/verify/index.ts +118 -6
- package/src/index.ts +3 -1
- package/src/machine/contracts.ts +1 -0
- package/src/machine/errors.ts +4 -2
- package/src/machine/index.ts +67 -36
- package/src/mcp/index.ts +221 -31
- package/src/mcp/version-selection.ts +163 -0
- package/src/types/index.ts +4 -0
- package/src/utils/azure-cli.ts +315 -0
- package/src/utils/azure-diagnostics.ts +75 -0
- package/src/utils/package-manager.ts +7 -4
- 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,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate only the Cosmos DB container migration for a model. Used after
|
|
3
|
+
* `scaffold --skip-infra`, when infrastructure changes become allowed.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { resolveModelPath } from "../../cli/commands/scaffold";
|
|
7
|
+
import { MachineCommandError } from "../../machine/errors";
|
|
8
|
+
import { parseModelFile } from "../scaffold/model-parser";
|
|
9
|
+
import { recordSessionOperations } from "../project/artifacts";
|
|
10
|
+
import { FileOperationSession } from "./file-session";
|
|
11
|
+
import { generateModelContainerMigration } from "./migration-generator";
|
|
12
|
+
|
|
13
|
+
export interface ModelMigrationResult {
|
|
14
|
+
model: string;
|
|
15
|
+
dryRun: boolean;
|
|
16
|
+
migration: { version: number; template: string } | null;
|
|
17
|
+
reason?: string;
|
|
18
|
+
operations: Array<{ path: string; action: string }>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function addModelContainerMigration(
|
|
22
|
+
modelInput: string,
|
|
23
|
+
options: { dryRun?: boolean; projectRoot?: string } = {}
|
|
24
|
+
): Promise<ModelMigrationResult> {
|
|
25
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
26
|
+
const modelInfo = await parseModelFile(resolveModelPath(modelInput));
|
|
27
|
+
if (modelInfo.connectorConfig) {
|
|
28
|
+
throw new MachineCommandError(
|
|
29
|
+
"connector-model-has-no-container",
|
|
30
|
+
`${modelInfo.name} uses connector ${modelInfo.connectorConfig.connector}; connector models do not get a Cosmos DB container.`,
|
|
31
|
+
undefined,
|
|
32
|
+
"failed"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const session = new FileOperationSession(options.dryRun ? "collect" : "commit", projectRoot);
|
|
37
|
+
const migration = generateModelContainerMigration(modelInfo, session);
|
|
38
|
+
if (!options.dryRun) {
|
|
39
|
+
recordSessionOperations(session.operations, {
|
|
40
|
+
schemaHash: modelInfo.semanticFingerprint,
|
|
41
|
+
sourceModel: modelInfo.name,
|
|
42
|
+
}, projectRoot);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
model: modelInfo.name,
|
|
47
|
+
dryRun: Boolean(options.dryRun),
|
|
48
|
+
migration: migration ? { version: migration.version, template: migration.template } : null,
|
|
49
|
+
...(migration ? {} : { reason: "The container is already owned by the legacy migration baseline." }),
|
|
50
|
+
operations: session.operations.map((operation) => ({ path: operation.path, action: operation.action })),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { runAzCliAsync } from "../../utils/azure-cli";
|
|
2
|
+
import { AzureCliDiagnostic, azureCliDiagnostic, redactAzureText } from "../../utils/azure-diagnostics";
|
|
3
|
+
|
|
4
|
+
export const DEPLOYMENT_QUERY = "{id:id,name:name,state:properties.provisioningState,correlationId:properties.correlationId,templateHash:properties.templateHash,timestamp:properties.timestamp,error:properties.error}";
|
|
5
|
+
export const DEFAULT_PROVISION_WAIT_SECONDS = 300;
|
|
6
|
+
export const MAX_PROVISION_WAIT_SECONDS = 3_600;
|
|
7
|
+
/** Consecutive transient state-read failures tolerated while the human CLI waits for a terminal state. */
|
|
8
|
+
export const UNBOUNDED_WAIT_TRANSIENT_RETRIES = 24;
|
|
9
|
+
|
|
10
|
+
export interface DeploymentReference {
|
|
11
|
+
subscription: string;
|
|
12
|
+
resourceGroup: string;
|
|
13
|
+
name: string;
|
|
14
|
+
id?: string;
|
|
15
|
+
correlationId?: string;
|
|
16
|
+
templateHash?: string;
|
|
17
|
+
/** Do not mistake a previous deployment under the same name for this submission. */
|
|
18
|
+
previousCorrelationId?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface AzureDeploymentError {
|
|
22
|
+
code: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface DeploymentObservation {
|
|
27
|
+
state: "Accepted" | "Running" | "Succeeded" | "Failed" | "Canceled" | "unknown";
|
|
28
|
+
reference: DeploymentReference;
|
|
29
|
+
reason?: "not-found" | "query-failed" | "invalid-response" | "identity-changed" | "not-yet-observed" | "cancelled";
|
|
30
|
+
errors: AzureDeploymentError[];
|
|
31
|
+
diagnostic: AzureCliDiagnostic;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function record(value: unknown): Record<string, unknown> | null {
|
|
35
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function extractAzureErrors(value: unknown, secretValues: readonly string[] = []): AzureDeploymentError[] {
|
|
39
|
+
const result: AzureDeploymentError[] = [];
|
|
40
|
+
const visit = (entry: unknown, depth: number) => {
|
|
41
|
+
if (depth > 8 || result.length >= 20) return;
|
|
42
|
+
if (typeof entry === "string") {
|
|
43
|
+
try { visit(JSON.parse(entry), depth + 1); } catch { /* Only structured Azure errors are projected. */ }
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const item = record(entry);
|
|
47
|
+
if (!item) return;
|
|
48
|
+
if (typeof item.code === "string" && typeof item.message === "string") {
|
|
49
|
+
result.push({ code: redactAzureText(item.code, secretValues).slice(0, 200), message: redactAzureText(item.message, secretValues).slice(0, 2_000) });
|
|
50
|
+
}
|
|
51
|
+
if (item.error) visit(item.error, depth + 1);
|
|
52
|
+
if (Array.isArray(item.details)) item.details.forEach((detail) => visit(detail, depth + 1));
|
|
53
|
+
};
|
|
54
|
+
visit(value, 0);
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function deploymentId(reference: DeploymentReference): string {
|
|
59
|
+
return reference.id ?? `/subscriptions/${reference.subscription}/resourceGroups/${reference.resourceGroup}/providers/Microsoft.Resources/deployments/${reference.name}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function queryAzureDeployment(
|
|
63
|
+
reference: DeploymentReference,
|
|
64
|
+
options: { timeoutMs?: number; signal?: AbortSignal; secretValues?: readonly string[] } = {}
|
|
65
|
+
): Promise<DeploymentObservation> {
|
|
66
|
+
const result = await runAzCliAsync([
|
|
67
|
+
"deployment", "group", "show", "--name", reference.name,
|
|
68
|
+
"--resource-group", reference.resourceGroup, "--subscription", reference.subscription,
|
|
69
|
+
"--query", DEPLOYMENT_QUERY, "-o", "json",
|
|
70
|
+
], { timeout: options.timeoutMs ?? 30_000, signal: options.signal });
|
|
71
|
+
const diagnostic = azureCliDiagnostic(result, "deployment-state", options.secretValues);
|
|
72
|
+
const unknown = (reason: DeploymentObservation["reason"]): DeploymentObservation => ({
|
|
73
|
+
state: "unknown", reference, reason, errors: [], diagnostic,
|
|
74
|
+
});
|
|
75
|
+
if (result.status !== 0 || result.failureKind) {
|
|
76
|
+
if (result.failureKind === "cancelled") return unknown("cancelled");
|
|
77
|
+
return unknown(/DeploymentNotFound|ResourceGroupNotFound/i.test(`${result.stderr}\n${result.stdout}`) ? "not-found" : "query-failed");
|
|
78
|
+
}
|
|
79
|
+
if (result.stdoutTruncated) return unknown("invalid-response");
|
|
80
|
+
let parsed: Record<string, unknown> | null;
|
|
81
|
+
try { parsed = record(JSON.parse(result.stdout)); } catch { return unknown("invalid-response"); }
|
|
82
|
+
if (!parsed || typeof parsed.id !== "string" || typeof parsed.state !== "string" || typeof parsed.correlationId !== "string") {
|
|
83
|
+
return unknown("invalid-response");
|
|
84
|
+
}
|
|
85
|
+
if (parsed.id.toLowerCase() !== deploymentId(reference).toLowerCase()
|
|
86
|
+
|| (reference.correlationId && parsed.correlationId !== reference.correlationId)
|
|
87
|
+
|| (reference.templateHash && parsed.templateHash !== reference.templateHash)) {
|
|
88
|
+
return unknown("identity-changed");
|
|
89
|
+
}
|
|
90
|
+
if (reference.previousCorrelationId && parsed.correlationId === reference.previousCorrelationId) {
|
|
91
|
+
return unknown("not-yet-observed");
|
|
92
|
+
}
|
|
93
|
+
const states: Record<string, DeploymentObservation["state"]> = {
|
|
94
|
+
accepted: "Accepted", running: "Running", creating: "Running", updating: "Running",
|
|
95
|
+
succeeded: "Succeeded", failed: "Failed", canceled: "Canceled", cancelled: "Canceled",
|
|
96
|
+
};
|
|
97
|
+
const state = states[parsed.state.toLowerCase()];
|
|
98
|
+
if (!state) return unknown("invalid-response");
|
|
99
|
+
return {
|
|
100
|
+
state,
|
|
101
|
+
reference: {
|
|
102
|
+
...reference,
|
|
103
|
+
id: parsed.id,
|
|
104
|
+
correlationId: parsed.correlationId,
|
|
105
|
+
...(typeof parsed.templateHash === "string" ? { templateHash: parsed.templateHash } : {}),
|
|
106
|
+
},
|
|
107
|
+
errors: extractAzureErrors(parsed.error, options.secretValues),
|
|
108
|
+
diagnostic,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function waitForAzureDeployment(
|
|
113
|
+
reference: DeploymentReference,
|
|
114
|
+
options: {
|
|
115
|
+
deadline: number;
|
|
116
|
+
signal?: AbortSignal;
|
|
117
|
+
pollIntervalMs?: number;
|
|
118
|
+
onObservation?: (observation: DeploymentObservation) => void;
|
|
119
|
+
secretValues?: readonly string[];
|
|
120
|
+
queryTimeoutMs?: number;
|
|
121
|
+
/**
|
|
122
|
+
* Consecutive non-terminal "unknown" reads (query failure, not yet visible) to keep polling through.
|
|
123
|
+
* Required for an unbounded deadline so a missing deployment cannot be polled forever.
|
|
124
|
+
*/
|
|
125
|
+
transientRetries?: number;
|
|
126
|
+
}
|
|
127
|
+
): Promise<DeploymentObservation> {
|
|
128
|
+
let observation: DeploymentObservation;
|
|
129
|
+
let unknownReads = 0;
|
|
130
|
+
do {
|
|
131
|
+
const remaining = options.deadline - Date.now();
|
|
132
|
+
// Even when the wait budget is exhausted, one bounded read can identify an accepted submission.
|
|
133
|
+
observation = await queryAzureDeployment(reference, {
|
|
134
|
+
timeoutMs: remaining > 0 ? Math.min(options.queryTimeoutMs ?? 30_000, remaining) : options.queryTimeoutMs ?? 30_000,
|
|
135
|
+
signal: options.signal,
|
|
136
|
+
secretValues: options.secretValues,
|
|
137
|
+
});
|
|
138
|
+
if (observation.state !== "unknown") reference = observation.reference;
|
|
139
|
+
options.onObservation?.(observation);
|
|
140
|
+
if (observation.state === "Succeeded" || observation.state === "Failed" || observation.state === "Canceled") return observation;
|
|
141
|
+
if (observation.state === "unknown") {
|
|
142
|
+
unknownReads += 1;
|
|
143
|
+
const retryable = observation.reason === "not-yet-observed" || observation.reason === "not-found"
|
|
144
|
+
|| (options.transientRetries !== undefined && (observation.reason === "query-failed" || observation.reason === "invalid-response"));
|
|
145
|
+
if (!retryable) return observation;
|
|
146
|
+
if (!Number.isFinite(options.deadline) && unknownReads > (options.transientRetries ?? 0)) return observation;
|
|
147
|
+
} else {
|
|
148
|
+
unknownReads = 0;
|
|
149
|
+
}
|
|
150
|
+
if (Date.now() >= options.deadline || options.signal?.aborted) return observation;
|
|
151
|
+
await new Promise<void>((resolve) => {
|
|
152
|
+
const done = () => {
|
|
153
|
+
clearTimeout(timer);
|
|
154
|
+
options.signal?.removeEventListener("abort", done);
|
|
155
|
+
resolve();
|
|
156
|
+
};
|
|
157
|
+
const timer = setTimeout(done, Math.min(options.pollIntervalMs ?? 5_000, Math.max(options.deadline - Date.now(), 0)));
|
|
158
|
+
options.signal?.addEventListener("abort", done, { once: true });
|
|
159
|
+
if (options.signal?.aborted) done();
|
|
160
|
+
});
|
|
161
|
+
} while (!options.signal?.aborted);
|
|
162
|
+
return observation;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export async function queryDeploymentOperationErrors(reference: DeploymentReference, secretValues: readonly string[] = [], signal?: AbortSignal): Promise<{
|
|
166
|
+
errors: AzureDeploymentError[];
|
|
167
|
+
diagnostic: AzureCliDiagnostic;
|
|
168
|
+
}> {
|
|
169
|
+
const result = await runAzCliAsync([
|
|
170
|
+
"deployment", "operation", "group", "list",
|
|
171
|
+
"--resource-group", reference.resourceGroup, "--name", reference.name, "--subscription", reference.subscription,
|
|
172
|
+
"--query", "[?properties.provisioningState=='Failed' || properties.provisioningState=='Canceled'].{error:properties.statusMessage}",
|
|
173
|
+
"-o", "json",
|
|
174
|
+
], { timeout: 30_000, signal });
|
|
175
|
+
const errors: AzureDeploymentError[] = [];
|
|
176
|
+
if (result.status === 0 && !result.failureKind && !result.stdoutTruncated) {
|
|
177
|
+
try {
|
|
178
|
+
const parsed: unknown = JSON.parse(result.stdout);
|
|
179
|
+
if (Array.isArray(parsed)) parsed.slice(0, 20).forEach((item) => errors.push(...extractAzureErrors(item, secretValues)));
|
|
180
|
+
} catch { /* The separate diagnostic explicitly records an unreadable operations response. */ }
|
|
181
|
+
}
|
|
182
|
+
return { errors: errors.slice(0, 20), diagnostic: azureCliDiagnostic(result, "deployment-operations", secretValues) };
|
|
183
|
+
}
|