alchemy 0.0.7 → 0.1.1
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/package.json +23 -22
- package/src/alchemize.ts +151 -0
- package/src/apply.ts +117 -0
- package/src/components/agent/agent.ts +45 -0
- package/src/components/agent/anthropic.ts +27 -0
- package/src/components/agent/index.ts +4 -0
- package/src/components/agent/model.ts +22 -0
- package/src/components/agent/openai.ts +44 -0
- package/src/components/agent/package.ts +126 -0
- package/src/components/agent/requirements.ts +111 -0
- package/src/components/agent/scrape.ts +47 -0
- package/src/components/agent/tsconfig.ts +150 -0
- package/src/components/agent/typescript.ts +177 -0
- package/src/components/aws/bucket.ts +168 -0
- package/src/components/aws/function.ts +302 -0
- package/src/components/aws/index.ts +6 -0
- package/src/components/aws/policy.ts +245 -0
- package/src/components/aws/queue.ts +222 -0
- package/src/components/aws/role.ts +245 -0
- package/src/components/aws/table.ts +172 -0
- package/src/components/esbuild.ts +128 -0
- package/src/components/fs.ts +45 -0
- package/src/destroy.ts +63 -0
- package/src/error.ts +16 -0
- package/src/global.ts +72 -0
- package/src/index.ts +6 -0
- package/src/input.ts +16 -0
- package/src/main.ts +34 -0
- package/src/output.ts +49 -0
- package/src/resource.ts +313 -0
- package/src/state.ts +100 -0
- package/test/aws/function.test.ts +144 -0
- package/test/aws/queue.test.ts +85 -0
- package/test/aws/role.test.ts +153 -0
- package/test/aws/table.test.ts +73 -0
- package/test/esbuild.test.ts +41 -0
- package/test/handler.ts +9 -0
- package/tsconfig.json +7 -0
- package/Column.js +0 -1
- package/Create.js +0 -1
- package/Delete.js +0 -1
- package/Drop.js +0 -1
- package/Insert.js +0 -1
- package/Makefile +0 -8
- package/README.md +0 -0
- package/Select.js +0 -1
- package/Table.js +0 -1
- package/Transaction.js +0 -1
- package/Types.js +0 -1
- package/Update.js +0 -1
- package/alchemy.js +0 -57
- package/alchemy.sublime-project +0 -9
- package/alchemy.sublime-workspace +0 -2304
- package/config/app.js +0 -8
- package/config/db.js +0 -20
- package/copy.js +0 -1
- package/defaultEngine.js +0 -8
- package/engine.js +0 -282
- package/getInstantiatorFunction.js +0 -28
- package/index.js +0 -52
- package/jstest.js +0 -3
- package/loggerFactory.js +0 -27
- package/logs/alchemy.log +0 -46
- package/logs/sql.log +0 -41
- package/sql/Column.js +0 -158
- package/sql/Columnable.js +0 -21
- package/sql/Create.js +0 -15
- package/sql/Delete.js +0 -34
- package/sql/Drop.js +0 -36
- package/sql/Insert.js +0 -96
- package/sql/SchemaElement.js +0 -11
- package/sql/Select.js +0 -192
- package/sql/SqlBase.js +0 -134
- package/sql/SqlStatement.js +0 -80
- package/sql/Table.js +0 -96
- package/sql/Transaction.js +0 -45
- package/sql/Update.js +0 -105
- package/sql/defineElement.js +0 -26
- package/sql/defineStatement.js +0 -26
- package/sql/types/BaseType.js +0 -43
- package/sql/types/BigSerialType.js +0 -5
- package/sql/types/IntegerType.js +0 -5
- package/sql/types/JsonType.js +0 -5
- package/sql/types/SerialType.js +0 -5
- package/sql/types/StringType.js +0 -31
- package/sql/types/TimestampType.js +0 -5
- package/sql/types/UUIDType.js +0 -5
- package/sql/types/defineType.js +0 -25
- package/sql/types/getType.js +0 -12
- package/sql/types/index.js +0 -11
- package/sql/where.js +0 -31
- package/sql/whereToString.js +0 -29
- package/test/ColumnTests.js +0 -80
- package/test/CreateTable.js +0 -54
- package/test/DeleteTests.js +0 -28
- package/test/DropTests.js +0 -23
- package/test/GetTypeTests.js +0 -56
- package/test/InsertTests.js +0 -86
- package/test/SelectExistsTests.js +0 -26
- package/test/SelectTests.js +0 -130
- package/test/TableTests.js +0 -46
- package/test.js +0 -8
- package/test.ls +0 -4
- package/validate.js +0 -26
- package/x.js +0 -5
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { generateText } from "ai";
|
|
2
|
+
import { mkdir, unlink, writeFile } from "fs/promises";
|
|
3
|
+
import { dirname } from "path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { Agent } from "./agent";
|
|
6
|
+
import { resolveModel } from "./model";
|
|
7
|
+
import { scrapeWebPage } from "./scrape";
|
|
8
|
+
|
|
9
|
+
export const RequirementsInput = z.object({
|
|
10
|
+
/**
|
|
11
|
+
* The ID of the model to use for generating requirements documentation
|
|
12
|
+
* @default "gpt-4o"
|
|
13
|
+
*/
|
|
14
|
+
modelId: z.string().optional(),
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* List of requirements to document and analyze
|
|
18
|
+
*/
|
|
19
|
+
requirements: z.array(z.string()),
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* One-line summary of the requirements document
|
|
23
|
+
*/
|
|
24
|
+
title: z.string(),
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Temperature setting for model generation (higher = more creative, lower = more focused)
|
|
28
|
+
* @default 0.7
|
|
29
|
+
*/
|
|
30
|
+
temperature: z.number().optional(),
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* File path where the requirements document should be written
|
|
34
|
+
*/
|
|
35
|
+
path: z.string(),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const RequirementsOutput = z.object({
|
|
39
|
+
/**
|
|
40
|
+
* Generated markdown document containing organized and analyzed requirements
|
|
41
|
+
*/
|
|
42
|
+
content: z.string(),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export type RequirementsInput = z.infer<typeof RequirementsInput>;
|
|
46
|
+
export type RequirementsOutput = z.infer<typeof RequirementsOutput>;
|
|
47
|
+
|
|
48
|
+
export class Requirements extends Agent(
|
|
49
|
+
"code::Requirements",
|
|
50
|
+
{
|
|
51
|
+
description:
|
|
52
|
+
"This Agent is responsible for writing and refining a detailed requirements document from a list of requirements.",
|
|
53
|
+
input: RequirementsInput,
|
|
54
|
+
output: RequirementsOutput,
|
|
55
|
+
},
|
|
56
|
+
async (ctx, input) => {
|
|
57
|
+
if (ctx.event === "delete") {
|
|
58
|
+
await unlink(input.path);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Get the appropriate model based on the ID
|
|
63
|
+
const { model } = await resolveModel(input.modelId ?? "gpt-4o");
|
|
64
|
+
|
|
65
|
+
// Generate the requirements document
|
|
66
|
+
const { text } = await generateText({
|
|
67
|
+
model,
|
|
68
|
+
temperature: input.temperature ?? 0.7,
|
|
69
|
+
maxSteps: 3, // Allow multiple steps for potential tool usage
|
|
70
|
+
tools: {
|
|
71
|
+
scrapeWebPage,
|
|
72
|
+
},
|
|
73
|
+
messages: [
|
|
74
|
+
{
|
|
75
|
+
role: "system",
|
|
76
|
+
content:
|
|
77
|
+
"You are a helpful assistant that creates markdown documents from requirements. You can use the scrapeWebPage tool to get additional requirements from relevant URLs if needed.",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
role: "user",
|
|
81
|
+
content: `Please analyze the following system requirements and create a comprehensive, unambiguous markdown document that covers all aspects:
|
|
82
|
+
|
|
83
|
+
Title: ${input.title}
|
|
84
|
+
|
|
85
|
+
Requirements:
|
|
86
|
+
${input.requirements.map((req) => `- ${req}`).join("\n")}
|
|
87
|
+
|
|
88
|
+
Guidelines:
|
|
89
|
+
1. Make explicit decisions for any ambiguous requirements - do not leave open questions
|
|
90
|
+
2. Document the reasoning behind key decisions
|
|
91
|
+
3. Be specific about technical choices and implementation details
|
|
92
|
+
4. Avoid phrases like "could be", "might need", or "possibly requires" - be definitive
|
|
93
|
+
5. Organize requirements into logical sections with clear dependencies and relationships
|
|
94
|
+
|
|
95
|
+
Format the output in markdown with appropriate headers, lists, and sections, starting with the title as an H1 header.
|
|
96
|
+
Include technical considerations and implementation details, ensuring every requirement has a clear, actionable specification.`,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Ensure the directory exists
|
|
102
|
+
await mkdir(dirname(input.path), { recursive: true });
|
|
103
|
+
|
|
104
|
+
// Write the requirements to the file
|
|
105
|
+
await writeFile(input.path, text);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
content: text,
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
) {}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { tool } from "ai";
|
|
2
|
+
import TurndownService from "turndown";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
export const scrapeWebPage = tool({
|
|
6
|
+
description: "Scrapes a web page and converts HTML content to Markdown",
|
|
7
|
+
parameters: z.object({
|
|
8
|
+
url: z.string().url().describe("The URL to scrape content from"),
|
|
9
|
+
turndownOptions: z
|
|
10
|
+
.object({
|
|
11
|
+
headingStyle: z.enum(["setext", "atx"]).optional(),
|
|
12
|
+
hr: z.string().optional(),
|
|
13
|
+
bulletListMarker: z.enum(["-", "*", "+"]).optional(),
|
|
14
|
+
codeBlockStyle: z.enum(["indented", "fenced"]).optional(),
|
|
15
|
+
emDelimiter: z.enum(["_", "*"]).optional(),
|
|
16
|
+
strongDelimiter: z.enum(["__", "**"]).optional(),
|
|
17
|
+
linkStyle: z.enum(["inlined", "referenced"]).optional(),
|
|
18
|
+
linkReferenceStyle: z
|
|
19
|
+
.enum(["full", "collapsed", "shortcut"])
|
|
20
|
+
.optional(),
|
|
21
|
+
})
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Custom options for Turndown HTML to Markdown conversion"),
|
|
24
|
+
}),
|
|
25
|
+
execute: async ({ url, turndownOptions }) => {
|
|
26
|
+
console.log("Scraping web page:", url);
|
|
27
|
+
try {
|
|
28
|
+
// Fetch the page content
|
|
29
|
+
const response = await fetch(url);
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Get the HTML content
|
|
35
|
+
const html = await response.text();
|
|
36
|
+
|
|
37
|
+
// Initialize Turndown with options
|
|
38
|
+
const turndownService = new TurndownService(turndownOptions);
|
|
39
|
+
|
|
40
|
+
// Convert HTML to Markdown and return
|
|
41
|
+
return turndownService.turndown(html);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error("Error scraping web page:", error);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { generateObject } from "ai";
|
|
2
|
+
import { mkdir, unlink, writeFile } from "fs/promises";
|
|
3
|
+
import { dirname } from "path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { Agent } from "./agent";
|
|
6
|
+
import { resolveModel } from "./model";
|
|
7
|
+
|
|
8
|
+
export type TsConfigProps = z.infer<typeof TsConfigProps>;
|
|
9
|
+
|
|
10
|
+
export const TsConfigProps = z.object({
|
|
11
|
+
/**
|
|
12
|
+
* The ID of the model to use for generating tsconfig configuration
|
|
13
|
+
* @default "gpt-4o"
|
|
14
|
+
*/
|
|
15
|
+
modelId: z.string().optional(),
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* List of requirements for the TypeScript configuration
|
|
19
|
+
* Can include module settings, compilation options, etc.
|
|
20
|
+
*/
|
|
21
|
+
requirements: z.array(z.string()),
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Temperature setting for model generation
|
|
25
|
+
* @default 0.3
|
|
26
|
+
*/
|
|
27
|
+
temperature: z.number().optional(),
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Path to the tsconfig.json file to generate
|
|
31
|
+
*/
|
|
32
|
+
path: z.string(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export type TsConfigJson = z.infer<typeof TsConfigSchema>;
|
|
36
|
+
export const TsConfigSchema = z.object({
|
|
37
|
+
$schema: z.string().optional(),
|
|
38
|
+
compilerOptions: z.object({
|
|
39
|
+
target: z.enum([
|
|
40
|
+
"es2015",
|
|
41
|
+
"es2016",
|
|
42
|
+
"es2017",
|
|
43
|
+
"es2018",
|
|
44
|
+
"es2019",
|
|
45
|
+
"es2020",
|
|
46
|
+
"es2021",
|
|
47
|
+
"es2022",
|
|
48
|
+
"esnext",
|
|
49
|
+
]),
|
|
50
|
+
module: z.enum([
|
|
51
|
+
"commonjs",
|
|
52
|
+
"es2015",
|
|
53
|
+
"es2020",
|
|
54
|
+
"es2022",
|
|
55
|
+
"esnext",
|
|
56
|
+
"node16",
|
|
57
|
+
"nodenext",
|
|
58
|
+
]),
|
|
59
|
+
lib: z.array(z.string()).optional(),
|
|
60
|
+
declaration: z.boolean().optional(),
|
|
61
|
+
declarationMap: z.boolean().optional(),
|
|
62
|
+
sourceMap: z.boolean().optional(),
|
|
63
|
+
outDir: z.string().optional(),
|
|
64
|
+
rootDir: z.string().optional(),
|
|
65
|
+
strict: z.boolean(),
|
|
66
|
+
esModuleInterop: z.boolean().optional(),
|
|
67
|
+
skipLibCheck: z.boolean().optional(),
|
|
68
|
+
forceConsistentCasingInFileNames: z.boolean().optional(),
|
|
69
|
+
moduleResolution: z
|
|
70
|
+
.enum(["node", "node16", "nodenext", "bundler"])
|
|
71
|
+
.optional(),
|
|
72
|
+
resolveJsonModule: z.boolean().optional(),
|
|
73
|
+
isolatedModules: z.boolean().optional(),
|
|
74
|
+
allowJs: z.boolean().optional(),
|
|
75
|
+
checkJs: z.boolean().optional(),
|
|
76
|
+
noEmit: z.boolean().optional(),
|
|
77
|
+
incremental: z.boolean().optional(),
|
|
78
|
+
composite: z.boolean().optional(),
|
|
79
|
+
tsBuildInfoFile: z.string().optional(),
|
|
80
|
+
paths: z.record(z.array(z.string())).optional(),
|
|
81
|
+
baseUrl: z.string().optional(),
|
|
82
|
+
types: z.array(z.string()).optional(),
|
|
83
|
+
typeRoots: z.array(z.string()).optional(),
|
|
84
|
+
}),
|
|
85
|
+
include: z.array(z.string()).optional(),
|
|
86
|
+
exclude: z.array(z.string()).optional(),
|
|
87
|
+
references: z
|
|
88
|
+
.array(
|
|
89
|
+
z.object({
|
|
90
|
+
path: z.string(),
|
|
91
|
+
}),
|
|
92
|
+
)
|
|
93
|
+
.optional(),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
export class TypeScriptConfig extends Agent(
|
|
97
|
+
"code::tsconfig",
|
|
98
|
+
{
|
|
99
|
+
description:
|
|
100
|
+
"This Agent is responsible for generating tsconfig.json configuration based on requirements.",
|
|
101
|
+
input: TsConfigProps,
|
|
102
|
+
output: TsConfigSchema,
|
|
103
|
+
},
|
|
104
|
+
async (ctx, props) => {
|
|
105
|
+
if (ctx.event === "delete") {
|
|
106
|
+
await unlink(props.path);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Get the appropriate model
|
|
111
|
+
const { model } = await resolveModel(props.modelId ?? "gpt-4o");
|
|
112
|
+
|
|
113
|
+
// Generate the tsconfig configuration using generateObject for type safety
|
|
114
|
+
const result = await generateObject({
|
|
115
|
+
model,
|
|
116
|
+
schema: TsConfigSchema,
|
|
117
|
+
temperature: props.temperature ?? 0.3,
|
|
118
|
+
messages: [
|
|
119
|
+
{
|
|
120
|
+
role: "system",
|
|
121
|
+
content:
|
|
122
|
+
"You are an expert at creating TypeScript configurations. You will generate a tsconfig.json configuration based on requirements.",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
role: "user",
|
|
126
|
+
content: `Please generate a tsconfig.json configuration based on these requirements:
|
|
127
|
+
|
|
128
|
+
Requirements:
|
|
129
|
+
${props.requirements.map((req) => `- ${req}`).join("\n")}
|
|
130
|
+
|
|
131
|
+
Rules:
|
|
132
|
+
1. Use modern TypeScript features
|
|
133
|
+
2. Enable strict type checking
|
|
134
|
+
3. Use ESM modules by default
|
|
135
|
+
4. Include source maps for better debugging
|
|
136
|
+
5. Infer appropriate settings for declarations, module resolution, and other options based on the requirements
|
|
137
|
+
6. Use appropriate paths for source and output directories`,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Ensure the directory exists
|
|
143
|
+
await mkdir(dirname(props.path), { recursive: true });
|
|
144
|
+
|
|
145
|
+
// Write the tsconfig.json file
|
|
146
|
+
await writeFile(props.path, JSON.stringify(result.object, null, 2));
|
|
147
|
+
|
|
148
|
+
return result.object;
|
|
149
|
+
},
|
|
150
|
+
) {}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { CoreMessage } from "ai";
|
|
2
|
+
import { generateText } from "ai";
|
|
3
|
+
import { mkdir, unlink, writeFile } from "fs/promises";
|
|
4
|
+
import { dirname } from "path";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { Agent } from "./agent";
|
|
7
|
+
import { resolveModel } from "./model";
|
|
8
|
+
|
|
9
|
+
export type CodeProps = z.infer<typeof CodeProps>;
|
|
10
|
+
|
|
11
|
+
export type File = z.infer<typeof File>;
|
|
12
|
+
export const File = z.object({
|
|
13
|
+
path: z.string(),
|
|
14
|
+
content: z.string(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const CodeProps = z.object({
|
|
18
|
+
/**
|
|
19
|
+
* The ID of the model to use for generating TypeScript code
|
|
20
|
+
* @default "gpt-4o"
|
|
21
|
+
*/
|
|
22
|
+
modelId: z.string().optional(),
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The name of the file to write the code to
|
|
26
|
+
*/
|
|
27
|
+
path: z.string(),
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The requirements for the TypeScript file
|
|
31
|
+
*/
|
|
32
|
+
requirements: z.string(),
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* List of other code files that it depends on
|
|
36
|
+
*/
|
|
37
|
+
dependencies: z.array(File).optional(),
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Temperature setting for model generation (higher = more creative, lower = more focused)
|
|
41
|
+
* @default 0.7
|
|
42
|
+
*/
|
|
43
|
+
temperature: z.number().optional(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export type CodeOutput = z.infer<typeof CodeOutput>;
|
|
47
|
+
export const CodeOutput = File;
|
|
48
|
+
|
|
49
|
+
export class TypeScriptFile extends Agent(
|
|
50
|
+
"code::TypeScriptFile",
|
|
51
|
+
{
|
|
52
|
+
description:
|
|
53
|
+
"This Agent is responsible for generating TypeScript code based on requirements and context from other code files.",
|
|
54
|
+
input: CodeProps,
|
|
55
|
+
output: CodeOutput,
|
|
56
|
+
},
|
|
57
|
+
async (ctx, props) => {
|
|
58
|
+
if (ctx.event === "delete") {
|
|
59
|
+
await unlink(props.path);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Get the appropriate model based on the ID
|
|
64
|
+
const { model } = await resolveModel(props.modelId ?? "gpt-4o");
|
|
65
|
+
|
|
66
|
+
// Generate the TypeScript code with retries
|
|
67
|
+
const content = await generateCodeWithRetries(model, props);
|
|
68
|
+
|
|
69
|
+
// Ensure the directory exists
|
|
70
|
+
await mkdir(dirname(props.path), { recursive: true });
|
|
71
|
+
|
|
72
|
+
// Write the TypeScript file
|
|
73
|
+
await writeFile(props.path, content);
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
path: props.path,
|
|
77
|
+
content: content,
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
) {}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Attempts to generate valid TypeScript code with retries
|
|
84
|
+
*/
|
|
85
|
+
async function generateCodeWithRetries(
|
|
86
|
+
model: any,
|
|
87
|
+
props: CodeProps,
|
|
88
|
+
maxAttempts = 3,
|
|
89
|
+
): Promise<string> {
|
|
90
|
+
const baseMessages: CoreMessage[] = [
|
|
91
|
+
{
|
|
92
|
+
role: "system",
|
|
93
|
+
content:
|
|
94
|
+
"You are an expert TypeScript developer that generates clean, well-documented code following best practices. " +
|
|
95
|
+
"Your response must follow this exact format:\n" +
|
|
96
|
+
"1. A single sentence describing what the code will do\n" +
|
|
97
|
+
"2. A single TypeScript code block surrounded by ```ts and ``` tags\n" +
|
|
98
|
+
"Do not include any other explanations or multiple code blocks.",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
role: "user",
|
|
102
|
+
content: `Please generate TypeScript code based on the following specifications:
|
|
103
|
+
|
|
104
|
+
Requirements:
|
|
105
|
+
${props.requirements}
|
|
106
|
+
|
|
107
|
+
${
|
|
108
|
+
props.dependencies?.length
|
|
109
|
+
? `Context (other code files):
|
|
110
|
+
${props.dependencies.map((dep) => dep.content).join("\n\n")}`
|
|
111
|
+
: ""
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
The code should:
|
|
115
|
+
1. Be well-documented with JSDoc comments
|
|
116
|
+
2. Follow TypeScript best practices
|
|
117
|
+
3. Include proper type definitions
|
|
118
|
+
4. Be ready to use with the provided context files
|
|
119
|
+
|
|
120
|
+
Your response MUST follow this format:
|
|
121
|
+
- One sentence summary of what the code will do
|
|
122
|
+
- Single TypeScript code block surrounded by \`\`\`ts and \`\`\` tags
|
|
123
|
+
- No other explanations or multiple code blocks`,
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
const messages = [...baseMessages];
|
|
128
|
+
|
|
129
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
130
|
+
const { text } = await generateText({
|
|
131
|
+
model,
|
|
132
|
+
temperature: Math.max(
|
|
133
|
+
0.1,
|
|
134
|
+
(props.temperature ?? 0.7) * (1 - attempt * 0.2),
|
|
135
|
+
), // Reduce temperature with each attempt
|
|
136
|
+
messages,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const code = extractTypeScriptCode(text);
|
|
140
|
+
if (code) {
|
|
141
|
+
return code;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (attempt === maxAttempts) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
"Failed to generate valid TypeScript code after maximum attempts",
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Add the failed attempt and correction request to the message chain
|
|
151
|
+
messages.push(
|
|
152
|
+
{
|
|
153
|
+
role: "assistant",
|
|
154
|
+
content: text,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
role: "user",
|
|
158
|
+
content:
|
|
159
|
+
"Your response did not match the required format. Please provide your response with exactly one sentence summary" +
|
|
160
|
+
" followed by a single TypeScript code block surrounded by ```ts and ``` tags. No other text or explanations.",
|
|
161
|
+
},
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// This should never be reached due to the throw above
|
|
166
|
+
throw new Error("Unexpected code generation failure");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Extracts TypeScript code from a markdown code block
|
|
171
|
+
* @param text Response text that may contain markdown
|
|
172
|
+
* @returns The TypeScript code if found, undefined otherwise
|
|
173
|
+
*/
|
|
174
|
+
function extractTypeScriptCode(text: string): string | undefined {
|
|
175
|
+
const matches = text.match(/```(?:ts|typescript)\n([\s\S]*?)```/);
|
|
176
|
+
return matches?.[1]?.trim();
|
|
177
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CreateBucketCommand,
|
|
3
|
+
DeleteBucketCommand,
|
|
4
|
+
GetBucketAclCommand,
|
|
5
|
+
GetBucketLocationCommand,
|
|
6
|
+
GetBucketTaggingCommand,
|
|
7
|
+
GetBucketVersioningCommand,
|
|
8
|
+
HeadBucketCommand,
|
|
9
|
+
NoSuchBucket,
|
|
10
|
+
PutBucketTaggingCommand,
|
|
11
|
+
S3Client,
|
|
12
|
+
} from "@aws-sdk/client-s3";
|
|
13
|
+
import { ignore } from "../../error";
|
|
14
|
+
import { Resource } from "../../resource";
|
|
15
|
+
|
|
16
|
+
export interface BucketProps {
|
|
17
|
+
bucketName: string;
|
|
18
|
+
tags?: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface BucketOutput extends BucketProps {
|
|
22
|
+
id: string; // Same as bucketName for AWS
|
|
23
|
+
arn: string;
|
|
24
|
+
bucketDomainName: string; // Format: bucket-name.s3.amazonaws.com
|
|
25
|
+
bucketRegionalDomainName?: string; // Format: bucket-name.s3.region.amazonaws.com
|
|
26
|
+
hostedZoneId?: string; // S3 hosted zone ID for the region
|
|
27
|
+
region?: string; // AWS Region where bucket resides
|
|
28
|
+
websiteEndpoint?: string; // Only if website hosting is enabled
|
|
29
|
+
websiteDomain?: string; // Only if website hosting is enabled
|
|
30
|
+
versioningEnabled?: boolean;
|
|
31
|
+
acl?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class Bucket extends Resource(
|
|
35
|
+
"s3::Bucket",
|
|
36
|
+
async (ctx, props: BucketProps) => {
|
|
37
|
+
const client = new S3Client({});
|
|
38
|
+
|
|
39
|
+
if (ctx.event === "delete") {
|
|
40
|
+
await ignore(NoSuchBucket.name, () =>
|
|
41
|
+
client.send(
|
|
42
|
+
new DeleteBucketCommand({
|
|
43
|
+
Bucket: props.bucketName,
|
|
44
|
+
}),
|
|
45
|
+
),
|
|
46
|
+
);
|
|
47
|
+
return props;
|
|
48
|
+
} else {
|
|
49
|
+
try {
|
|
50
|
+
// Check if bucket exists
|
|
51
|
+
await client.send(
|
|
52
|
+
new HeadBucketCommand({
|
|
53
|
+
Bucket: props.bucketName,
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Update tags if they changed and bucket exists
|
|
58
|
+
if (ctx.event === "update" && props.tags) {
|
|
59
|
+
await client.send(
|
|
60
|
+
new PutBucketTaggingCommand({
|
|
61
|
+
Bucket: props.bucketName,
|
|
62
|
+
Tagging: {
|
|
63
|
+
TagSet: Object.entries(props.tags).map(([Key, Value]) => ({
|
|
64
|
+
Key,
|
|
65
|
+
Value,
|
|
66
|
+
})),
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
} catch (error: any) {
|
|
72
|
+
if (error.name === "NotFound") {
|
|
73
|
+
// Create bucket if it doesn't exist
|
|
74
|
+
await client.send(
|
|
75
|
+
new CreateBucketCommand({
|
|
76
|
+
Bucket: props.bucketName,
|
|
77
|
+
// Add tags during creation if specified
|
|
78
|
+
...(props.tags && {
|
|
79
|
+
Tagging: {
|
|
80
|
+
TagSet: Object.entries(props.tags).map(([Key, Value]) => ({
|
|
81
|
+
Key,
|
|
82
|
+
Value,
|
|
83
|
+
})),
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
}),
|
|
87
|
+
);
|
|
88
|
+
} else {
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Get bucket details
|
|
94
|
+
const [locationResponse, versioningResponse, aclResponse] =
|
|
95
|
+
await Promise.all([
|
|
96
|
+
client.send(
|
|
97
|
+
new GetBucketLocationCommand({ Bucket: props.bucketName }),
|
|
98
|
+
),
|
|
99
|
+
client.send(
|
|
100
|
+
new GetBucketVersioningCommand({ Bucket: props.bucketName }),
|
|
101
|
+
),
|
|
102
|
+
client.send(new GetBucketAclCommand({ Bucket: props.bucketName })),
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
const region = locationResponse.LocationConstraint || "us-east-1";
|
|
106
|
+
|
|
107
|
+
// Get tags if they exist
|
|
108
|
+
let tags = props.tags;
|
|
109
|
+
if (!tags) {
|
|
110
|
+
try {
|
|
111
|
+
const taggingResponse = await client.send(
|
|
112
|
+
new GetBucketTaggingCommand({ Bucket: props.bucketName }),
|
|
113
|
+
);
|
|
114
|
+
tags = Object.fromEntries(
|
|
115
|
+
taggingResponse.TagSet?.map(({ Key, Value }) => [Key, Value]) || [],
|
|
116
|
+
);
|
|
117
|
+
} catch (error: any) {
|
|
118
|
+
if (error.name !== "NoSuchTagSet") {
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const output: BucketOutput = {
|
|
125
|
+
bucketName: props.bucketName,
|
|
126
|
+
id: props.bucketName,
|
|
127
|
+
arn: `arn:aws:s3:::${props.bucketName}`,
|
|
128
|
+
bucketDomainName: `${props.bucketName}.s3.amazonaws.com`,
|
|
129
|
+
bucketRegionalDomainName: `${props.bucketName}.s3.${region}.amazonaws.com`,
|
|
130
|
+
region,
|
|
131
|
+
hostedZoneId: getHostedZoneId(region),
|
|
132
|
+
versioningEnabled: versioningResponse.Status === "Enabled",
|
|
133
|
+
acl: aclResponse.Grants?.[0]?.Permission?.toLowerCase(),
|
|
134
|
+
...(tags && { tags }),
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return output;
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
) {}
|
|
141
|
+
|
|
142
|
+
// Helper function to get S3 hosted zone IDs by region
|
|
143
|
+
function getHostedZoneId(region: string): string {
|
|
144
|
+
const hostedZoneIds: Record<string, string> = {
|
|
145
|
+
"us-east-1": "Z3AQBSTGFYJSTF",
|
|
146
|
+
"us-east-2": "Z2O1EMRO9K5GLX",
|
|
147
|
+
"us-west-1": "Z2F56UZL2M1ACD",
|
|
148
|
+
"us-west-2": "Z3BJ6K6RIION7M",
|
|
149
|
+
"af-south-1": "Z11KHD8FBVPUYU",
|
|
150
|
+
"ap-east-1": "ZNB98KWMFR0R6",
|
|
151
|
+
"ap-south-1": "Z11RGJOFQNVJUP",
|
|
152
|
+
"ap-northeast-1": "Z2M4EHUR26P7ZW",
|
|
153
|
+
"ap-northeast-2": "Z3W03O7B5YMIYP",
|
|
154
|
+
"ap-northeast-3": "Z2YQB5RD63NC85",
|
|
155
|
+
"ap-southeast-1": "Z3O0J2DXBE1FTB",
|
|
156
|
+
"ap-southeast-2": "Z1WCIGYICN2BYD",
|
|
157
|
+
"ca-central-1": "Z1QDHH18159H29",
|
|
158
|
+
"eu-central-1": "Z21DNDUVLTQW6Q",
|
|
159
|
+
"eu-west-1": "Z1BKCTXD74EZPE",
|
|
160
|
+
"eu-west-2": "Z3GKZC51ZF0DB4",
|
|
161
|
+
"eu-west-3": "Z3R1K369G5AVDG",
|
|
162
|
+
"eu-north-1": "Z3BAZG2TWCNX0D",
|
|
163
|
+
"eu-south-1": "Z30OZKI7KPW7MI",
|
|
164
|
+
"me-south-1": "Z1MPMWCPA7YB62",
|
|
165
|
+
"sa-east-1": "Z7KQH4QJS55SO",
|
|
166
|
+
};
|
|
167
|
+
return hostedZoneIds[region] || "Z3AQBSTGFYJSTF"; // Default to us-east-1 if region not found
|
|
168
|
+
}
|