alchemy 0.3.0 → 0.4.0
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.md +8 -3
- package/lib/ai/astro-file.js +7 -24
- package/lib/ai/client.d.ts +7 -0
- package/lib/ai/client.js +45 -0
- package/lib/ai/css-file.js +7 -24
- package/lib/ai/data.d.ts +26 -21
- package/lib/ai/data.js +33 -22
- package/lib/ai/document.d.ts +76 -5
- package/lib/ai/document.js +112 -56
- package/lib/ai/html-file.js +7 -24
- package/lib/ai/json-file.js +11 -32
- package/lib/ai/typescript-file.d.ts +1 -2
- package/lib/ai/typescript-file.js +8 -39
- package/lib/ai/vue-file.js +7 -24
- package/lib/ai/yaml-file.js +12 -48
- package/lib/alchemy.d.ts +5 -0
- package/lib/alchemy.js +61 -3
- package/lib/apply.js +18 -11
- package/lib/aws/account-id.d.ts +4 -1
- package/lib/aws/account-id.js +1 -1
- package/lib/aws/credentials.d.ts +5 -0
- package/lib/aws/credentials.js +0 -0
- package/lib/aws/oidc/oidc-provider.js +2 -2
- package/lib/aws/role.js +35 -23
- package/lib/cloudflare/account-api-token.d.ts +168 -0
- package/lib/cloudflare/account-api-token.js +179 -0
- package/lib/cloudflare/account-id.d.ts +1 -0
- package/lib/cloudflare/account-id.js +0 -0
- package/lib/cloudflare/api.d.ts +38 -1
- package/lib/cloudflare/api.js +55 -4
- package/lib/cloudflare/bucket.d.ts +118 -3
- package/lib/cloudflare/bucket.js +269 -89
- package/lib/cloudflare/custom-domain.d.ts +75 -0
- package/lib/cloudflare/custom-domain.js +154 -0
- package/lib/cloudflare/{dns.js → dns-records.js} +14 -1
- package/lib/cloudflare/generate-asset-manifest.js +1 -1
- package/lib/cloudflare/index.d.ts +5 -1
- package/lib/cloudflare/index.js +5 -1
- package/lib/cloudflare/permission-groups.d.ts +78 -0
- package/lib/cloudflare/permission-groups.js +48 -0
- package/lib/cloudflare/r2-rest-state-store.d.ts +2 -1
- package/lib/cloudflare/r2-rest-state-store.js +3 -2
- package/lib/cloudflare/static-site-router.js +16 -7
- package/lib/cloudflare/static-site.d.ts +17 -18
- package/lib/cloudflare/static-site.js +19 -7
- package/lib/cloudflare/upload-asset-manifest.js +4 -2
- package/lib/{util/encrypt.d.ts → encrypt.d.ts} +1 -1
- package/lib/{util/encrypt.js → encrypt.js} +1 -1
- package/lib/fs/copy-file.d.ts +54 -0
- package/lib/fs/copy-file.js +63 -0
- package/lib/fs/file-system-state-store.js +1 -1
- package/lib/fs/file.d.ts +31 -0
- package/lib/fs/file.js +30 -5
- package/lib/fs/index.d.ts +6 -0
- package/lib/fs/index.js +6 -0
- package/lib/fs/static-astro-file.d.ts +34 -0
- package/lib/fs/static-astro-file.js +39 -0
- package/lib/fs/static-css-file.d.ts +24 -0
- package/lib/fs/static-css-file.js +29 -0
- package/lib/fs/static-html-file.d.ts +31 -0
- package/lib/fs/static-html-file.js +36 -0
- package/lib/fs/static-json-file.d.ts +2 -2
- package/lib/fs/static-json-file.js +3 -2
- package/lib/fs/static-text-file.d.ts +1 -1
- package/lib/fs/static-text-file.js +3 -2
- package/lib/fs/static-typescript-file.d.ts +2 -2
- package/lib/fs/static-typescript-file.js +3 -2
- package/lib/fs/static-vue-file.d.ts +28 -0
- package/lib/fs/static-vue-file.js +33 -0
- package/lib/fs/static-yaml-file.d.ts +11 -8
- package/lib/fs/static-yaml-file.js +3 -2
- package/lib/internal/{providers.d.ts → docs/providers.d.ts} +14 -4
- package/lib/internal/docs/providers.js +196 -0
- package/lib/secret.d.ts +3 -0
- package/lib/secret.js +13 -0
- package/lib/{util/serde.d.ts → serde.d.ts} +1 -1
- package/lib/{util/serde.js → serde.js} +12 -4
- package/lib/test/bun.js +3 -2
- package/lib/util/sha256.d.ts +1 -0
- package/lib/util/sha256.js +4 -0
- package/lib/web/vitepress/config.d.ts +11 -0
- package/lib/web/vitepress/config.js +32 -0
- package/lib/web/vitepress/home-page.d.ts +5 -3
- package/lib/web/vitepress/home-page.js +48 -35
- package/lib/web/vitepress/index.d.ts +2 -0
- package/lib/web/vitepress/index.js +2 -0
- package/lib/web/vitepress/process-front-matter-files.d.ts +18 -0
- package/lib/web/vitepress/process-front-matter-files.js +68 -0
- package/lib/web/vitepress/vitepress.d.ts +5 -4
- package/lib/web/vitepress/vitepress.js +20 -36
- package/package.json +8 -4
- package/src/ai/astro-file.ts +11 -30
- package/src/ai/client.ts +59 -0
- package/src/ai/css-file.ts +11 -30
- package/src/ai/data.ts +50 -25
- package/src/ai/document.ts +171 -66
- package/src/ai/html-file.ts +11 -30
- package/src/ai/json-file.ts +13 -37
- package/src/ai/typescript-file.ts +14 -48
- package/src/ai/vue-file.ts +11 -30
- package/src/ai/yaml-file.ts +13 -53
- package/src/alchemy.ts +74 -9
- package/src/apply.ts +27 -26
- package/src/aws/account-id.ts +6 -2
- package/src/aws/credentials.ts +6 -0
- package/src/aws/oidc/oidc-provider.ts +17 -17
- package/src/aws/role.ts +68 -54
- package/src/cloudflare/account-api-token.ts +365 -0
- package/src/cloudflare/account-id.ts +0 -0
- package/src/cloudflare/api.ts +88 -17
- package/src/cloudflare/bucket.ts +493 -133
- package/src/cloudflare/custom-domain.ts +318 -0
- package/src/cloudflare/{dns.ts → dns-records.ts} +36 -22
- package/src/cloudflare/generate-asset-manifest.ts +4 -4
- package/src/cloudflare/index.ts +5 -1
- package/src/cloudflare/permission-groups.ts +137 -0
- package/src/cloudflare/r2-rest-state-store.ts +14 -13
- package/src/cloudflare/static-site-router.ts +18 -8
- package/src/cloudflare/static-site.ts +27 -35
- package/src/cloudflare/upload-asset-manifest.ts +6 -4
- package/src/cloudflare/worker.ts +44 -43
- package/src/dns/import-dns.ts +4 -4
- package/src/{util/encrypt.ts → encrypt.ts} +7 -10
- package/src/fs/copy-file.ts +115 -0
- package/src/fs/file-system-state-store.ts +5 -5
- package/src/fs/file.ts +64 -9
- package/src/fs/index.ts +6 -0
- package/src/fs/static-astro-file.ts +45 -0
- package/src/fs/static-css-file.ts +35 -0
- package/src/fs/static-html-file.ts +42 -0
- package/src/fs/static-json-file.ts +4 -3
- package/src/fs/static-text-file.ts +5 -2
- package/src/fs/static-typescript-file.ts +4 -3
- package/src/fs/static-vue-file.ts +39 -0
- package/src/fs/static-yaml-file.ts +13 -9
- package/src/internal/docs/providers.ts +281 -0
- package/src/secret.ts +16 -0
- package/src/{util/serde.ts → serde.ts} +16 -10
- package/src/test/bun.ts +8 -7
- package/src/util/sha256.ts +5 -0
- package/src/web/vitepress/config.ts +50 -0
- package/src/web/vitepress/home-page.ts +54 -42
- package/src/web/vitepress/index.ts +2 -0
- package/src/web/vitepress/process-front-matter-files.ts +98 -0
- package/src/web/vitepress/vitepress.ts +34 -50
- package/lib/internal/getting-started.d.ts +0 -10
- package/lib/internal/getting-started.js +0 -94
- package/lib/internal/project.d.ts +0 -13
- package/lib/internal/project.js +0 -101
- package/lib/internal/providers.js +0 -148
- package/src/internal/getting-started.ts +0 -107
- package/src/internal/project.ts +0 -119
- package/src/internal/providers.ts +0 -209
- /package/lib/cloudflare/{dns.d.ts → dns-records.d.ts} +0 -0
package/lib/ai/document.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { generateText } from "ai";
|
|
2
|
-
import
|
|
3
|
-
import path from "node:path";
|
|
2
|
+
import { StaticTextFile } from "../fs/static-text-file";
|
|
4
3
|
import { Resource } from "../resource";
|
|
5
|
-
import {
|
|
6
|
-
import { createModel } from "./client";
|
|
4
|
+
import { createModel, withRateLimitRetry } from "./client";
|
|
7
5
|
/**
|
|
8
6
|
* Default system prompt for markdown document generation
|
|
9
7
|
*/
|
|
@@ -13,7 +11,22 @@ const DEFAULT_MD_SYSTEM_PROMPT = "You are a technical documentation writer. Crea
|
|
|
13
11
|
* Supports powerful context handling through the alchemy template literal tag.
|
|
14
12
|
*
|
|
15
13
|
* @example
|
|
16
|
-
* // Create
|
|
14
|
+
* // Create an in-memory document (no file created)
|
|
15
|
+
* const apiDocs = await Document("api-docs", {
|
|
16
|
+
* title: "API Documentation",
|
|
17
|
+
* prompt: await alchemy`
|
|
18
|
+
* Generate API documentation based on these source files:
|
|
19
|
+
* ${alchemy.file("src/api.ts")}
|
|
20
|
+
* ${alchemy.file("src/types.ts")}
|
|
21
|
+
* `,
|
|
22
|
+
* model: {
|
|
23
|
+
* id: "gpt-4o",
|
|
24
|
+
* provider: "openai"
|
|
25
|
+
* }
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Create a document and write it to disk
|
|
17
30
|
* const apiDocs = await Document("api-docs", {
|
|
18
31
|
* title: "API Documentation",
|
|
19
32
|
* path: "./docs/api.md",
|
|
@@ -29,6 +42,23 @@ const DEFAULT_MD_SYSTEM_PROMPT = "You are a technical documentation writer. Crea
|
|
|
29
42
|
* });
|
|
30
43
|
*
|
|
31
44
|
* @example
|
|
45
|
+
* // Use message history for iterative document generation
|
|
46
|
+
* const apiDocs = await Document("api-docs", {
|
|
47
|
+
* title: "API Documentation",
|
|
48
|
+
* path: "./docs/api.md",
|
|
49
|
+
* messages: [
|
|
50
|
+
* { role: "user", content: "Create API documentation for these files" },
|
|
51
|
+
* { role: "assistant", content: "I'll help you create API documentation. Please provide the files." },
|
|
52
|
+
* { role: "user", content: "Here are the files: [file contents]" }
|
|
53
|
+
* ],
|
|
54
|
+
* system: "You are a technical documentation writer. Generate clear and concise API documentation.",
|
|
55
|
+
* model: {
|
|
56
|
+
* id: "gpt-4o",
|
|
57
|
+
* provider: "openai"
|
|
58
|
+
* }
|
|
59
|
+
* });
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
32
62
|
* // Use alchemy template literals with file collections and temperature control
|
|
33
63
|
* const modelDocs = await Document("models", {
|
|
34
64
|
* title: "Data Models",
|
|
@@ -61,67 +91,84 @@ const DEFAULT_MD_SYSTEM_PROMPT = "You are a technical documentation writer. Crea
|
|
|
61
91
|
* });
|
|
62
92
|
*/
|
|
63
93
|
export const Document = Resource("docs::Document", async function (id, props) {
|
|
64
|
-
//
|
|
65
|
-
|
|
94
|
+
// Validate that either prompt or messages are provided
|
|
95
|
+
if (!props.prompt && !props.messages) {
|
|
96
|
+
throw new Error("Either prompt or messages must be provided");
|
|
97
|
+
}
|
|
98
|
+
// Handle deletion phase
|
|
66
99
|
if (this.phase === "delete") {
|
|
67
|
-
try {
|
|
68
|
-
await fs.unlink(props.path);
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
// Ignore if file doesn't exist
|
|
72
|
-
if (error.code !== "ENOENT") {
|
|
73
|
-
throw error;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
100
|
return this.destroy();
|
|
77
101
|
}
|
|
102
|
+
if (this.phase === "update" && props.freeze) {
|
|
103
|
+
if (props.path) {
|
|
104
|
+
const filePath = props.path;
|
|
105
|
+
const fileId = `${id}-file`;
|
|
106
|
+
await StaticTextFile(fileId, filePath, this.output.content);
|
|
107
|
+
}
|
|
108
|
+
return this(this.output);
|
|
109
|
+
}
|
|
78
110
|
// Use provided system prompt or default
|
|
79
111
|
const system = props.system || DEFAULT_MD_SYSTEM_PROMPT;
|
|
80
|
-
// Generate initial content
|
|
81
|
-
const { text } = await
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
:
|
|
89
|
-
|
|
112
|
+
// Generate initial content with rate limit retry
|
|
113
|
+
const { text } = await withRateLimitRetry(async () => {
|
|
114
|
+
return generateText({
|
|
115
|
+
model: createModel(props),
|
|
116
|
+
...(props.messages
|
|
117
|
+
? { messages: props.messages }
|
|
118
|
+
: { prompt: props.prompt }),
|
|
119
|
+
system,
|
|
120
|
+
maxTokens: props.maxTokens || 8192,
|
|
121
|
+
providerOptions: props.model?.options,
|
|
122
|
+
...(props.temperature === undefined
|
|
123
|
+
? {}
|
|
124
|
+
: // some models error if you provide it (rather than ignoring it)
|
|
125
|
+
{ temperature: props.temperature }),
|
|
126
|
+
});
|
|
90
127
|
});
|
|
91
128
|
// Extract and validate markdown content
|
|
92
|
-
let { content, error } =
|
|
129
|
+
let { content, error } = extractMarkdownContent(text);
|
|
93
130
|
// Re-prompt if there are validation errors
|
|
94
131
|
if (error) {
|
|
95
132
|
const errorSystem = `${system}\n\nERROR: ${error}\n\nPlease try again and ensure your response contains exactly one markdown document inside \`\`\`md fences.`;
|
|
96
|
-
const { text: retryText } = await
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
:
|
|
133
|
+
const { text: retryText } = await withRateLimitRetry(async () => {
|
|
134
|
+
return generateText({
|
|
135
|
+
model: createModel(props),
|
|
136
|
+
...(props.messages
|
|
137
|
+
? { messages: props.messages }
|
|
138
|
+
: { prompt: props.prompt }),
|
|
139
|
+
system: errorSystem,
|
|
140
|
+
providerOptions: props.model?.options,
|
|
141
|
+
...(props.temperature === undefined
|
|
142
|
+
? {}
|
|
143
|
+
: { temperature: props.temperature }),
|
|
144
|
+
});
|
|
104
145
|
});
|
|
105
|
-
const retryResult =
|
|
146
|
+
const retryResult = extractMarkdownContent(retryText);
|
|
106
147
|
if (retryResult.error) {
|
|
107
|
-
throw new Error(`Failed to generate valid markdown content: ${retryResult.error}`);
|
|
148
|
+
throw new Error(`Failed to generate valid markdown content: ${retryResult.error}\n${retryText}`);
|
|
108
149
|
}
|
|
109
150
|
content = retryResult.content;
|
|
110
151
|
}
|
|
111
|
-
|
|
112
|
-
|
|
152
|
+
// Create result object
|
|
153
|
+
const result = {
|
|
154
|
+
...props,
|
|
155
|
+
content,
|
|
156
|
+
messages: [
|
|
157
|
+
...(props.messages || [{ role: "user", content: props.prompt }]),
|
|
158
|
+
{ role: "assistant", content },
|
|
159
|
+
],
|
|
160
|
+
createdAt: this.output?.createdAt || Date.now(),
|
|
161
|
+
updatedAt: Date.now(),
|
|
162
|
+
title: props.title || id,
|
|
163
|
+
};
|
|
164
|
+
// Write file if path is provided
|
|
165
|
+
if (props.path) {
|
|
166
|
+
const filePath = props.path;
|
|
167
|
+
const fileId = `${id}-file`;
|
|
168
|
+
result.file = await StaticTextFile(fileId, filePath, content);
|
|
113
169
|
}
|
|
114
|
-
// Write content to file
|
|
115
|
-
await fs.writeFile(props.path, content);
|
|
116
|
-
// Get file stats for timestamps
|
|
117
|
-
const stats = await fs.stat(props.path);
|
|
118
170
|
// Return the resource
|
|
119
|
-
return this(
|
|
120
|
-
...props,
|
|
121
|
-
content: content,
|
|
122
|
-
createdAt: stats.birthtimeMs,
|
|
123
|
-
updatedAt: stats.mtimeMs,
|
|
124
|
-
});
|
|
171
|
+
return this(result);
|
|
125
172
|
});
|
|
126
173
|
/**
|
|
127
174
|
* Extracts markdown content from between ```md fences
|
|
@@ -130,20 +177,29 @@ export const Document = Resource("docs::Document", async function (id, props) {
|
|
|
130
177
|
* @param text The text to extract markdown content from
|
|
131
178
|
* @returns The extracted markdown content or error message
|
|
132
179
|
*/
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
if (
|
|
180
|
+
function extractMarkdownContent(text) {
|
|
181
|
+
const lines = text.split("\n");
|
|
182
|
+
const startIdx = lines.findIndex((line) => line.trim() === "```md");
|
|
183
|
+
if (startIdx === -1) {
|
|
137
184
|
return {
|
|
138
185
|
content: "",
|
|
139
186
|
error: "No markdown code block found in the response. Please include your markdown content within ```md fences.",
|
|
140
187
|
};
|
|
141
188
|
}
|
|
142
|
-
|
|
189
|
+
const rest = lines.slice(startIdx + 1);
|
|
190
|
+
const endRelativeIdx = rest
|
|
191
|
+
.map((line) => line.trim() === "```")
|
|
192
|
+
.lastIndexOf(true);
|
|
193
|
+
if (endRelativeIdx === -1) {
|
|
143
194
|
return {
|
|
144
195
|
content: "",
|
|
145
|
-
error: "
|
|
196
|
+
error: "Markdown block was not closed properly.",
|
|
146
197
|
};
|
|
147
198
|
}
|
|
148
|
-
|
|
199
|
+
const endIdx = startIdx + 1 + endRelativeIdx;
|
|
200
|
+
const content = lines
|
|
201
|
+
.slice(startIdx + 1, endIdx)
|
|
202
|
+
.join("\n")
|
|
203
|
+
.trim();
|
|
204
|
+
return { content };
|
|
149
205
|
}
|
package/lib/ai/html-file.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { generateText } from "ai";
|
|
2
|
-
import
|
|
3
|
-
import path from "node:path";
|
|
2
|
+
import { StaticHTMLFile } from "../fs/static-html-file";
|
|
4
3
|
import { Resource } from "../resource";
|
|
5
|
-
import { ignore } from "../util/ignore";
|
|
6
4
|
import { createModel } from "./client";
|
|
7
5
|
/**
|
|
8
6
|
* Default system prompt for HTML file generation
|
|
@@ -68,18 +66,8 @@ const DEFAULT_HTML_SYSTEM_PROMPT = "You are an HTML code generator. Create HTML
|
|
|
68
66
|
* });
|
|
69
67
|
*/
|
|
70
68
|
export const HTMLFile = Resource("ai::HTMLFile", async function (id, props) {
|
|
71
|
-
//
|
|
72
|
-
await fs.mkdir(path.dirname(props.path), { recursive: true });
|
|
69
|
+
// Handle deletion phase
|
|
73
70
|
if (this.phase === "delete") {
|
|
74
|
-
try {
|
|
75
|
-
await fs.unlink(props.path);
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
// Ignore if file doesn't exist
|
|
79
|
-
if (error.code !== "ENOENT") {
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
71
|
return this.destroy();
|
|
84
72
|
}
|
|
85
73
|
// Use provided system prompt or default
|
|
@@ -114,19 +102,14 @@ export const HTMLFile = Resource("ai::HTMLFile", async function (id, props) {
|
|
|
114
102
|
}
|
|
115
103
|
code = retryResult.code;
|
|
116
104
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
// Write content to file
|
|
121
|
-
await fs.writeFile(props.path, code);
|
|
122
|
-
// Get file stats for timestamps
|
|
123
|
-
const stats = await fs.stat(props.path);
|
|
105
|
+
// Use StaticHTMLFile to create/update the file
|
|
106
|
+
const file = await StaticHTMLFile("file", props.path, code);
|
|
124
107
|
// Return the resource
|
|
125
108
|
return this({
|
|
126
109
|
...props,
|
|
127
|
-
content:
|
|
128
|
-
createdAt:
|
|
129
|
-
updatedAt:
|
|
110
|
+
content: file.content,
|
|
111
|
+
createdAt: Date.now(),
|
|
112
|
+
updatedAt: Date.now(),
|
|
130
113
|
});
|
|
131
114
|
});
|
|
132
115
|
/**
|
package/lib/ai/json-file.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { generateObject, generateText } from "ai";
|
|
2
|
-
import
|
|
3
|
-
import path from "node:path";
|
|
2
|
+
import { StaticJsonFile } from "../fs/static-json-file";
|
|
4
3
|
import { Resource } from "../resource";
|
|
5
|
-
import { ignore } from "../util/ignore";
|
|
6
4
|
import { ark } from "./ark";
|
|
7
5
|
import { createModel } from "./client";
|
|
8
6
|
/**
|
|
@@ -77,23 +75,10 @@ const DEFAULT_JSON_SYSTEM_PROMPT = "You are a JSON generator. Create valid JSON
|
|
|
77
75
|
* });
|
|
78
76
|
*/
|
|
79
77
|
export const JSONFile = Resource("ai::JSONFile", async function (id, props) {
|
|
80
|
-
//
|
|
81
|
-
await fs.mkdir(path.dirname(props.path), { recursive: true });
|
|
78
|
+
// Handle deletion phase
|
|
82
79
|
if (this.phase === "delete") {
|
|
83
|
-
try {
|
|
84
|
-
await fs.unlink(props.path);
|
|
85
|
-
}
|
|
86
|
-
catch (error) {
|
|
87
|
-
// Ignore if file doesn't exist
|
|
88
|
-
if (error.code !== "ENOENT") {
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
80
|
return this.destroy();
|
|
93
81
|
}
|
|
94
|
-
// Determine if we should use pretty printing
|
|
95
|
-
const pretty = props.pretty !== false;
|
|
96
|
-
const indent = props.indent ?? 2;
|
|
97
82
|
let jsonContent;
|
|
98
83
|
let jsonObject;
|
|
99
84
|
// Check if schema is provided
|
|
@@ -111,9 +96,9 @@ export const JSONFile = Resource("ai::JSONFile", async function (id, props) {
|
|
|
111
96
|
: { temperature: props.temperature }),
|
|
112
97
|
});
|
|
113
98
|
jsonObject = object;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
99
|
+
// Use StaticJsonFile to create the file
|
|
100
|
+
const file = await StaticJsonFile("file", props.path, jsonObject);
|
|
101
|
+
jsonContent = file.content;
|
|
117
102
|
}
|
|
118
103
|
else {
|
|
119
104
|
// Use fence-based extraction
|
|
@@ -149,26 +134,20 @@ export const JSONFile = Resource("ai::JSONFile", async function (id, props) {
|
|
|
149
134
|
}
|
|
150
135
|
content = retryResult.content;
|
|
151
136
|
}
|
|
152
|
-
// Parse JSON
|
|
137
|
+
// Parse JSON to get the object representation
|
|
153
138
|
jsonObject = JSON.parse(content);
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (this.phase === "update" && props.path !== this.props.path) {
|
|
158
|
-
await ignore("ENOENT", () => fs.unlink(this.props.path));
|
|
139
|
+
// Use StaticJsonFile to create the file
|
|
140
|
+
const file = await StaticJsonFile("file", props.path, jsonObject);
|
|
141
|
+
jsonContent = file.content;
|
|
159
142
|
}
|
|
160
|
-
// Write content to file
|
|
161
|
-
await fs.writeFile(props.path, jsonContent);
|
|
162
|
-
// Get file stats for timestamps
|
|
163
|
-
const stats = await fs.stat(props.path);
|
|
164
143
|
// Return the resource
|
|
165
144
|
return this({
|
|
166
145
|
...props,
|
|
167
146
|
schema: props.schema,
|
|
168
147
|
content: jsonContent,
|
|
169
148
|
json: jsonObject,
|
|
170
|
-
createdAt:
|
|
171
|
-
updatedAt:
|
|
149
|
+
createdAt: Date.now(),
|
|
150
|
+
updatedAt: Date.now(),
|
|
172
151
|
});
|
|
173
152
|
});
|
|
174
153
|
/**
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import prettier from "prettier";
|
|
2
1
|
import type { Context } from "../context";
|
|
3
2
|
import { Resource } from "../resource";
|
|
4
3
|
import type { Secret } from "../secret";
|
|
@@ -53,7 +52,7 @@ export interface TypeScriptFileProps {
|
|
|
53
52
|
* Prettier configuration to use for formatting the TypeScript code
|
|
54
53
|
* If not provided, will use the default Prettier configuration
|
|
55
54
|
*/
|
|
56
|
-
prettierConfig?:
|
|
55
|
+
prettierConfig?: object;
|
|
57
56
|
}
|
|
58
57
|
/**
|
|
59
58
|
* A TypeScript file that can be created, updated, and deleted
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { generateText } from "ai";
|
|
2
|
-
import
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import prettier from "prettier";
|
|
2
|
+
import { StaticTypeScriptFile } from "../fs/static-typescript-file";
|
|
5
3
|
import { Resource } from "../resource";
|
|
6
|
-
import { ignore } from "../util/ignore";
|
|
7
4
|
import { createModel } from "./client";
|
|
8
5
|
/**
|
|
9
6
|
* Default system prompt for TypeScript file generation
|
|
@@ -75,18 +72,9 @@ const DEFAULT_TS_SYSTEM_PROMPT = "You are a TypeScript code generator. Create Ty
|
|
|
75
72
|
* });
|
|
76
73
|
*/
|
|
77
74
|
export const TypeScriptFile = Resource("ai::TypeScriptFile", async function (id, props) {
|
|
78
|
-
//
|
|
79
|
-
await fs.mkdir(path.dirname(props.path), { recursive: true });
|
|
75
|
+
// Handle delete phase
|
|
80
76
|
if (this.phase === "delete") {
|
|
81
|
-
|
|
82
|
-
await fs.unlink(props.path);
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
// Ignore if file doesn't exist
|
|
86
|
-
if (error.code !== "ENOENT") {
|
|
87
|
-
throw error;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
77
|
+
// StaticTypeScriptFile will handle the deletion
|
|
90
78
|
return this.destroy();
|
|
91
79
|
}
|
|
92
80
|
// Use provided system prompt or default
|
|
@@ -121,33 +109,14 @@ export const TypeScriptFile = Resource("ai::TypeScriptFile", async function (id,
|
|
|
121
109
|
}
|
|
122
110
|
code = retryResult.code;
|
|
123
111
|
}
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
// Set default parser to typescript
|
|
127
|
-
const prettierOptions = {
|
|
128
|
-
parser: "typescript",
|
|
129
|
-
...props.prettierConfig,
|
|
130
|
-
};
|
|
131
|
-
// Format the code
|
|
132
|
-
code = await prettier.format(code, prettierOptions);
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
// If Prettier formatting fails, just use the unformatted code
|
|
136
|
-
console.warn("Failed to format TypeScript code with Prettier:", error);
|
|
137
|
-
}
|
|
138
|
-
if (this.phase === "update" && props.path !== this.props.path) {
|
|
139
|
-
await ignore("ENOENT", () => fs.unlink(this.props.path));
|
|
140
|
-
}
|
|
141
|
-
// Write content to file
|
|
142
|
-
await fs.writeFile(props.path, code);
|
|
143
|
-
// Get file stats for timestamps
|
|
144
|
-
const stats = await fs.stat(props.path);
|
|
112
|
+
// Use StaticTypeScriptFile to create/update the file
|
|
113
|
+
const file = await StaticTypeScriptFile("file", props.path, code);
|
|
145
114
|
// Return the resource
|
|
146
115
|
return this({
|
|
147
116
|
...props,
|
|
148
|
-
content:
|
|
149
|
-
createdAt:
|
|
150
|
-
updatedAt:
|
|
117
|
+
content: file.content,
|
|
118
|
+
createdAt: Date.now(),
|
|
119
|
+
updatedAt: Date.now(),
|
|
151
120
|
});
|
|
152
121
|
});
|
|
153
122
|
/**
|
package/lib/ai/vue-file.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { generateText } from "ai";
|
|
2
|
-
import
|
|
3
|
-
import path from "node:path";
|
|
2
|
+
import { StaticVueFile } from "../fs/static-vue-file";
|
|
4
3
|
import { Resource } from "../resource";
|
|
5
|
-
import { ignore } from "../util/ignore";
|
|
6
4
|
import { createModel } from "./client";
|
|
7
5
|
/**
|
|
8
6
|
* Default system prompt for Vue file generation
|
|
@@ -66,18 +64,8 @@ const DEFAULT_VUE_SYSTEM_PROMPT = "You are a Vue component generator. Create a s
|
|
|
66
64
|
* });
|
|
67
65
|
*/
|
|
68
66
|
export const VueFile = Resource("ai::VueFile", async function (id, props) {
|
|
69
|
-
//
|
|
70
|
-
await fs.mkdir(path.dirname(props.path), { recursive: true });
|
|
67
|
+
// Handle deletion phase
|
|
71
68
|
if (this.phase === "delete") {
|
|
72
|
-
try {
|
|
73
|
-
await fs.unlink(props.path);
|
|
74
|
-
}
|
|
75
|
-
catch (error) {
|
|
76
|
-
// Ignore if file doesn't exist
|
|
77
|
-
if (error.code !== "ENOENT") {
|
|
78
|
-
throw error;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
69
|
return this.destroy();
|
|
82
70
|
}
|
|
83
71
|
// Use provided system prompt or default
|
|
@@ -112,19 +100,14 @@ export const VueFile = Resource("ai::VueFile", async function (id, props) {
|
|
|
112
100
|
}
|
|
113
101
|
code = retryResult.code;
|
|
114
102
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
// Write content to file
|
|
119
|
-
await fs.writeFile(props.path, code);
|
|
120
|
-
// Get file stats for timestamps
|
|
121
|
-
const stats = await fs.stat(props.path);
|
|
103
|
+
// Use StaticVueFile to create/update the file
|
|
104
|
+
const file = await StaticVueFile("file", props.path, code);
|
|
122
105
|
// Return the resource
|
|
123
106
|
return this({
|
|
124
107
|
...props,
|
|
125
|
-
content:
|
|
126
|
-
createdAt:
|
|
127
|
-
updatedAt:
|
|
108
|
+
content: file.content,
|
|
109
|
+
createdAt: Date.now(),
|
|
110
|
+
updatedAt: Date.now(),
|
|
128
111
|
});
|
|
129
112
|
});
|
|
130
113
|
/**
|
package/lib/ai/yaml-file.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { generateObject, generateText } from "ai";
|
|
2
|
-
import
|
|
3
|
-
import path from "node:path";
|
|
2
|
+
import { StaticYamlFile } from "../fs/static-yaml-file";
|
|
4
3
|
import { Resource } from "../resource";
|
|
5
|
-
import { ignore } from "../util/ignore";
|
|
6
4
|
import { ark } from "./ark";
|
|
7
5
|
import { createModel } from "./client";
|
|
8
6
|
/**
|
|
@@ -96,29 +94,10 @@ const DEFAULT_YAML_SYSTEM_PROMPT = "You are a YAML generator. Create valid YAML
|
|
|
96
94
|
* });
|
|
97
95
|
*/
|
|
98
96
|
export const YAMLFile = Resource("ai::YAMLFile", async function (id, props) {
|
|
99
|
-
//
|
|
100
|
-
await fs.mkdir(path.dirname(props.path), { recursive: true });
|
|
97
|
+
// Handle deletion phase
|
|
101
98
|
if (this.phase === "delete") {
|
|
102
|
-
try {
|
|
103
|
-
await fs.unlink(props.path);
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
// Ignore if file doesn't exist
|
|
107
|
-
if (error.code !== "ENOENT") {
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
99
|
return this.destroy();
|
|
112
100
|
}
|
|
113
|
-
// Dynamic import js-yaml to avoid dependency issues
|
|
114
|
-
// This allows the code to work even if js-yaml is not installed
|
|
115
|
-
let yaml;
|
|
116
|
-
try {
|
|
117
|
-
yaml = await import("yaml");
|
|
118
|
-
}
|
|
119
|
-
catch (err) {
|
|
120
|
-
throw new Error("The 'yaml' package is required for the YAMLFile resource. Please install it with: bun add yaml");
|
|
121
|
-
}
|
|
122
101
|
let yamlContent;
|
|
123
102
|
let yamlObject;
|
|
124
103
|
// Check if schema is provided
|
|
@@ -136,15 +115,9 @@ export const YAMLFile = Resource("ai::YAMLFile", async function (id, props) {
|
|
|
136
115
|
: { temperature: props.temperature }),
|
|
137
116
|
});
|
|
138
117
|
yamlObject = object;
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
indent: 2,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
catch (error) {
|
|
146
|
-
throw new Error(`Failed to convert object to YAML: ${error.message}`);
|
|
147
|
-
}
|
|
118
|
+
// Let StaticYamlFile handle the YAML conversion
|
|
119
|
+
const file = await StaticYamlFile("file", props.path, yamlObject);
|
|
120
|
+
yamlContent = file.content;
|
|
148
121
|
}
|
|
149
122
|
else {
|
|
150
123
|
// Use fence-based extraction
|
|
@@ -181,29 +154,20 @@ export const YAMLFile = Resource("ai::YAMLFile", async function (id, props) {
|
|
|
181
154
|
content = retryResult.content;
|
|
182
155
|
}
|
|
183
156
|
yamlContent = content;
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
throw new Error(`Failed to parse YAML: ${error.message}`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
if (this.phase === "update" && props.path !== this.props.path) {
|
|
193
|
-
await ignore("ENOENT", () => fs.unlink(this.props.path));
|
|
157
|
+
// Create the file with the string content
|
|
158
|
+
const file = await StaticYamlFile("file", props.path, yamlContent);
|
|
159
|
+
// We need to parse the YAML to get the object representation
|
|
160
|
+
const yaml = await import("yaml");
|
|
161
|
+
yamlObject = yaml.parse(yamlContent);
|
|
194
162
|
}
|
|
195
|
-
// Write content to file
|
|
196
|
-
await fs.writeFile(props.path, yamlContent);
|
|
197
|
-
// Get file stats for timestamps
|
|
198
|
-
const stats = await fs.stat(props.path);
|
|
199
163
|
// Return the resource
|
|
200
164
|
return this({
|
|
201
165
|
...props,
|
|
202
166
|
schema: props.schema,
|
|
203
167
|
content: yamlContent,
|
|
204
168
|
yaml: yamlObject,
|
|
205
|
-
createdAt:
|
|
206
|
-
updatedAt:
|
|
169
|
+
createdAt: Date.now(),
|
|
170
|
+
updatedAt: Date.now(),
|
|
207
171
|
});
|
|
208
172
|
});
|
|
209
173
|
/**
|
package/lib/alchemy.d.ts
CHANGED
|
@@ -31,6 +31,10 @@ export interface Alchemy {
|
|
|
31
31
|
scope: typeof scope;
|
|
32
32
|
run: typeof run;
|
|
33
33
|
destroy: typeof destroy;
|
|
34
|
+
/**
|
|
35
|
+
* Get an environment variable and error if it's not set.
|
|
36
|
+
*/
|
|
37
|
+
env: typeof env;
|
|
34
38
|
/**
|
|
35
39
|
* Creates an encrypted secret that can be safely stored in state files.
|
|
36
40
|
* Requires a password to be set either globally in the application options
|
|
@@ -146,4 +150,5 @@ declare function run<T>(...args: [id: string, fn: (this: Scope, scope: Scope) =>
|
|
|
146
150
|
options: AlchemyOptions,
|
|
147
151
|
fn: (this: Scope, scope: Scope) => Promise<T>
|
|
148
152
|
]): Promise<T>;
|
|
153
|
+
export declare function env<T = string>(name: string, value?: T | undefined, error?: string): Promise<T>;
|
|
149
154
|
export {};
|