alchemy 0.2.2 → 0.2.4
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/lib/ai/ark.d.ts +11 -0
- package/lib/ai/ark.js +86 -0
- package/lib/ai/client.d.ts +51 -0
- package/lib/ai/client.js +29 -0
- package/lib/ai/document.d.ts +97 -0
- package/lib/ai/document.js +82 -0
- package/lib/ai/index.d.ts +2 -0
- package/lib/ai/index.js +2 -0
- package/lib/ai/object.d.ts +101 -0
- package/lib/ai/object.js +70 -0
- package/lib/alchemy.d.ts +84 -2
- package/lib/alchemy.js +114 -10
- package/lib/apply.d.ts +1 -1
- package/lib/apply.js +13 -10
- package/lib/aws/bucket.d.ts +87 -1
- package/lib/aws/bucket.js +48 -1
- package/lib/aws/function.d.ts +186 -1
- package/lib/aws/function.js +62 -8
- package/lib/aws/oidc/github-oidc-provider.d.ts +81 -0
- package/lib/aws/oidc/github-oidc-provider.js +68 -0
- package/lib/aws/oidc/oidc-provider.d.ts +14 -6
- package/lib/aws/oidc/oidc-provider.js +26 -5
- package/lib/aws/policy-attachment.d.ts +44 -1
- package/lib/aws/policy-attachment.js +31 -0
- package/lib/aws/policy.d.ts +160 -1
- package/lib/aws/policy.js +78 -0
- package/lib/aws/queue.d.ts +83 -1
- package/lib/aws/queue.js +39 -0
- package/lib/aws/role.d.ts +162 -1
- package/lib/aws/role.js +114 -0
- package/lib/aws/ses.d.ts +57 -2
- package/lib/aws/ses.js +42 -1
- package/lib/aws/table.d.ts +96 -1
- package/lib/aws/table.js +36 -0
- package/lib/cloudflare/asset-manifest.js +0 -1
- package/lib/cloudflare/bound.js +0 -1
- package/lib/cloudflare/bucket.d.ts +37 -1
- package/lib/cloudflare/bucket.js +36 -0
- package/lib/cloudflare/durable-object-namespace.d.ts +25 -0
- package/lib/cloudflare/durable-object-namespace.js +22 -0
- package/lib/cloudflare/kv-namespace.d.ts +37 -1
- package/lib/cloudflare/kv-namespace.js +36 -0
- package/lib/cloudflare/static-site.d.ts +53 -1
- package/lib/cloudflare/static-site.js +53 -1
- package/lib/cloudflare/worker-metadata.js +0 -1
- package/lib/cloudflare/worker.d.ts +64 -1
- package/lib/cloudflare/worker.js +63 -0
- package/lib/cloudflare/wrangler.json.d.ts +1 -1
- package/lib/cloudflare/zone-settings.js +0 -1
- package/lib/cloudflare/zone.d.ts +56 -1
- package/lib/cloudflare/zone.js +55 -0
- package/lib/context.d.ts +9 -4
- package/lib/context.js +4 -2
- package/lib/destroy.js +9 -8
- package/lib/esbuild/bundle.d.ts +43 -4
- package/lib/esbuild/bundle.js +16 -0
- package/lib/fs/file-collection.d.ts +16 -0
- package/lib/fs/file-collection.js +6 -0
- package/lib/fs/file-ref.d.ts +14 -0
- package/lib/fs/file-ref.js +6 -0
- package/lib/fs/file.d.ts +78 -5
- package/lib/fs/file.js +39 -3
- package/lib/fs/folder.d.ts +37 -5
- package/lib/fs/folder.js +28 -3
- package/lib/fs/index.d.ts +6 -0
- package/lib/fs/index.js +6 -0
- package/lib/fs/json-file.d.ts +16 -0
- package/lib/fs/json-file.js +7 -0
- package/lib/fs/text-file.d.ts +12 -0
- package/lib/fs/text-file.js +7 -0
- package/lib/fs/typescript-file.d.ts +19 -0
- package/lib/fs/typescript-file.js +14 -0
- package/lib/fs/yaml-file.d.ts +19 -0
- package/lib/fs/yaml-file.js +8 -0
- package/lib/github/secret.d.ts +63 -2
- package/lib/github/secret.js +61 -1
- package/lib/internal/docs.d.ts +5 -0
- package/lib/internal/docs.js +324 -0
- package/lib/resource.d.ts +2 -2
- package/lib/resource.js +4 -0
- package/lib/scope.d.ts +1 -1
- package/lib/scope.js +6 -6
- package/lib/secret.d.ts +67 -0
- package/lib/secret.js +67 -0
- package/lib/shadcn/component.d.ts +1 -1
- package/lib/state.d.ts +1 -1
- package/lib/state.js +8 -1
- package/lib/stripe/price.d.ts +51 -1
- package/lib/stripe/price.js +50 -0
- package/lib/stripe/product.d.ts +39 -1
- package/lib/stripe/product.js +38 -0
- package/lib/stripe/webhook.d.ts +46 -1
- package/lib/stripe/webhook.js +45 -0
- package/lib/test/bun.d.ts +64 -0
- package/lib/test/bun.js +35 -3
- package/lib/util/serde.d.ts +3 -1
- package/lib/util/serde.js +20 -4
- package/lib/vite/vite.d.ts +1 -1
- package/lib/vitepress/dependencies.d.ts +12 -0
- package/lib/vitepress/dependencies.js +35 -0
- package/lib/vitepress/home-page.d.ts +133 -0
- package/lib/vitepress/home-page.js +11 -0
- package/lib/vitepress/index.d.ts +2 -0
- package/lib/vitepress/index.js +2 -0
- package/lib/vitepress/vitepress.d.ts +71 -0
- package/lib/vitepress/vitepress.js +266 -0
- package/package.json +22 -10
- package/src/ai/ark.ts +118 -0
- package/src/ai/client.ts +78 -0
- package/src/ai/document.ts +160 -0
- package/src/ai/index.ts +2 -0
- package/src/ai/object.ts +149 -0
- package/src/alchemy.ts +192 -17
- package/src/apply.ts +25 -13
- package/src/aws/bucket.ts +111 -7
- package/src/aws/function.ts +231 -10
- package/src/aws/oidc/github-oidc-provider.ts +83 -0
- package/src/aws/oidc/oidc-provider.ts +39 -7
- package/src/aws/policy-attachment.ts +44 -1
- package/src/aws/policy.ts +177 -2
- package/src/aws/queue.ts +89 -0
- package/src/aws/role.ts +174 -2
- package/src/aws/ses.ts +56 -1
- package/src/aws/table.ts +103 -0
- package/src/cloudflare/bucket.ts +36 -0
- package/src/cloudflare/durable-object-namespace.ts +25 -0
- package/src/cloudflare/kv-namespace.ts +36 -0
- package/src/cloudflare/static-site.ts +53 -1
- package/src/cloudflare/worker.ts +63 -0
- package/src/cloudflare/zone.ts +55 -0
- package/src/context.ts +30 -10
- package/src/destroy.ts +11 -8
- package/src/esbuild/bundle.ts +53 -3
- package/src/fs/file-collection.ts +24 -0
- package/src/fs/file-ref.ts +22 -0
- package/src/fs/file.ts +107 -6
- package/src/fs/folder.ts +45 -5
- package/src/fs/index.ts +6 -0
- package/src/fs/json-file.ts +23 -0
- package/src/fs/text-file.ts +19 -0
- package/src/fs/typescript-file.ts +36 -0
- package/src/fs/yaml-file.ts +26 -0
- package/src/github/secret.ts +65 -2
- package/src/internal/docs.ts +369 -0
- package/src/resource.ts +7 -2
- package/src/scope.ts +9 -9
- package/src/secret.ts +67 -0
- package/src/state.ts +9 -2
- package/src/stripe/price.ts +50 -0
- package/src/stripe/product.ts +38 -0
- package/src/stripe/webhook.ts +45 -0
- package/src/test/bun.ts +83 -3
- package/src/util/serde.ts +30 -4
- package/src/vitepress/dependencies.ts +68 -0
- package/src/vitepress/home-page.ts +166 -0
- package/src/vitepress/index.md +75 -0
- package/src/vitepress/index.ts +2 -0
- package/src/vitepress/vitepress.ts +382 -0
- package/lib/project/vite.d.ts +0 -90
- package/lib/project/vite.js +0 -228
- package/src/project/vite.ts +0 -406
package/lib/ai/ark.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Tool as AITool, type Schema, type ToolExecutionOptions } from "ai";
|
|
2
|
+
import { type JsonSchema, type type } from "arktype";
|
|
3
|
+
export declare namespace ark {
|
|
4
|
+
function schema<T>(type: JsonSchema): Schema<T>;
|
|
5
|
+
interface Tool<Input extends type, Output> extends Omit<AITool<Schema<type.infer<Input>>>, "parameters" | "execute"> {
|
|
6
|
+
description?: string;
|
|
7
|
+
parameters: Input;
|
|
8
|
+
execute: (input: type.infer<Input>, options: ToolExecutionOptions) => Promise<Output>;
|
|
9
|
+
}
|
|
10
|
+
function tool<Input extends type, Output>(tool: Tool<Input, Output>): AITool<Schema<type.infer<Input>>, Output>;
|
|
11
|
+
}
|
package/lib/ai/ark.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { tool as aitool, jsonSchema, } from "ai";
|
|
2
|
+
import { ArkErrors } from "arktype";
|
|
3
|
+
export var ark;
|
|
4
|
+
(function (ark) {
|
|
5
|
+
function schema(type) {
|
|
6
|
+
const jsonSchemaObj = type.toJsonSchema();
|
|
7
|
+
const processedSchema = processSchema(jsonSchemaObj);
|
|
8
|
+
return jsonSchema(processedSchema, {
|
|
9
|
+
validate: (value) => {
|
|
10
|
+
const out = type(value);
|
|
11
|
+
if (out instanceof ArkErrors) {
|
|
12
|
+
return {
|
|
13
|
+
success: false,
|
|
14
|
+
error: new Error(out.summary),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
success: true,
|
|
19
|
+
value: out,
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
ark.schema = schema;
|
|
25
|
+
/**
|
|
26
|
+
* Recursively processes a JSON schema and sets additionalProperties: false
|
|
27
|
+
* for any object types.
|
|
28
|
+
*
|
|
29
|
+
* Structured Outputs requires additionalProperties: false
|
|
30
|
+
*/
|
|
31
|
+
function processSchema(schema) {
|
|
32
|
+
if (!schema || typeof schema !== "object")
|
|
33
|
+
return schema;
|
|
34
|
+
// Create a copy to avoid mutating the original
|
|
35
|
+
const result = { ...schema };
|
|
36
|
+
// Convert anyOf with all const values to enum
|
|
37
|
+
if (result.anyOf && Array.isArray(result.anyOf)) {
|
|
38
|
+
const allConst = result.anyOf.every((item) => item && typeof item === "object" && "const" in item);
|
|
39
|
+
if (allConst) {
|
|
40
|
+
// Extract all const values
|
|
41
|
+
const enumValues = result.anyOf.map((item) => item.const);
|
|
42
|
+
// Determine the type based on the first const value
|
|
43
|
+
// Assuming all const values are of the same type
|
|
44
|
+
const firstType = typeof enumValues[0];
|
|
45
|
+
// Replace anyOf with enum
|
|
46
|
+
delete result.anyOf;
|
|
47
|
+
result.type = firstType;
|
|
48
|
+
result.enum = enumValues;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// Process each item in anyOf
|
|
52
|
+
result.anyOf = result.anyOf.map(processSchema);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// If this is an object type, set additionalProperties: false
|
|
56
|
+
if (result.type === "object") {
|
|
57
|
+
result.additionalProperties = false;
|
|
58
|
+
result.properties ??= {};
|
|
59
|
+
}
|
|
60
|
+
// Process properties of objects
|
|
61
|
+
if (result.properties && typeof result.properties === "object") {
|
|
62
|
+
result.properties = Object.fromEntries(Object.entries(result.properties).map(([key, value]) => [
|
|
63
|
+
key,
|
|
64
|
+
processSchema(value),
|
|
65
|
+
]));
|
|
66
|
+
}
|
|
67
|
+
// Process items in arrays
|
|
68
|
+
if (result.items) {
|
|
69
|
+
result.items = processSchema(result.items);
|
|
70
|
+
}
|
|
71
|
+
// Process allOf, oneOf
|
|
72
|
+
for (const key of ["allOf", "oneOf"]) {
|
|
73
|
+
if (Array.isArray(result[key])) {
|
|
74
|
+
result[key] = result[key].map(processSchema);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
function tool(tool) {
|
|
80
|
+
return aitool({
|
|
81
|
+
...tool,
|
|
82
|
+
parameters: ark.schema(tool.parameters),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
ark.tool = tool;
|
|
86
|
+
})(ark || (ark = {}));
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Secret } from "../secret";
|
|
2
|
+
/**
|
|
3
|
+
* Model configuration for AI operations
|
|
4
|
+
*/
|
|
5
|
+
export interface ModelConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Model ID to use
|
|
8
|
+
* @default 'gpt-4o'
|
|
9
|
+
*/
|
|
10
|
+
id?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Model provider name
|
|
13
|
+
* @default 'openai'
|
|
14
|
+
*/
|
|
15
|
+
provider?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Model-specific options
|
|
18
|
+
*/
|
|
19
|
+
options?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Configuration for creating an OpenAI client
|
|
23
|
+
*/
|
|
24
|
+
export interface ClientConfig {
|
|
25
|
+
/**
|
|
26
|
+
* Base URL for the OpenAI API
|
|
27
|
+
* @default 'https://api.openai.com/v1'
|
|
28
|
+
*/
|
|
29
|
+
baseURL?: string;
|
|
30
|
+
/**
|
|
31
|
+
* OpenAI API key to use for generating content
|
|
32
|
+
* If not provided, will use OPENAI_API_KEY environment variable
|
|
33
|
+
*/
|
|
34
|
+
apiKey?: Secret;
|
|
35
|
+
/**
|
|
36
|
+
* Model configuration
|
|
37
|
+
*/
|
|
38
|
+
model?: ModelConfig;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Creates an OpenAI-compatible client with the given configuration
|
|
42
|
+
*/
|
|
43
|
+
export declare function createClient(config: ClientConfig): import("@ai-sdk/openai-compatible").OpenAICompatibleProvider<string, string, string, string>;
|
|
44
|
+
/**
|
|
45
|
+
* Gets the model ID from the configuration or returns the default
|
|
46
|
+
*/
|
|
47
|
+
export declare function getModelId(config: ClientConfig): string;
|
|
48
|
+
/**
|
|
49
|
+
* Gets the model options from the configuration
|
|
50
|
+
*/
|
|
51
|
+
export declare function getModelOptions(config: ClientConfig): Record<string, unknown>;
|
package/lib/ai/client.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
2
|
+
/**
|
|
3
|
+
* Creates an OpenAI-compatible client with the given configuration
|
|
4
|
+
*/
|
|
5
|
+
export function createClient(config) {
|
|
6
|
+
// Get API key from props or environment
|
|
7
|
+
const apiKey = config.apiKey?.unencrypted || process.env.OPENAI_API_KEY;
|
|
8
|
+
if (!apiKey) {
|
|
9
|
+
throw new Error("OpenAI API key is required");
|
|
10
|
+
}
|
|
11
|
+
// Initialize OpenAI compatible provider
|
|
12
|
+
return createOpenAICompatible({
|
|
13
|
+
name: config.model?.provider || "openai",
|
|
14
|
+
apiKey,
|
|
15
|
+
baseURL: config.baseURL || "https://api.openai.com/v1",
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Gets the model ID from the configuration or returns the default
|
|
20
|
+
*/
|
|
21
|
+
export function getModelId(config) {
|
|
22
|
+
return config.model?.id || "gpt-4o";
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Gets the model options from the configuration
|
|
26
|
+
*/
|
|
27
|
+
export function getModelOptions(config) {
|
|
28
|
+
return config.model?.options || {};
|
|
29
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Context } from "../context";
|
|
2
|
+
import { Resource } from "../resource";
|
|
3
|
+
import type { Secret } from "../secret";
|
|
4
|
+
import { type ModelConfig } from "./client";
|
|
5
|
+
/**
|
|
6
|
+
* Properties for creating or updating a Document
|
|
7
|
+
*/
|
|
8
|
+
export interface DocumentProps {
|
|
9
|
+
/**
|
|
10
|
+
* Path to the markdown document
|
|
11
|
+
*/
|
|
12
|
+
path: string;
|
|
13
|
+
/**
|
|
14
|
+
* Base URL for the OpenAI API
|
|
15
|
+
* @default 'https://api.openai.com/v1'
|
|
16
|
+
*/
|
|
17
|
+
baseURL?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Prompt for generating content
|
|
20
|
+
* Use alchemy template literals to include file context:
|
|
21
|
+
* @example
|
|
22
|
+
* prompt: await alchemy`
|
|
23
|
+
* Generate docs using:
|
|
24
|
+
* ${alchemy.file("src/api.ts")}
|
|
25
|
+
* `
|
|
26
|
+
*/
|
|
27
|
+
prompt: string;
|
|
28
|
+
/**
|
|
29
|
+
* OpenAI API key to use for generating content
|
|
30
|
+
* If not provided, will use OPENAI_API_KEY environment variable
|
|
31
|
+
*/
|
|
32
|
+
apiKey?: Secret;
|
|
33
|
+
/**
|
|
34
|
+
* Model configuration
|
|
35
|
+
*/
|
|
36
|
+
model?: ModelConfig;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A markdown document that can be created, updated, and deleted
|
|
40
|
+
*/
|
|
41
|
+
export interface Document extends DocumentProps, Resource<"docs::Document"> {
|
|
42
|
+
/**
|
|
43
|
+
* Content of the document
|
|
44
|
+
*/
|
|
45
|
+
content: string;
|
|
46
|
+
/**
|
|
47
|
+
* Time at which the document was created
|
|
48
|
+
*/
|
|
49
|
+
createdAt: number;
|
|
50
|
+
/**
|
|
51
|
+
* Time at which the document was last updated
|
|
52
|
+
*/
|
|
53
|
+
updatedAt: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resource for managing AI-generated markdown documents using the Vercel AI SDK.
|
|
57
|
+
* Supports powerful context handling through the alchemy template literal tag.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* // Create a document using alchemy template literals for context
|
|
61
|
+
* const apiDocs = await Document("api-docs", {
|
|
62
|
+
* path: "./docs/api.md",
|
|
63
|
+
* prompt: await alchemy`
|
|
64
|
+
* Generate API documentation based on these source files:
|
|
65
|
+
* ${alchemy.file("src/api.ts")}
|
|
66
|
+
* ${alchemy.file("src/types.ts")}
|
|
67
|
+
* `
|
|
68
|
+
* // The above will automatically append the file contents as code blocks:
|
|
69
|
+
* //
|
|
70
|
+
* // Generate API documentation based on these source files:
|
|
71
|
+
* // [api.ts](src/api.ts)
|
|
72
|
+
* // [types.ts](src/types.ts)
|
|
73
|
+
* //
|
|
74
|
+
* // // src/api.ts
|
|
75
|
+
* // ```ts
|
|
76
|
+
* // ... contents of api.ts ...
|
|
77
|
+
* // ```
|
|
78
|
+
* //
|
|
79
|
+
* // // src/types.ts
|
|
80
|
+
* // ```ts
|
|
81
|
+
* // ... contents of types.ts ...
|
|
82
|
+
* // ```
|
|
83
|
+
* });
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // Use alchemy template literals with file collections
|
|
87
|
+
* const modelDocs = await Document("models", {
|
|
88
|
+
* path: "./docs/models.md",
|
|
89
|
+
* prompt: await alchemy`
|
|
90
|
+
* Write documentation for these data models:
|
|
91
|
+
* ${alchemy.files("src/models/user.ts", "src/models/post.ts")}
|
|
92
|
+
* `
|
|
93
|
+
* // This creates a prompt with all files appended as code blocks,
|
|
94
|
+
* // automatically handling syntax highlighting based on file extensions
|
|
95
|
+
* });
|
|
96
|
+
*/
|
|
97
|
+
export declare const Document: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<Document>, id: string, props: DocumentProps) => Promise<Document>);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { generateText } from "ai";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { Resource } from "../resource";
|
|
5
|
+
import { createClient, getModelId, getModelOptions, } from "./client";
|
|
6
|
+
/**
|
|
7
|
+
* Resource for managing AI-generated markdown documents using the Vercel AI SDK.
|
|
8
|
+
* Supports powerful context handling through the alchemy template literal tag.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Create a document using alchemy template literals for context
|
|
12
|
+
* const apiDocs = await Document("api-docs", {
|
|
13
|
+
* path: "./docs/api.md",
|
|
14
|
+
* prompt: await alchemy`
|
|
15
|
+
* Generate API documentation based on these source files:
|
|
16
|
+
* ${alchemy.file("src/api.ts")}
|
|
17
|
+
* ${alchemy.file("src/types.ts")}
|
|
18
|
+
* `
|
|
19
|
+
* // The above will automatically append the file contents as code blocks:
|
|
20
|
+
* //
|
|
21
|
+
* // Generate API documentation based on these source files:
|
|
22
|
+
* // [api.ts](src/api.ts)
|
|
23
|
+
* // [types.ts](src/types.ts)
|
|
24
|
+
* //
|
|
25
|
+
* // // src/api.ts
|
|
26
|
+
* // ```ts
|
|
27
|
+
* // ... contents of api.ts ...
|
|
28
|
+
* // ```
|
|
29
|
+
* //
|
|
30
|
+
* // // src/types.ts
|
|
31
|
+
* // ```ts
|
|
32
|
+
* // ... contents of types.ts ...
|
|
33
|
+
* // ```
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // Use alchemy template literals with file collections
|
|
38
|
+
* const modelDocs = await Document("models", {
|
|
39
|
+
* path: "./docs/models.md",
|
|
40
|
+
* prompt: await alchemy`
|
|
41
|
+
* Write documentation for these data models:
|
|
42
|
+
* ${alchemy.files("src/models/user.ts", "src/models/post.ts")}
|
|
43
|
+
* `
|
|
44
|
+
* // This creates a prompt with all files appended as code blocks,
|
|
45
|
+
* // automatically handling syntax highlighting based on file extensions
|
|
46
|
+
* });
|
|
47
|
+
*/
|
|
48
|
+
export const Document = Resource("docs::Document", async function (id, props) {
|
|
49
|
+
// Ensure directory exists
|
|
50
|
+
await fs.mkdir(path.dirname(props.path), { recursive: true });
|
|
51
|
+
if (this.phase === "delete") {
|
|
52
|
+
try {
|
|
53
|
+
await fs.unlink(props.path);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// Ignore if file doesn't exist
|
|
57
|
+
if (error.code !== "ENOENT") {
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return this.destroy();
|
|
62
|
+
}
|
|
63
|
+
// Initialize OpenAI compatible provider using shared client
|
|
64
|
+
const provider = createClient(props);
|
|
65
|
+
// Generate content
|
|
66
|
+
const { text } = await generateText({
|
|
67
|
+
model: provider(getModelId(props)),
|
|
68
|
+
prompt: props.prompt,
|
|
69
|
+
...getModelOptions(props),
|
|
70
|
+
});
|
|
71
|
+
// Write content to file
|
|
72
|
+
await fs.writeFile(props.path, text);
|
|
73
|
+
// Get file stats for timestamps
|
|
74
|
+
const stats = await fs.stat(props.path);
|
|
75
|
+
// Return the resource
|
|
76
|
+
return this({
|
|
77
|
+
...props,
|
|
78
|
+
content: text,
|
|
79
|
+
createdAt: stats.birthtimeMs,
|
|
80
|
+
updatedAt: stats.mtimeMs,
|
|
81
|
+
});
|
|
82
|
+
});
|
package/lib/ai/index.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { JsonSchema, Type, type } from "arktype";
|
|
2
|
+
import type { Context } from "../context";
|
|
3
|
+
import { Resource } from "../resource";
|
|
4
|
+
import type { Secret } from "../secret";
|
|
5
|
+
import { type ModelConfig } from "./client";
|
|
6
|
+
/**
|
|
7
|
+
* Properties for creating or updating an AI Object
|
|
8
|
+
*/
|
|
9
|
+
export interface ObjectProps<T extends Type<any, any>> {
|
|
10
|
+
/**
|
|
11
|
+
* The ArkType schema to validate and structure the generated content
|
|
12
|
+
*/
|
|
13
|
+
schema: T | JsonSchema;
|
|
14
|
+
/**
|
|
15
|
+
* Prompt for generating the content
|
|
16
|
+
* Use alchemy template literals to include file context:
|
|
17
|
+
* @example
|
|
18
|
+
* prompt: await alchemy`
|
|
19
|
+
* Generate a description for:
|
|
20
|
+
* ${alchemy.file("src/data.ts")}
|
|
21
|
+
* `
|
|
22
|
+
*/
|
|
23
|
+
prompt: string;
|
|
24
|
+
/**
|
|
25
|
+
* System prompt to guide the AI's behavior
|
|
26
|
+
* @example
|
|
27
|
+
* system: "You are a technical writer tasked with describing code"
|
|
28
|
+
*/
|
|
29
|
+
system?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Base URL for the OpenAI API
|
|
32
|
+
* @default 'https://api.openai.com/v1'
|
|
33
|
+
*/
|
|
34
|
+
baseURL?: string;
|
|
35
|
+
/**
|
|
36
|
+
* OpenAI API key to use for generating content
|
|
37
|
+
* If not provided, will use OPENAI_API_KEY environment variable
|
|
38
|
+
*/
|
|
39
|
+
apiKey?: Secret;
|
|
40
|
+
/**
|
|
41
|
+
* Model configuration
|
|
42
|
+
*/
|
|
43
|
+
model?: ModelConfig;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A resource that uses AI to generate structured content based on a schema
|
|
47
|
+
*/
|
|
48
|
+
export interface Object<T> extends Resource<"ai::Object"> {
|
|
49
|
+
type: JsonSchema;
|
|
50
|
+
/**
|
|
51
|
+
* The generated content, typed according to the provided schema
|
|
52
|
+
*/
|
|
53
|
+
object: T;
|
|
54
|
+
/**
|
|
55
|
+
* Time at which the content was generated
|
|
56
|
+
*/
|
|
57
|
+
createdAt: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resource for generating structured content using the Vercel AI SDK.
|
|
61
|
+
* Supports powerful context handling through the alchemy template literal tag.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // Generate a product description with specific fields
|
|
65
|
+
* const productSchema = type({
|
|
66
|
+
* name: "string",
|
|
67
|
+
* description: "string",
|
|
68
|
+
* features: "string[]",
|
|
69
|
+
* price: "number"
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* const product = await Object("new-product", {
|
|
73
|
+
* schema: productSchema,
|
|
74
|
+
* prompt: "Generate a product description for a new smartphone",
|
|
75
|
+
* system: "You are a product copywriter specializing in tech products"
|
|
76
|
+
* });
|
|
77
|
+
*
|
|
78
|
+
* console.log(product.content); // Typed as per schema
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* // Generate code documentation with context
|
|
82
|
+
* const docSchema = type({
|
|
83
|
+
* summary: "string",
|
|
84
|
+
* parameters: {
|
|
85
|
+
* name: "string",
|
|
86
|
+
* type: "string",
|
|
87
|
+
* description: "string"
|
|
88
|
+
* }[],
|
|
89
|
+
* returns: "string"
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* const docs = await Object("function-docs", {
|
|
93
|
+
* schema: docSchema,
|
|
94
|
+
* prompt: await alchemy`
|
|
95
|
+
* Generate documentation for this function:
|
|
96
|
+
* ${alchemy.file("src/utils/format.ts")}
|
|
97
|
+
* `,
|
|
98
|
+
* system: "You are a technical documentation writer"
|
|
99
|
+
* });
|
|
100
|
+
*/
|
|
101
|
+
export declare const Object: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | (<const T extends Type<any, any>>(this: Context<Object<any>>, id: string, props: ObjectProps<T>) => Promise<Object<type.infer<T>>>);
|
package/lib/ai/object.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { generateObject } from "ai";
|
|
2
|
+
import { Resource } from "../resource";
|
|
3
|
+
import { ark } from "./ark";
|
|
4
|
+
import { createClient, getModelId, getModelOptions, } from "./client";
|
|
5
|
+
/**
|
|
6
|
+
* Resource for generating structured content using the Vercel AI SDK.
|
|
7
|
+
* Supports powerful context handling through the alchemy template literal tag.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Generate a product description with specific fields
|
|
11
|
+
* const productSchema = type({
|
|
12
|
+
* name: "string",
|
|
13
|
+
* description: "string",
|
|
14
|
+
* features: "string[]",
|
|
15
|
+
* price: "number"
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* const product = await Object("new-product", {
|
|
19
|
+
* schema: productSchema,
|
|
20
|
+
* prompt: "Generate a product description for a new smartphone",
|
|
21
|
+
* system: "You are a product copywriter specializing in tech products"
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* console.log(product.content); // Typed as per schema
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Generate code documentation with context
|
|
28
|
+
* const docSchema = type({
|
|
29
|
+
* summary: "string",
|
|
30
|
+
* parameters: {
|
|
31
|
+
* name: "string",
|
|
32
|
+
* type: "string",
|
|
33
|
+
* description: "string"
|
|
34
|
+
* }[],
|
|
35
|
+
* returns: "string"
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* const docs = await Object("function-docs", {
|
|
39
|
+
* schema: docSchema,
|
|
40
|
+
* prompt: await alchemy`
|
|
41
|
+
* Generate documentation for this function:
|
|
42
|
+
* ${alchemy.file("src/utils/format.ts")}
|
|
43
|
+
* `,
|
|
44
|
+
* system: "You are a technical documentation writer"
|
|
45
|
+
* });
|
|
46
|
+
*/
|
|
47
|
+
export const Object = Resource("ai::Object", async function (id, props) {
|
|
48
|
+
if (this.phase === "delete") {
|
|
49
|
+
return this.destroy();
|
|
50
|
+
}
|
|
51
|
+
// Initialize OpenAI compatible provider using shared client
|
|
52
|
+
const provider = createClient(props);
|
|
53
|
+
// Generate structured output using generateObject
|
|
54
|
+
const { object } = await generateObject({
|
|
55
|
+
model: provider(getModelId(props)),
|
|
56
|
+
// Convert ArkType schema to Zod schema for generateObject
|
|
57
|
+
// This is needed because generateObject expects a Zod schema
|
|
58
|
+
schema: ark.schema(props.schema),
|
|
59
|
+
system: props.system ||
|
|
60
|
+
"You are an AI assistant tasked with generating structured content.",
|
|
61
|
+
prompt: props.prompt,
|
|
62
|
+
...getModelOptions(props),
|
|
63
|
+
});
|
|
64
|
+
// Return the resource with typed content
|
|
65
|
+
return this({
|
|
66
|
+
type: props.schema,
|
|
67
|
+
object: object,
|
|
68
|
+
createdAt: Date.now(),
|
|
69
|
+
});
|
|
70
|
+
});
|
package/lib/alchemy.d.ts
CHANGED
|
@@ -2,14 +2,71 @@ import { destroy } from "./destroy";
|
|
|
2
2
|
import { Scope } from "./scope";
|
|
3
3
|
import { secret } from "./secret";
|
|
4
4
|
import type { StateStoreType } from "./state";
|
|
5
|
+
/**
|
|
6
|
+
* Type alias for semantic highlighting of `alchemy` as a type keyword
|
|
7
|
+
*/
|
|
5
8
|
export type alchemy = Alchemy;
|
|
6
9
|
export declare const alchemy: Alchemy;
|
|
10
|
+
/**
|
|
11
|
+
* The Alchemy interface provides core functionality and is augmented by providers.
|
|
12
|
+
* Supports both application scoping with secrets and template string interpolation.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Create an application scope with stage and secret handling
|
|
16
|
+
* const app = alchemy("github:alchemy", {
|
|
17
|
+
* stage: "prod",
|
|
18
|
+
* phase: "up",
|
|
19
|
+
* // Required for encrypting/decrypting secrets
|
|
20
|
+
* password: process.env.SECRET_PASSPHRASE
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Create a resource with encrypted secrets
|
|
24
|
+
* const resource = await Resource("my-resource", {
|
|
25
|
+
* apiKey: alchemy.secret(process.env.API_KEY)
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* await app.finalize();
|
|
29
|
+
*/
|
|
7
30
|
export interface Alchemy {
|
|
8
31
|
scope: typeof scope;
|
|
9
32
|
run: typeof run;
|
|
10
33
|
destroy: typeof destroy;
|
|
34
|
+
/**
|
|
35
|
+
* Creates an encrypted secret that can be safely stored in state files.
|
|
36
|
+
* Requires a password to be set either globally in the application options
|
|
37
|
+
* or locally in the current scope.
|
|
38
|
+
*/
|
|
11
39
|
secret: typeof secret;
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new application scope with the given name and options.
|
|
42
|
+
* Used to create and manage resources with proper secret handling.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* const app = alchemy("my-app", {
|
|
46
|
+
* stage: "prod",
|
|
47
|
+
* // Required for encrypting/decrypting secrets
|
|
48
|
+
* password: process.env.SECRET_PASSPHRASE
|
|
49
|
+
* });
|
|
50
|
+
*/
|
|
12
51
|
(...parameters: Parameters<typeof scope>): ReturnType<typeof scope>;
|
|
52
|
+
/**
|
|
53
|
+
* Template literal tag that supports file interpolation for documentation.
|
|
54
|
+
* Automatically formats the content and appends file contents as code blocks.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* // Generate documentation using file contents
|
|
58
|
+
* await Document("api-docs", {
|
|
59
|
+
* prompt: await alchemy`
|
|
60
|
+
* Generate docs using the contents of:
|
|
61
|
+
* ${alchemy.file("README.md")}
|
|
62
|
+
* ${alchemy.file("./.cursorrules")}
|
|
63
|
+
*
|
|
64
|
+
* And here are the source files:
|
|
65
|
+
* ${alchemy.files(files)}
|
|
66
|
+
* `
|
|
67
|
+
* });
|
|
68
|
+
*/
|
|
69
|
+
(template: TemplateStringsArray, ...values: any[]): Promise<string>;
|
|
13
70
|
}
|
|
14
71
|
export interface AlchemyOptions {
|
|
15
72
|
/**
|
|
@@ -50,15 +107,40 @@ export interface AlchemyOptions {
|
|
|
50
107
|
quiet?: boolean;
|
|
51
108
|
/**
|
|
52
109
|
* A passphrase to use to encrypt/decrypt secrets.
|
|
110
|
+
* Required if using alchemy.secret() in this scope.
|
|
53
111
|
*/
|
|
54
112
|
password?: string;
|
|
55
113
|
}
|
|
56
114
|
/**
|
|
57
115
|
* Enter a new scope synchronously.
|
|
58
|
-
*
|
|
59
|
-
* @
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* // Create a scope with a password for secret handling
|
|
119
|
+
* await using scope = alchemy.scope("my-scope", {
|
|
120
|
+
* password: process.env.SECRET_PASSPHRASE
|
|
121
|
+
* });
|
|
122
|
+
*
|
|
123
|
+
* // Use secrets within the scope
|
|
124
|
+
* const resource = await Resource("my-resource", {
|
|
125
|
+
* apiKey: alchemy.secret(process.env.API_KEY)
|
|
126
|
+
* });
|
|
60
127
|
*/
|
|
61
128
|
declare function scope(id: string | undefined, options?: AlchemyOptions): Scope;
|
|
129
|
+
/**
|
|
130
|
+
* Run a function in a new scope asynchronously.
|
|
131
|
+
* Useful for isolating secret handling with a specific password.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* // Run operations in a scope with its own password
|
|
135
|
+
* await alchemy.run("secure-scope", {
|
|
136
|
+
* password: process.env.SCOPE_PASSWORD
|
|
137
|
+
* }, async () => {
|
|
138
|
+
* // Secrets in this scope will use this password
|
|
139
|
+
* const resource = await Resource("my-resource", {
|
|
140
|
+
* apiKey: alchemy.secret(process.env.API_KEY)
|
|
141
|
+
* });
|
|
142
|
+
* });
|
|
143
|
+
*/
|
|
62
144
|
declare function run<T>(...args: [id: string, fn: (this: Scope, scope: Scope) => Promise<T>] | [
|
|
63
145
|
id: string,
|
|
64
146
|
options: AlchemyOptions,
|