alchemy 0.1.11 → 0.1.12

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 (277) hide show
  1. package/README.md +324 -0
  2. package/lib/apply.d.ts +2 -2
  3. package/lib/apply.d.ts.map +1 -1
  4. package/lib/apply.js +2 -2
  5. package/lib/apply.js.map +1 -1
  6. package/lib/aws/index.d.ts +1 -0
  7. package/lib/aws/index.d.ts.map +1 -1
  8. package/lib/aws/index.js +1 -0
  9. package/lib/aws/index.js.map +1 -1
  10. package/lib/aws/ses.d.ts +75 -0
  11. package/lib/aws/ses.d.ts.map +1 -0
  12. package/lib/aws/ses.js +197 -0
  13. package/lib/aws/ses.js.map +1 -0
  14. package/lib/cloudflare/api.d.ts +84 -0
  15. package/lib/cloudflare/api.d.ts.map +1 -0
  16. package/lib/cloudflare/api.js +178 -0
  17. package/lib/cloudflare/api.js.map +1 -0
  18. package/lib/cloudflare/asset-manifest.d.ts +15 -0
  19. package/lib/cloudflare/asset-manifest.d.ts.map +1 -0
  20. package/lib/cloudflare/asset-manifest.js +2 -0
  21. package/lib/cloudflare/asset-manifest.js.map +1 -0
  22. package/lib/cloudflare/auth.d.ts +52 -0
  23. package/lib/cloudflare/auth.d.ts.map +1 -0
  24. package/lib/cloudflare/auth.js +114 -0
  25. package/lib/cloudflare/auth.js.map +1 -0
  26. package/lib/cloudflare/bindings.d.ts +244 -0
  27. package/lib/cloudflare/bindings.d.ts.map +1 -0
  28. package/lib/cloudflare/bindings.js +7 -0
  29. package/lib/cloudflare/bindings.js.map +1 -0
  30. package/lib/cloudflare/generate-asset-manifest.d.ts +3 -0
  31. package/lib/cloudflare/generate-asset-manifest.d.ts.map +1 -0
  32. package/lib/cloudflare/generate-asset-manifest.js +69 -0
  33. package/lib/cloudflare/generate-asset-manifest.js.map +1 -0
  34. package/lib/cloudflare/index.d.ts +6 -0
  35. package/lib/cloudflare/index.d.ts.map +1 -0
  36. package/lib/cloudflare/index.js +6 -0
  37. package/lib/cloudflare/index.js.map +1 -0
  38. package/lib/cloudflare/kv-namespace.d.ts +61 -0
  39. package/lib/cloudflare/kv-namespace.d.ts.map +1 -0
  40. package/lib/cloudflare/kv-namespace.js +93 -0
  41. package/lib/cloudflare/kv-namespace.js.map +1 -0
  42. package/lib/cloudflare/state.d.ts +83 -0
  43. package/lib/cloudflare/state.d.ts.map +1 -0
  44. package/lib/cloudflare/state.js +109 -0
  45. package/lib/cloudflare/state.js.map +1 -0
  46. package/lib/cloudflare/static-site-router.d.ts +10 -0
  47. package/lib/cloudflare/static-site-router.d.ts.map +1 -0
  48. package/lib/cloudflare/static-site-router.js +88 -0
  49. package/lib/cloudflare/static-site-router.js.map +1 -0
  50. package/lib/cloudflare/static-site.d.ts +166 -0
  51. package/lib/cloudflare/static-site.d.ts.map +1 -0
  52. package/lib/cloudflare/static-site.js +152 -0
  53. package/lib/cloudflare/static-site.js.map +1 -0
  54. package/lib/cloudflare/types.d.ts +52 -0
  55. package/lib/cloudflare/types.d.ts.map +1 -0
  56. package/lib/cloudflare/types.js +21 -0
  57. package/lib/cloudflare/types.js.map +1 -0
  58. package/lib/cloudflare/upload-asset-manifest.d.ts +12 -0
  59. package/lib/cloudflare/upload-asset-manifest.d.ts.map +1 -0
  60. package/lib/cloudflare/upload-asset-manifest.js +55 -0
  61. package/lib/cloudflare/upload-asset-manifest.js.map +1 -0
  62. package/lib/cloudflare/worker.d.ts +87 -0
  63. package/lib/cloudflare/worker.d.ts.map +1 -0
  64. package/lib/cloudflare/worker.js +364 -0
  65. package/lib/cloudflare/worker.js.map +1 -0
  66. package/lib/esbuild.d.ts +82 -0
  67. package/lib/esbuild.d.ts.map +1 -1
  68. package/lib/esbuild.js +24 -17
  69. package/lib/esbuild.js.map +1 -1
  70. package/lib/fs.d.ts +1 -1
  71. package/lib/global.d.ts.map +1 -1
  72. package/lib/global.js +3 -1
  73. package/lib/global.js.map +1 -1
  74. package/lib/index.d.ts +0 -1
  75. package/lib/index.d.ts.map +1 -1
  76. package/lib/index.js +0 -1
  77. package/lib/index.js.map +1 -1
  78. package/lib/main.js +1 -1
  79. package/lib/main.js.map +1 -1
  80. package/lib/output.d.ts +17 -13
  81. package/lib/output.d.ts.map +1 -1
  82. package/lib/output.js +2 -2
  83. package/lib/output.js.map +1 -1
  84. package/lib/resource.d.ts +3 -2
  85. package/lib/resource.d.ts.map +1 -1
  86. package/lib/resource.js +2 -2
  87. package/lib/resource.js.map +1 -1
  88. package/lib/utils/content-type.d.ts +8 -0
  89. package/lib/utils/content-type.d.ts.map +1 -0
  90. package/lib/utils/content-type.js +41 -0
  91. package/lib/utils/content-type.js.map +1 -0
  92. package/package.json +26 -8
  93. package/src/apply.ts +5 -5
  94. package/src/aws/index.ts +1 -0
  95. package/src/aws/ses.ts +346 -0
  96. package/src/cloudflare/api.ts +276 -0
  97. package/src/cloudflare/asset-manifest.ts +16 -0
  98. package/src/cloudflare/auth.ts +187 -0
  99. package/src/cloudflare/bindings.ts +286 -0
  100. package/src/cloudflare/generate-asset-manifest.ts +93 -0
  101. package/src/cloudflare/index.ts +5 -0
  102. package/src/cloudflare/kv-namespace.ts +195 -0
  103. package/src/cloudflare/state.ts +138 -0
  104. package/src/cloudflare/static-site-router.ts +108 -0
  105. package/src/cloudflare/static-site.ts +379 -0
  106. package/src/cloudflare/types.ts +71 -0
  107. package/src/cloudflare/upload-asset-manifest.ts +83 -0
  108. package/src/cloudflare/worker.ts +568 -0
  109. package/src/esbuild.ts +31 -22
  110. package/src/global.ts +1 -0
  111. package/src/index.ts +0 -1
  112. package/src/main.ts +1 -1
  113. package/src/output.ts +32 -32
  114. package/src/resource.ts +11 -5
  115. package/src/utils/content-type.ts +42 -0
  116. package/lib/$.d.ts +0 -7
  117. package/lib/$.d.ts.map +0 -1
  118. package/lib/$.js +0 -31
  119. package/lib/$.js.map +0 -1
  120. package/lib/agent/ai.d.ts +0 -40
  121. package/lib/agent/ai.d.ts.map +0 -1
  122. package/lib/agent/ai.js +0 -132
  123. package/lib/agent/ai.js.map +0 -1
  124. package/lib/agent/anthropic.d.ts +0 -9
  125. package/lib/agent/anthropic.d.ts.map +0 -1
  126. package/lib/agent/anthropic.js +0 -25
  127. package/lib/agent/anthropic.js.map +0 -1
  128. package/lib/agent/dependencies.d.ts +0 -4
  129. package/lib/agent/dependencies.d.ts.map +0 -1
  130. package/lib/agent/dependencies.js +0 -48
  131. package/lib/agent/dependencies.js.map +0 -1
  132. package/lib/agent/file-context.d.ts +0 -13
  133. package/lib/agent/file-context.d.ts.map +0 -1
  134. package/lib/agent/file-context.js +0 -6
  135. package/lib/agent/file-context.js.map +0 -1
  136. package/lib/agent/index.d.ts +0 -10
  137. package/lib/agent/index.d.ts.map +0 -1
  138. package/lib/agent/index.js +0 -10
  139. package/lib/agent/index.js.map +0 -1
  140. package/lib/agent/model.d.ts +0 -8
  141. package/lib/agent/model.d.ts.map +0 -1
  142. package/lib/agent/model.js +0 -29
  143. package/lib/agent/model.js.map +0 -1
  144. package/lib/agent/module.d.ts +0 -81
  145. package/lib/agent/module.d.ts.map +0 -1
  146. package/lib/agent/module.js +0 -309
  147. package/lib/agent/module.js.map +0 -1
  148. package/lib/agent/openai.d.ts +0 -6
  149. package/lib/agent/openai.d.ts.map +0 -1
  150. package/lib/agent/openai.js +0 -41
  151. package/lib/agent/openai.js.map +0 -1
  152. package/lib/agent/program.d.ts +0 -12
  153. package/lib/agent/program.d.ts.map +0 -1
  154. package/lib/agent/program.js +0 -21
  155. package/lib/agent/program.js.map +0 -1
  156. package/lib/agent/prompts.d.ts +0 -4
  157. package/lib/agent/prompts.d.ts.map +0 -1
  158. package/lib/agent/prompts.js +0 -11
  159. package/lib/agent/prompts.js.map +0 -1
  160. package/lib/agent/scrape.d.ts +0 -71
  161. package/lib/agent/scrape.d.ts.map +0 -1
  162. package/lib/agent/scrape.js +0 -45
  163. package/lib/agent/scrape.js.map +0 -1
  164. package/lib/aws/auto/docs.d.ts +0 -11
  165. package/lib/aws/auto/docs.d.ts.map +0 -1
  166. package/lib/aws/auto/docs.js +0 -56
  167. package/lib/aws/auto/docs.js.map +0 -1
  168. package/lib/aws/auto/index.d.ts +0 -5
  169. package/lib/aws/auto/index.d.ts.map +0 -1
  170. package/lib/aws/auto/index.js +0 -5
  171. package/lib/aws/auto/index.js.map +0 -1
  172. package/lib/aws/auto/resource.d.ts +0 -24
  173. package/lib/aws/auto/resource.d.ts.map +0 -1
  174. package/lib/aws/auto/resource.js +0 -194
  175. package/lib/aws/auto/resource.js.map +0 -1
  176. package/lib/aws/auto/service.d.ts +0 -10
  177. package/lib/aws/auto/service.d.ts.map +0 -1
  178. package/lib/aws/auto/service.js +0 -34
  179. package/lib/aws/auto/service.js.map +0 -1
  180. package/lib/aws/auto/spec.d.ts +0 -83
  181. package/lib/aws/auto/spec.d.ts.map +0 -1
  182. package/lib/aws/auto/spec.js +0 -58
  183. package/lib/aws/auto/spec.js.map +0 -1
  184. package/lib/markdown/design.d.ts +0 -17
  185. package/lib/markdown/design.d.ts.map +0 -1
  186. package/lib/markdown/design.js +0 -92
  187. package/lib/markdown/design.js.map +0 -1
  188. package/lib/markdown/extract.d.ts +0 -7
  189. package/lib/markdown/extract.d.ts.map +0 -1
  190. package/lib/markdown/extract.js +0 -10
  191. package/lib/markdown/extract.js.map +0 -1
  192. package/lib/markdown/index.d.ts +0 -4
  193. package/lib/markdown/index.d.ts.map +0 -1
  194. package/lib/markdown/index.js +0 -4
  195. package/lib/markdown/index.js.map +0 -1
  196. package/lib/markdown/requirements.d.ts +0 -79
  197. package/lib/markdown/requirements.d.ts.map +0 -1
  198. package/lib/markdown/requirements.js +0 -105
  199. package/lib/markdown/requirements.js.map +0 -1
  200. package/lib/typescript/check-omission.d.ts +0 -2
  201. package/lib/typescript/check-omission.d.ts.map +0 -1
  202. package/lib/typescript/check-omission.js +0 -43
  203. package/lib/typescript/check-omission.js.map +0 -1
  204. package/lib/typescript/debug-type-errors.d.ts +0 -3
  205. package/lib/typescript/debug-type-errors.d.ts.map +0 -1
  206. package/lib/typescript/debug-type-errors.js +0 -35
  207. package/lib/typescript/debug-type-errors.js.map +0 -1
  208. package/lib/typescript/extract.d.ts +0 -7
  209. package/lib/typescript/extract.d.ts.map +0 -1
  210. package/lib/typescript/extract.js +0 -10
  211. package/lib/typescript/extract.js.map +0 -1
  212. package/lib/typescript/index.d.ts +0 -7
  213. package/lib/typescript/index.d.ts.map +0 -1
  214. package/lib/typescript/index.js +0 -7
  215. package/lib/typescript/index.js.map +0 -1
  216. package/lib/typescript/install-dependencies.d.ts +0 -29
  217. package/lib/typescript/install-dependencies.d.ts.map +0 -1
  218. package/lib/typescript/install-dependencies.js +0 -52
  219. package/lib/typescript/install-dependencies.js.map +0 -1
  220. package/lib/typescript/package.d.ts +0 -157
  221. package/lib/typescript/package.d.ts.map +0 -1
  222. package/lib/typescript/package.js +0 -115
  223. package/lib/typescript/package.js.map +0 -1
  224. package/lib/typescript/repair-omissions.d.ts +0 -3
  225. package/lib/typescript/repair-omissions.d.ts.map +0 -1
  226. package/lib/typescript/repair-omissions.js +0 -23
  227. package/lib/typescript/repair-omissions.js.map +0 -1
  228. package/lib/typescript/repair.d.ts +0 -3
  229. package/lib/typescript/repair.d.ts.map +0 -1
  230. package/lib/typescript/repair.js +0 -34
  231. package/lib/typescript/repair.js.map +0 -1
  232. package/lib/typescript/tsconfig.d.ts +0 -469
  233. package/lib/typescript/tsconfig.d.ts.map +0 -1
  234. package/lib/typescript/tsconfig.js +0 -143
  235. package/lib/typescript/tsconfig.js.map +0 -1
  236. package/lib/typescript/typescript.d.ts +0 -107
  237. package/lib/typescript/typescript.d.ts.map +0 -1
  238. package/lib/typescript/typescript.js +0 -180
  239. package/lib/typescript/typescript.js.map +0 -1
  240. package/lib/typescript/validate.d.ts +0 -10
  241. package/lib/typescript/validate.d.ts.map +0 -1
  242. package/lib/typescript/validate.js +0 -49
  243. package/lib/typescript/validate.js.map +0 -1
  244. package/src/$.ts +0 -36
  245. package/src/agent/ai.ts +0 -215
  246. package/src/agent/anthropic.ts +0 -31
  247. package/src/agent/dependencies.ts +0 -62
  248. package/src/agent/file-context.ts +0 -8
  249. package/src/agent/index.ts +0 -9
  250. package/src/agent/model.ts +0 -39
  251. package/src/agent/module.ts +0 -418
  252. package/src/agent/openai.ts +0 -50
  253. package/src/agent/program.md +0 -38
  254. package/src/agent/program.ts +0 -37
  255. package/src/agent/prompts.ts +0 -20
  256. package/src/agent/scrape.ts +0 -47
  257. package/src/aws/auto/cfn.json +0 -225310
  258. package/src/aws/auto/docs.ts +0 -73
  259. package/src/aws/auto/index.ts +0 -4
  260. package/src/aws/auto/resource.ts +0 -269
  261. package/src/aws/auto/service.ts +0 -51
  262. package/src/aws/auto/spec.ts +0 -160
  263. package/src/markdown/design.ts +0 -125
  264. package/src/markdown/extract.ts +0 -9
  265. package/src/markdown/index.ts +0 -3
  266. package/src/markdown/requirements.ts +0 -135
  267. package/src/typescript/check-omission.ts +0 -47
  268. package/src/typescript/debug-type-errors.ts +0 -42
  269. package/src/typescript/extract.ts +0 -9
  270. package/src/typescript/index.ts +0 -6
  271. package/src/typescript/install-dependencies.ts +0 -61
  272. package/src/typescript/package.ts +0 -143
  273. package/src/typescript/repair-omissions.ts +0 -31
  274. package/src/typescript/repair.ts +0 -45
  275. package/src/typescript/tsconfig.ts +0 -170
  276. package/src/typescript/typescript.ts +0 -217
  277. package/src/typescript/validate.ts +0 -73
@@ -1,73 +0,0 @@
1
- import TurndownService from "turndown";
2
- import { generateText } from "../../agent/ai";
3
- import { resolveModel } from "../../agent/model";
4
- import { type Context, Resource } from "../../resource";
5
-
6
- export class AWSDocReference extends Resource(
7
- "AWSDocReference",
8
- async (
9
- ctx: Context<{ content: string }>,
10
- props: {
11
- serviceName: string;
12
- resourceName: string;
13
- requirements: string;
14
- },
15
- ): Promise<{
16
- content: string;
17
- } | void> => {
18
- if (ctx.event === "delete") {
19
- return;
20
- }
21
-
22
- const serviceNameLower = props.serviceName.toLowerCase();
23
- const url = `https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/${serviceNameLower}`;
24
-
25
- const response = await fetch(url);
26
- if (!response.ok) {
27
- throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
28
- }
29
-
30
- const html = await response.text();
31
- const turndownService = new TurndownService({
32
- headingStyle: "atx",
33
- codeBlockStyle: "fenced",
34
- });
35
- const markdown = turndownService.turndown(html);
36
-
37
- // Use AI to analyze the docs and extract relevant API information
38
- const model = await resolveModel("gpt-4o");
39
- const apiSummary = await generateText({
40
- model,
41
- temperature: 0.1,
42
- messages: [
43
- {
44
- role: "system",
45
- content:
46
- "You are an expert at analyzing AWS SDK documentation and identifying relevant APIs for CloudFormation resource implementations.",
47
- },
48
- {
49
- role: "user",
50
- content: `Please analyze the AWS SDK v3 documentation for the ${props.serviceName} service and identify APIs that would be relevant for implementing the ${props.resourceName} resource's CRUD lifecycle.
51
-
52
- Requirements for the CRUD lifecycle:
53
- ${props.requirements}
54
-
55
- For each relevant API, extract:
56
- 1. The command name (e.g., CreateUserCOMmand, DeleteRoleCommand)
57
- 2. A brief description of what it does
58
- 3. The Input type name (e.g. CreateUserCommandInput)
59
- 4. The Output type name (e.g. CreateUserCommandOutput)
60
-
61
- Also include the written documentation for the API.
62
-
63
- Documentation:
64
- ${markdown}`,
65
- },
66
- ],
67
- });
68
-
69
- return {
70
- content: apiSummary.text,
71
- };
72
- },
73
- ) {}
@@ -1,4 +0,0 @@
1
- export * from "./docs";
2
- export * from "./resource";
3
- export * from "./service";
4
- export * from "./spec";
@@ -1,269 +0,0 @@
1
- import { kebabCase } from "change-case";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { z } from "zod";
5
- import { generateObject } from "../../agent/ai";
6
- import { resolveModel } from "../../agent/model";
7
- import { Requirements } from "../../markdown/requirements";
8
- import { Resource } from "../../resource";
9
- import { TypeScriptFile } from "../../typescript";
10
- import { AWSDocReference } from "./docs";
11
- import type { CfnResource } from "./spec";
12
-
13
- export class AWSResource extends Resource(
14
- "cfn-resource",
15
- async (
16
- ctx,
17
- props: CfnResource & {
18
- ServiceName: string;
19
- requirementsDir: string;
20
- srcDir: string;
21
- rootDir: string;
22
- },
23
- ) => {
24
- if (ctx.event === "delete") {
25
- return;
26
- }
27
-
28
- // Load relevant Terraform provider implementation files for this resource
29
- const terraformServicePath = path.join(
30
- props.rootDir,
31
- "3p",
32
- "terraform-provider-aws",
33
- "internal",
34
- "service",
35
- props.ServiceName.toLowerCase(),
36
- );
37
-
38
- const files = await fs.readdir(terraformServicePath);
39
- const resourcePrefix = kebabCase(props.ResourceName).toLowerCase();
40
- const goFiles = files.filter(
41
- (file) => file.endsWith(".go") && !file.endsWith("_test.go"),
42
- );
43
-
44
- if (goFiles.length === 0) {
45
- throw new Error(
46
- `No Terraform implementation files found for resource ${props.ResourceName} in ${terraformServicePath}. Expected files starting with "${resourcePrefix}"`,
47
- );
48
- }
49
-
50
- // Identify relevant files using AI
51
- const { relevantFiles } = await identifyRelevantFiles(
52
- props.ResourceName,
53
- props.ServiceName,
54
- goFiles,
55
- resourcePrefix,
56
- props,
57
- );
58
-
59
- if (relevantFiles.length === 0) {
60
- throw new Error(
61
- `No relevant Terraform implementation files identified for resource ${props.ResourceName}`,
62
- );
63
- }
64
-
65
- console.log(
66
- `Found ${relevantFiles.length} relevant files for ${props.ResourceFQN}:`,
67
- );
68
- for (const file of relevantFiles) {
69
- console.log(` - ${file}`);
70
- }
71
-
72
- const fileContents = await Promise.all(
73
- relevantFiles.map(async (file) => {
74
- const content = await fs.readFile(
75
- path.join(terraformServicePath, file),
76
- "utf-8",
77
- );
78
- return `// ${file}\n${content}`;
79
- }),
80
- );
81
-
82
- const terraformImplementation = fileContents.join("\n\n");
83
-
84
- const requirements = new Requirements("requirements", {
85
- modelId: "o3-mini",
86
- reasoningEffort: "high",
87
- file: path.join(
88
- props.requirementsDir,
89
- kebabCase(props.ResourceName) + ".md",
90
- ),
91
- requirements: [
92
- `Due to problems relying on the AWS CloudFormation Service, we have decided
93
- to reproduce the CRUD lifecycle of the ${props.ResourceName} resource locally using
94
- TypeScript and the AWS SDK v3.
95
-
96
- Before proceeding, we need to clearly define the requirements for this Resource's lifecycle handler, covering the following aspects:
97
- 1. Input and Output contract. These are the input properties that are accepted by CloudFormation
98
- and the output Attributes produced and referenceable after the resource has been created. Don't be lazy by just referencing the documentation, you should describe the property and its behavior explicitly in the requirements document.
99
- 2. Update lifecycle for each property. This determines the behavior of the CRUD lifecycle when
100
- a property changes, e.g. when a resource must be REPLACED when a property changes, or whether it can be mutated with a PUT.
101
- 3. AWS APIs that must be called to create this "CloudFormation Resource". This is not always 1:1, e.g. an IAM Role may have Policies that need to be created and attached.
102
- 4. Waiting for stabilization - AWS resources can take time to fully stabilize and be ready for use. Our Resource should wait and poll until the resource is ready. Please identify the resources that require stabilization check and which API call can be used to check the status reliably.
103
- Note: ideally, stabilization checks are performed downstream where they are used (i.e. optimistically instead of pessimistically), so we should analyze this Resource's upstream dependencies and define any checks that should be performed.
104
- 5. Create, Update and Delete lifecycle procedure. What operations should be performed on CREATE, UPDATE and DELETE?
105
- 6. Error handling. Point out specific error codes for AWS APIs that require special handling an describe how.
106
-
107
- Don't be vague. This is a specific requirements doc. Information like "robust" blah-blah is not relevant. Simply focus on the details of the AWS Resource, its contract, composite parts, error handling and lifecycle.
108
-
109
- Based on analysis of the Terraform AWS Provider implementation below, be sure to identify:
110
- - Important edge cases and error conditions that need special handling
111
- - API call patterns and sequences that have been proven to work
112
- - Stabilization and wait conditions that are necessary
113
- - Any specific error codes or scenarios that require retry logic or special handling
114
-
115
- Here is the relevant Terraform implementation:
116
- ${terraformImplementation}
117
-
118
- Below is the CloudFormation Resource Specification for the ${props.ResourceName} resource:
119
- ${JSON.stringify(props, null, 2)}
120
- `,
121
- ],
122
- });
123
-
124
- // First, fetch AWS SDK documentation
125
- const docReference = new AWSDocReference("docs", {
126
- serviceName: props.ServiceName,
127
- resourceName: props.ResourceName,
128
- requirements: requirements.content,
129
- });
130
-
131
- const exampleDir = path.join(props.rootDir, "alchemy", "src", "aws");
132
- const fewShotExamples = await Promise.all(
133
- (await fs.readdir(exampleDir))
134
- .filter((file) => file.endsWith(".ts"))
135
- .map(async (file) => {
136
- const content = await fs.readFile(
137
- path.join(exampleDir, file),
138
- "utf-8",
139
- );
140
- return `// ${file}\n${content}`;
141
- }),
142
- );
143
-
144
- const implementation = new TypeScriptFile("implementation", {
145
- modelId: "claude-3-5-sonnet-20241022",
146
- path: path.join(props.srcDir, kebabCase(props.ResourceName) + ".ts"),
147
- projectRoot: __dirname,
148
- typeCheck: true,
149
- requirements: requirements.content.apply((requirements) =>
150
- docReference.content.apply(
151
- (
152
- docs,
153
- ) => `Implement the ${props.ResourceName} using TypeScript and AWS SDK V3.
154
-
155
- Requirements:
156
- ${requirements}
157
-
158
- API SDK v3 Documentation:
159
- ${docs}
160
-
161
- Here is the Terraform Go implementation that we can learn from:
162
- ${terraformImplementation}
163
-
164
- Here are some examples of how this has been done before. Make sure to follow the same structure and style of defining input and output contracts as types and then export a class using the mixin pattern and implement the lifecycle as a function, using ctx.event to detect CREATE, UPDATE and DELETE events:
165
- ${fewShotExamples.join("\n")}
166
-
167
- One slight difference from the example is that the \`ignore\` and \`Resource\` functions should be imported from alchemy
168
-
169
- import { type Context, Resource, ignore } from "alchemy";
170
-
171
- Make sure to add an explicit type for Context<T>
172
-
173
- async (ctx: Context<OutputType>, props: InputType) => Promise<OutputType> { .. }
174
-
175
- It must be the OutputType. This avoids problems like it ctx.output being \`unknown\`.
176
-
177
- Another common gotcha is that ctx.outputs only exists when ctx.event is update | delete. You will get type error when it is create.
178
-
179
- For reference, here is what the Context object looks like:
180
- export type Context<Outputs> = {
181
- stage: string;
182
- resourceID: ResourceID;
183
- scope: IScope;
184
- /**
185
- * Indicate that this resource is being replaced.
186
- * This will cause the resource to be deleted at the end of the stack's CREATE phase.
187
- */
188
- replace(): void;
189
- } & (
190
- | {
191
- event: "create";
192
- }
193
- | {
194
- event: "update" | "delete";
195
- output: Outputs;
196
- }
197
- );
198
- `,
199
- ),
200
- ),
201
- });
202
-
203
- return {
204
- requirements,
205
- implementation,
206
- };
207
- },
208
- ) {}
209
-
210
- const RelevantFilesSchema = z.object({
211
- /**
212
- * List of Go file names that are relevant for implementing the CRUD lifecycle
213
- * for this resource
214
- */
215
- relevantFiles: z.array(z.string()),
216
- });
217
-
218
- async function identifyRelevantFiles(
219
- resourceName: string,
220
- serviceName: string,
221
- files: string[],
222
- resourcePrefix: string,
223
- cfnSpec: CfnResource,
224
- ): Promise<z.infer<typeof RelevantFilesSchema>> {
225
- const model = await resolveModel("gpt-4o");
226
-
227
- const result = await generateObject({
228
- model,
229
- schema: RelevantFilesSchema,
230
- temperature: 0.1,
231
- messages: [
232
- {
233
- role: "system",
234
- content:
235
- "You are an expert at AWS service implementations and Go code analysis. Your task is to identify ONLY the Go files that are specifically implementing the given AWS resource's CRUD lifecycle, excluding any files that work with other resources.",
236
- },
237
- {
238
- role: "user",
239
- content: `Please identify which Go files are specifically implementing the CRUD lifecycle for the AWS::${serviceName}::${resourceName} resource.
240
-
241
- Available files (all in the ${serviceName.toLowerCase()} package):
242
- ${files.map((f) => `- ${f}`).join("\n")}
243
-
244
- CloudFormation Resource Specification:
245
- ${JSON.stringify(cfnSpec, null, 2)}
246
-
247
- Selection Rules:
248
- 1. ONLY select files that are specifically implementing this exact resource type (AWS::${serviceName}::${resourceName})
249
- 2. Files must handle one or more of:
250
- - Resource creation and deletion
251
- - Resource updates and modifications
252
- - Resource state checking and stabilization
253
- - Resource validation
254
- 3. Strict Exclusions:
255
- - Test files
256
- - Files working with other resource types
257
- - General utility files (unless they are ONLY used by this resource)
258
- - Files that just happen to share a similar prefix but work with different resources
259
- 4. Files should start with the prefix: ${resourcePrefix}
260
-
261
- Important: Be very strict about only including files that are specifically working with this exact resource type. If a file works with multiple resources or similar resources, exclude it unless it's absolutely essential for this resource's implementation.
262
-
263
- Return ONLY the list of relevant file names.`,
264
- },
265
- ],
266
- });
267
-
268
- return result.object;
269
- }
@@ -1,51 +0,0 @@
1
- import { kebabCase } from "change-case";
2
- import path from "node:path";
3
- import { Folder } from "../../fs";
4
- import { Resource } from "../../resource";
5
- import { AWSResource } from "./resource";
6
- import type { CfnService } from "./spec";
7
-
8
- export class AWSService extends Resource(
9
- "aws-service",
10
- async (
11
- ctx,
12
- props: CfnService & {
13
- requirementsDir: string;
14
- srcDir: string;
15
- rootDir: string;
16
- },
17
- ) => {
18
- const serviceDir = new Folder(
19
- "service",
20
- path.join(props.requirementsDir, kebabCase(props.ServiceName)),
21
- ).path;
22
- const srcDir = new Folder(
23
- "src",
24
- path.join(props.srcDir, kebabCase(props.ServiceName)),
25
- ).path;
26
-
27
- const resources = Object.entries(props.Resources);
28
- if (process.env.DEBUG) {
29
- // Only instantiate first resource in debug mode
30
- const [resourceName, resource] = resources[0];
31
- return [
32
- new AWSResource(resourceName, {
33
- ...resource,
34
- ServiceName: props.ServiceName,
35
- requirementsDir: serviceDir,
36
- srcDir,
37
- rootDir: props.rootDir,
38
- }),
39
- ];
40
- }
41
- return resources.map(([resourceName, resource]) => {
42
- return new AWSResource(resourceName, {
43
- ...resource,
44
- ServiceName: props.ServiceName,
45
- rootDir: props.rootDir,
46
- srcDir,
47
- requirementsDir: serviceDir,
48
- });
49
- });
50
- },
51
- ) {}
@@ -1,160 +0,0 @@
1
- import path from "node:path";
2
-
3
- export interface CfnSpec {
4
- PropertyTypes: {
5
- [propertyFQN: `${keyof CfnSpec["ResourceTypes"]}.${string}`]: PropertySpec;
6
- };
7
- ResourceTypes: {
8
- [key: `AWS::${string}::${string}`]: ResourceSpec;
9
- };
10
- ResourceSpecificationVersion: string;
11
- }
12
-
13
- export type UpdateType = "Immutable" | "Mutable" | "Conditional";
14
-
15
- export type PrimitiveType = "String" | "Double" | "Integer" | "Json";
16
-
17
- export type PropertySpec =
18
- | {
19
- Type?: never;
20
- PrimitiveType?: never;
21
- Documentation: string;
22
- Properties: {
23
- [propertyName: string]: {
24
- Documentation: string;
25
- Type: "Extensions" | "Subject";
26
- UpdateType: UpdateType;
27
- Required: boolean;
28
- };
29
- };
30
- }
31
- | {
32
- Type: "List" | "Map";
33
- PrimitiveType?: PrimitiveType;
34
- Required: boolean;
35
- ItemType: string;
36
- UpdateType: UpdateType;
37
- }
38
- | {
39
- Type?: never;
40
- PrimitiveType: PrimitiveType;
41
- Documentation: string;
42
- UpdateType: UpdateType;
43
- Required: boolean;
44
- };
45
-
46
- export type ResourceProperty = {
47
- [propertyName: string]: {
48
- Documentation: string;
49
- UpdateType: UpdateType;
50
- Required: boolean;
51
- } & (
52
- | {
53
- Type: string;
54
- PrimitiveType?: never;
55
- }
56
- | {
57
- Type: "List";
58
- ItemType?: string;
59
- PrimitiveItemType?: PrimitiveType;
60
- DuplicatesAllowed: boolean;
61
- }
62
- | {
63
- Type?: never;
64
- PrimitiveType: PrimitiveType;
65
- }
66
- );
67
- };
68
-
69
- export type ResourceSpec = {
70
- Documentation: string;
71
- Properties: ResourceProperty;
72
- Attributes?: {
73
- [attributeName: string]: {
74
- PrimitiveType: PrimitiveType;
75
- };
76
- };
77
- };
78
-
79
- export type CfnResource = ResourceSpec & {
80
- ServiceName: string;
81
- ResourceName: string;
82
- ResourceFQN: string;
83
- Properties: {
84
- [propertyName: string]: ResourceProperty & {
85
- Spec: PropertySpec;
86
- };
87
- };
88
- };
89
-
90
- export type CfnService = {
91
- ServiceName: string;
92
- Resources: {
93
- [resourceName: string]: CfnResource;
94
- };
95
- };
96
- export type CfnServices = Record<string, CfnService>;
97
-
98
- export async function loadServices(): Promise<CfnServices> {
99
- const cfnSpec = await loadSpec();
100
-
101
- const resources = Object.keys(cfnSpec.ResourceTypes).map((resourceType) => {
102
- const [partition, service, resource] = resourceType.split("::");
103
-
104
- return {
105
- partition,
106
- service,
107
- resource,
108
- };
109
- });
110
- const awsServices = resources.filter(({ partition }) => partition === "AWS");
111
- const awsServiceGroups = awsServices.reduce<CfnServices>((acc, service) => {
112
- if (!acc[service.service]) {
113
- acc[service.service] = {
114
- ServiceName: service.service,
115
- Resources: {},
116
- };
117
- }
118
- const spec = {
119
- ...cfnSpec.ResourceTypes[`AWS::${service.service}::${service.resource}`],
120
- ServiceName: service.service,
121
- ResourceName: service.resource,
122
- ResourceFQN: `AWS::${service.service}::${service.resource}`,
123
- } as CfnResource;
124
- for (const [propertyName, property] of Object.entries(spec.Properties)) {
125
- if (property.Type && !["List", "Map"].includes(property.Type)) {
126
- const propFQN =
127
- `${spec.ResourceFQN}.${propertyName}` as keyof typeof cfnSpec.PropertyTypes;
128
- const propertySpec = {
129
- ...cfnSpec.PropertyTypes[propFQN],
130
- PropertyFQN: propFQN,
131
- };
132
- // @ts-ignore
133
- property.Spec = propertySpec;
134
- if (propertySpec === undefined) {
135
- throw new Error(`Property ${propertyName} has no spec`);
136
- }
137
- }
138
- }
139
- acc[service.service].Resources[service.resource] = spec;
140
- return acc;
141
- }, {} as CfnServices);
142
-
143
- return awsServiceGroups;
144
- }
145
-
146
- export async function loadSpec(): Promise<CfnSpec> {
147
- const awsSpecURL =
148
- "https://dnwj8swjjbsbt.cloudfront.net/latest/gzip/CloudFormationResourceSpecification.json";
149
-
150
- const specPath = path.join(__dirname, "cfn.json");
151
-
152
- let awsSpec;
153
- try {
154
- awsSpec = JSON.parse(await Bun.file(specPath).text());
155
- } catch {
156
- awsSpec = await (await fetch(awsSpecURL)).json();
157
- await Bun.write(specPath, JSON.stringify(awsSpec, null, 2));
158
- }
159
- return awsSpec as CfnSpec;
160
- }
@@ -1,125 +0,0 @@
1
- import { tool } from "ai";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { z } from "zod";
5
- import {
6
- type FileContext,
7
- type ModelId,
8
- dependenciesAsMessages,
9
- generateText,
10
- resolveModel,
11
- } from "../agent";
12
- import { Prompts } from "../agent/prompts";
13
- import { rm } from "../fs";
14
- import { extractMarkdown } from "../markdown/extract";
15
- import { type Context, Resource } from "../resource";
16
-
17
- export interface DesignInput {
18
- prompt: string;
19
- path: string;
20
- deps?: any[];
21
- model?: ModelId;
22
- dependencies: FileContext[];
23
- }
24
-
25
- export interface DesignOutput {
26
- path: string;
27
- content: string;
28
- }
29
-
30
- export class Design extends Resource(
31
- "design",
32
- async (ctx: Context<DesignOutput>, props: DesignInput) => {
33
- if (ctx.event === "delete") {
34
- await rm(props.path);
35
- return;
36
- }
37
- const model = await resolveModel(props.model ?? "gpt-4o");
38
-
39
- const loadFile = tool({
40
- description: "Load a relevant file into context",
41
- parameters: z.object({
42
- file: z.string(),
43
- }),
44
- execute: async ({ file }) => fs.readFile(file, "utf-8"),
45
- });
46
-
47
- let existingContent: string | undefined = undefined;
48
- if (ctx.event === "update") {
49
- if (props.path) {
50
- try {
51
- existingContent = await fs.readFile(props.path, "utf-8");
52
- } catch {
53
- // File doesn't exist yet, continue with empty content
54
- }
55
- } else {
56
- existingContent = ctx.output.content;
57
- }
58
- }
59
-
60
- console.log(
61
- ctx.event === "create" ? "Designing" : "Revising design",
62
- ":",
63
- props.path,
64
- );
65
-
66
- const result = await generateText({
67
- model,
68
- // tools: {
69
- // import: importFile,
70
- // },
71
- providerOptions:
72
- props.model === "o3-mini"
73
- ? {
74
- openai: {
75
- reasoningEffort: "high",
76
- },
77
- }
78
- : undefined,
79
- messages: [
80
- {
81
- role: "system",
82
- content: Prompts.program,
83
- },
84
- ...dependenciesAsMessages(props.dependencies),
85
- ...(existingContent
86
- ? [
87
- {
88
- role: "user" as const,
89
- content: `Here is the prior version of ${props.path}:\n${existingContent}`,
90
- },
91
- {
92
- role: "assistant" as const,
93
- content:
94
- "Thanks, I'll make sure to consider this when producing the next revision.",
95
- },
96
- ]
97
- : []),
98
- {
99
- role: "user",
100
- content: props.prompt,
101
- },
102
- {
103
- role: "user",
104
- content: [
105
- `Make sure to list each of the files that need to be implemented and`,
106
- "and to follow best practices in term of separation of concerns.",
107
- "Ideally, each component has its own dedicated file.",
108
- "Each file should be referenced using markdown links, e.g. [file.ts](./file.ts).",
109
- ].join(" "),
110
- },
111
- ],
112
- });
113
-
114
- const content = extractMarkdown(result.text);
115
-
116
- await fs.mkdir(path.dirname(props.path), { recursive: true });
117
-
118
- await fs.writeFile(props.path, content);
119
-
120
- return {
121
- path: props.path,
122
- content,
123
- };
124
- },
125
- ) {}
@@ -1,9 +0,0 @@
1
- /**
2
- * Extracts markdown content from a code block or returns the full content
3
- * @param text Response text that may contain markdown code blocks
4
- * @returns The markdown content from code block if found, otherwise returns the full text
5
- */
6
- export function extractMarkdown(text: string): string {
7
- const matches = text.match(/```md\n([\s\S]*?)```/);
8
- return matches?.[1]?.trim() ?? text.trim();
9
- }
@@ -1,3 +0,0 @@
1
- export * from "./design";
2
- export * from "./extract";
3
- export * from "./requirements";