alchemy 0.0.8 → 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.
Files changed (105) hide show
  1. package/package.json +23 -22
  2. package/src/alchemize.ts +151 -0
  3. package/src/apply.ts +117 -0
  4. package/src/components/agent/agent.ts +45 -0
  5. package/src/components/agent/anthropic.ts +27 -0
  6. package/src/components/agent/index.ts +4 -0
  7. package/src/components/agent/model.ts +22 -0
  8. package/src/components/agent/openai.ts +44 -0
  9. package/src/components/agent/package.ts +126 -0
  10. package/src/components/agent/requirements.ts +111 -0
  11. package/src/components/agent/scrape.ts +47 -0
  12. package/src/components/agent/tsconfig.ts +150 -0
  13. package/src/components/agent/typescript.ts +177 -0
  14. package/src/components/aws/bucket.ts +168 -0
  15. package/src/components/aws/function.ts +302 -0
  16. package/src/components/aws/index.ts +6 -0
  17. package/src/components/aws/policy.ts +245 -0
  18. package/src/components/aws/queue.ts +222 -0
  19. package/src/components/aws/role.ts +245 -0
  20. package/src/components/aws/table.ts +172 -0
  21. package/src/components/esbuild.ts +128 -0
  22. package/src/components/fs.ts +45 -0
  23. package/src/destroy.ts +63 -0
  24. package/src/error.ts +16 -0
  25. package/src/global.ts +72 -0
  26. package/src/index.ts +6 -0
  27. package/src/input.ts +16 -0
  28. package/src/main.ts +34 -0
  29. package/src/output.ts +49 -0
  30. package/src/resource.ts +313 -0
  31. package/src/state.ts +100 -0
  32. package/test/aws/function.test.ts +144 -0
  33. package/test/aws/queue.test.ts +85 -0
  34. package/test/aws/role.test.ts +153 -0
  35. package/test/aws/table.test.ts +73 -0
  36. package/test/esbuild.test.ts +41 -0
  37. package/test/handler.ts +9 -0
  38. package/tsconfig.json +7 -0
  39. package/Column.js +0 -1
  40. package/Create.js +0 -1
  41. package/Delete.js +0 -1
  42. package/Drop.js +0 -1
  43. package/Insert.js +0 -1
  44. package/Makefile +0 -8
  45. package/README.md +0 -0
  46. package/Select.js +0 -1
  47. package/Table.js +0 -1
  48. package/Transaction.js +0 -1
  49. package/Types.js +0 -1
  50. package/Update.js +0 -1
  51. package/alchemy.js +0 -57
  52. package/alchemy.sublime-project +0 -9
  53. package/alchemy.sublime-workspace +0 -2304
  54. package/config/app.js +0 -8
  55. package/config/db.js +0 -20
  56. package/copy.js +0 -1
  57. package/defaultEngine.js +0 -8
  58. package/engine.js +0 -282
  59. package/getInstantiatorFunction.js +0 -28
  60. package/index.js +0 -52
  61. package/jstest.js +0 -3
  62. package/loggerFactory.js +0 -27
  63. package/logs/alchemy.log +0 -46
  64. package/logs/sql.log +0 -41
  65. package/sql/Column.js +0 -158
  66. package/sql/Columnable.js +0 -21
  67. package/sql/Create.js +0 -15
  68. package/sql/Delete.js +0 -34
  69. package/sql/Drop.js +0 -36
  70. package/sql/Insert.js +0 -96
  71. package/sql/SchemaElement.js +0 -11
  72. package/sql/Select.js +0 -192
  73. package/sql/SqlBase.js +0 -134
  74. package/sql/SqlStatement.js +0 -80
  75. package/sql/Table.js +0 -96
  76. package/sql/Transaction.js +0 -45
  77. package/sql/Update.js +0 -105
  78. package/sql/defineElement.js +0 -26
  79. package/sql/defineStatement.js +0 -26
  80. package/sql/types/BaseType.js +0 -43
  81. package/sql/types/BigSerialType.js +0 -5
  82. package/sql/types/IntegerType.js +0 -5
  83. package/sql/types/JsonType.js +0 -5
  84. package/sql/types/SerialType.js +0 -5
  85. package/sql/types/StringType.js +0 -31
  86. package/sql/types/TimestampType.js +0 -5
  87. package/sql/types/UUIDType.js +0 -5
  88. package/sql/types/defineType.js +0 -25
  89. package/sql/types/getType.js +0 -12
  90. package/sql/types/index.js +0 -11
  91. package/sql/where.js +0 -31
  92. package/sql/whereToString.js +0 -29
  93. package/test/ColumnTests.js +0 -80
  94. package/test/CreateTable.js +0 -54
  95. package/test/DeleteTests.js +0 -28
  96. package/test/DropTests.js +0 -23
  97. package/test/GetTypeTests.js +0 -56
  98. package/test/InsertTests.js +0 -86
  99. package/test/SelectExistsTests.js +0 -26
  100. package/test/SelectTests.js +0 -130
  101. package/test/TableTests.js +0 -46
  102. package/test.js +0 -8
  103. package/test.ls +0 -4
  104. package/validate.js +0 -26
  105. package/x.js +0 -5
package/package.json CHANGED
@@ -1,28 +1,29 @@
1
1
  {
2
2
  "name": "alchemy",
3
- "version": "0.0.8",
4
- "description": "sql alchemy",
5
- "main": "alchemy.js",
6
- "scripts": {
7
- "test": "make test",
8
- "test-w": "make test-w",
9
- "test-debug": "mocha --debug-brk --require should"
10
- },
11
- "author": "Radu Brehar",
12
- "license": "MIT",
13
- "dependencies": {
14
- "pg": "~3.4.0",
15
- "expect.js": "^0.3.0",
16
- "functionally": "^0.2.1",
17
- "classy": "^1.4.0",
18
- "bluebird": "~2.2.2",
19
- "bunyan": "^0.23.1",
20
- "jsesc": "~0.4.3",
21
- "newify": "~1.0.0",
22
- "ustring": "^1.1.1"
3
+ "version": "0.1.1",
4
+ "module": "index.ts",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts",
8
+ "./agent": "./src/components/agent/index.ts",
9
+ "./fs": "./src/components/fs/index.ts",
10
+ "./aws": "./src/components/aws/index.ts",
11
+ "./esbuild": "./src/components/esbuild.ts"
23
12
  },
24
13
  "devDependencies": {
25
- "should": "~4.0.4",
26
- "mocha": "~1.20.1"
14
+ "@types/bun": "latest"
15
+ },
16
+ "peerDependencies": {
17
+ "@ai-sdk/openai": "^1.0.0",
18
+ "@aws-sdk/client-dynamodb": "^3.0.0",
19
+ "@aws-sdk/client-iam": "^3.0.0",
20
+ "@aws-sdk/client-lambda": "^3.0.0",
21
+ "@aws-sdk/client-s3": "^3.0.0",
22
+ "@aws-sdk/client-sqs": "^3.0.1",
23
+ "ai": "^4.0.0",
24
+ "jszip": "^3.0.0",
25
+ "esbuild": "^0.24.2",
26
+ "turndown": "^7.2.0",
27
+ "zod": "^3.24.1"
27
28
  }
28
29
  }
@@ -0,0 +1,151 @@
1
+ import { evaluate } from "./apply";
2
+ import { destroy } from "./destroy";
3
+ import { defaultStage, nodes, providers, stateStore } from "./global";
4
+ import type { StateStore } from "./state";
5
+
6
+ let finalized = false;
7
+
8
+ export interface AlchemizeOptions {
9
+ /**
10
+ * Determines whether the resources will be created/updated or deleted.
11
+ *
12
+ * @default "up"
13
+ */
14
+ mode?: "up" | "destroy";
15
+ /**
16
+ * Name to scope the resource state under (e.g. `.alchemy/{stage}/..`).
17
+ *
18
+ * @default - your POSIX username
19
+ */
20
+ stage?: string;
21
+ /**
22
+ * If true, will not prune resources that were dropped from the root stack.
23
+ *
24
+ * @default true
25
+ */
26
+ destroyOrphans?: boolean;
27
+ /**
28
+ * A custom state store to use instead of the default file system store.
29
+ */
30
+ stateStore?: StateStore;
31
+ }
32
+
33
+ /**
34
+ * Explicitly finalize the program by deleting any resources that were dropped from the root stack.
35
+ *
36
+ * By default, this will be called when
37
+ */
38
+ export async function alchemize(options?: AlchemizeOptions) {
39
+ if (finalized) {
40
+ return;
41
+ }
42
+ finalized = true;
43
+
44
+ const stage = options?.stage ?? defaultStage;
45
+ const state = options?.stateStore ?? stateStore;
46
+
47
+ await state.init?.();
48
+
49
+ // Track in-progress deletions to avoid duplicate work
50
+ const deletionPromises = new Map<string, Promise<void>>();
51
+
52
+ const priorStates = await state.all(stage);
53
+ const priorIDs = Object.keys(priorStates);
54
+
55
+ const mode = options?.mode ?? "up";
56
+
57
+ if (mode === "up") {
58
+ await Promise.allSettled(
59
+ Array.from(nodes.values())
60
+ .reverse()
61
+ .map((node) =>
62
+ evaluate(node.resource, {
63
+ stage,
64
+ }),
65
+ ),
66
+ );
67
+
68
+ if (options?.destroyOrphans === false) {
69
+ return;
70
+ }
71
+ }
72
+ const aliveIDs = new Set(
73
+ mode === "up"
74
+ ? nodes.keys()
75
+ : // There are no alive resources in destroy mode
76
+ [],
77
+ );
78
+
79
+ const orphanIDs = Array.from(priorIDs).filter((id) => !aliveIDs.has(id));
80
+
81
+ const orphanStates = Object.fromEntries(
82
+ orphanIDs.map((id) => [id, priorStates[id]] as const),
83
+ );
84
+ // compute a map of resourceID -> upstream dependencies (resources that depend on it)
85
+ const orphanGraph: Record<string, Set<string>> = {};
86
+ for (const [orphanID, orphanState] of Object.entries(orphanStates)) {
87
+ orphanGraph[orphanID] ??= new Set();
88
+ for (const dep of orphanState.deps) {
89
+ (orphanGraph[dep] ??= new Set()).add(orphanID);
90
+ }
91
+ }
92
+
93
+ // Start deletion from each orphan that has no dependents (nothing depends on it)
94
+ await Promise.all(
95
+ orphanIDs
96
+ .filter((id) => orphanGraph[id].size === 0)
97
+ .map((id) => deleteOrphan(id)),
98
+ );
99
+
100
+ // Recursively delete orphans in dependency order
101
+ async function deleteOrphan(orphanID: string): Promise<void> {
102
+ // Return existing deletion promise if this orphan is already being deleted
103
+ const existing = deletionPromises.get(orphanID);
104
+ if (existing) {
105
+ return existing;
106
+ }
107
+
108
+ // Create promise immediately to prevent race conditions
109
+ let resolve: () => void;
110
+ let reject: (error: any) => void;
111
+ const promise = new Promise<void>((res, rej) => {
112
+ resolve = res;
113
+ reject = rej;
114
+ });
115
+ deletionPromises.set(orphanID, promise);
116
+
117
+ try {
118
+ const orphanState = orphanStates[orphanID];
119
+
120
+ // First delete this resource
121
+ const providerType = orphanState.provider;
122
+ const provider = providers.get(providerType);
123
+ if (!provider) {
124
+ throw new Error(
125
+ `No provider found for ${providerType}. Did you forget to import it?`,
126
+ );
127
+ }
128
+ await destroy(stage, orphanID, orphanState, provider);
129
+
130
+ // After this resource is deleted, we can delete its dependencies if they're orphans
131
+ if (orphanState.deps.length > 0) {
132
+ await Promise.all(
133
+ orphanState.deps
134
+ .filter((dep) => orphanIDs.includes(dep)) // Only delete if it's an orphan
135
+ .map((dep) => deleteOrphan(dep)),
136
+ );
137
+ }
138
+
139
+ resolve!();
140
+ } catch (error) {
141
+ reject!(error);
142
+ }
143
+
144
+ return promise;
145
+ }
146
+ }
147
+
148
+ if (process.env.ALCHEMY_NO_DEPLOY !== "true") {
149
+ // Listen for signals or events
150
+ process.on("exit", () => alchemize());
151
+ }
package/src/apply.ts ADDED
@@ -0,0 +1,117 @@
1
+ import { defaultStage, stateStore } from "./global";
2
+ import { Output } from "./output";
3
+ import {
4
+ Input,
5
+ Provider,
6
+ type Resource,
7
+ ResourceID,
8
+ isResource,
9
+ } from "./resource";
10
+ import type { StateStore } from "./state";
11
+
12
+ interface ApplyOptions {
13
+ stage?: string;
14
+ stateStore?: StateStore;
15
+ }
16
+
17
+ /**
18
+ * Apply a sub-graph to produce a resource.
19
+ * @param output A sub-graph that produces a resource.
20
+ * @returns The resource properties.
21
+ */
22
+ export async function apply<T>(
23
+ output: T | Output<T>,
24
+ options?: ApplyOptions,
25
+ ): Promise<T> {
26
+ return (await evaluate(output, options)).value;
27
+ }
28
+
29
+ class Evaluated<T> {
30
+ constructor(
31
+ public readonly value: T,
32
+ public readonly deps: string[] = [],
33
+ ) {}
34
+ }
35
+
36
+ const cache = new WeakMap<Resource, Promise<Evaluated<any>>>();
37
+
38
+ export async function evaluate<T>(
39
+ output: T | Output<T>,
40
+ options: ApplyOptions = {},
41
+ ): Promise<Evaluated<T>> {
42
+ const state = options.stateStore ?? stateStore;
43
+ if (isResource(output)) {
44
+ const resource = output;
45
+ const resourceID = resource[ResourceID];
46
+ const evaluated = await Promise.all(
47
+ resource[Input].map((r) => evaluate<any>(r, options)),
48
+ );
49
+
50
+ if (cache.has(resource)) {
51
+ return await cache.get(resource)!;
52
+ }
53
+ let resolve: (value: Evaluated<any>) => void;
54
+ let reject: (reason?: any) => void;
55
+ const promise = new Promise<Evaluated<any>>((res, rej) => {
56
+ resolve = res;
57
+ reject = rej;
58
+ });
59
+ // set this eagerly to avoid double-apply (caused by recursive applies to inputs)
60
+ cache.set(resource, promise);
61
+
62
+ const deps = new Set(evaluated.flatMap((input) => input.deps));
63
+ const inputs = evaluated.map((input) => input.value);
64
+ try {
65
+ const result: T = await resource[Provider].update(
66
+ options.stage ?? defaultStage,
67
+ resource,
68
+ deps,
69
+ inputs as [],
70
+ state,
71
+ );
72
+ resolve!(new Evaluated(result, [resourceID, ...deps]));
73
+ } catch (error) {
74
+ reject!(error);
75
+ }
76
+ return promise;
77
+ } else if (output instanceof Output) {
78
+ const inside = output as unknown as {
79
+ parent: Output<any>;
80
+ fn: (value: any) => T;
81
+ };
82
+ const parent = await evaluate(inside.parent, options);
83
+ const ret = inside.fn(parent.value);
84
+ // the ret may be an Output (e.g. in the flatMap case), so we need to evaluate it and include its deps
85
+ const evaluated = await evaluate(ret, options);
86
+ return new Evaluated<T>(evaluated.value, [
87
+ ...parent.deps,
88
+ ...evaluated.deps,
89
+ ]);
90
+ } else if (Array.isArray(output)) {
91
+ const evaluatedItems = await Promise.all(
92
+ output.map((item) => evaluate(item, options)),
93
+ );
94
+ return new Evaluated(
95
+ evaluatedItems.map((e) => e.value) as unknown as T,
96
+ evaluatedItems.flatMap((e) => e.deps),
97
+ );
98
+ } else if (output && typeof output === "object") {
99
+ const entries = Object.entries(output);
100
+ const evaluatedEntries = await Promise.all(
101
+ entries.map(
102
+ async ([key, value]) => [key, await evaluate(value, options)] as const,
103
+ ),
104
+ );
105
+
106
+ const result = Object.fromEntries(
107
+ evaluatedEntries.map(([key, evaluated]) => [key, evaluated.value]),
108
+ ) as unknown as T;
109
+
110
+ const deps = evaluatedEntries.flatMap(([_, evaluated]) => evaluated.deps);
111
+
112
+ return new Evaluated(result, deps);
113
+ }
114
+
115
+ // Base case: primitive value
116
+ return new Evaluated<T>(output as T);
117
+ }
@@ -0,0 +1,45 @@
1
+ import type { ZodType } from "zod";
2
+ import { type Context, type Provider, Resource } from "../../resource";
3
+
4
+ export function Agent<Type extends string, TInput, TOutput>(
5
+ type: Type,
6
+ {
7
+ description,
8
+ input,
9
+ output,
10
+ }: {
11
+ description: string;
12
+ input: ZodType<TInput>;
13
+ output: ZodType<TOutput>;
14
+ },
15
+ handler: (ctx: Context<TOutput>, props: TInput) => Promise<TOutput | void>,
16
+ ): Provider<Type, [TInput], Awaited<TOutput>> & {
17
+ description: string;
18
+ input: ZodType<TInput>;
19
+ output: ZodType<TOutput>;
20
+ } {
21
+ // @ts-expect-error - we don't care that the props are not statically known, they will be visible outside this scope
22
+ return class Agent extends Resource(
23
+ type,
24
+ async (ctx: Context<TOutput>, props: TInput): Promise<TOutput | void> => {
25
+ // Validate input
26
+ const validatedInput = input.parse(props);
27
+
28
+ // Call handler with validated input
29
+ const result = await handler(ctx, validatedInput);
30
+
31
+ if (ctx.event === "delete") {
32
+ return;
33
+ }
34
+
35
+ // Validate output
36
+ const validatedOutput = output.parse(result);
37
+
38
+ return validatedOutput;
39
+ },
40
+ ) {
41
+ static readonly description = description;
42
+ static readonly input = input;
43
+ static readonly output = output;
44
+ };
45
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @see https://docs.anthropic.com/en/docs/about-claude/models#model-comparison-table
3
+ */
4
+ export const AnthropicModels = [
5
+ // Claude 3.5 Sonnet models
6
+ "claude-3-5-sonnet",
7
+ "claude-3-5-sonnet-20241022",
8
+ "claude-3-5-sonnet-20240620",
9
+
10
+ // Claude 3.5 Haiku models
11
+ "claude-3-5-haiku",
12
+ "claude-3-5-haiku-20241022",
13
+
14
+ // Claude 3 models
15
+ "claude-3-opus",
16
+ "claude-3-opus-20240229",
17
+ "claude-3-sonnet",
18
+ "claude-3-sonnet-20240229",
19
+ "claude-3-haiku",
20
+ "claude-3-haiku-20240307",
21
+ ] as const;
22
+
23
+ export type AnthropicModelId = (typeof AnthropicModels)[number] | (string & {});
24
+
25
+ export function isAnthropicModel(model: string): model is AnthropicModelId {
26
+ return model.startsWith("claude-");
27
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./package";
2
+ export * from "./requirements";
3
+ export * from "./tsconfig";
4
+ export * from "./typescript";
@@ -0,0 +1,22 @@
1
+ import { type AnthropicModelId, isAnthropicModel } from "./anthropic";
2
+ import { type OpenAIChatModelId, isOpenAIModel } from "./openai";
3
+
4
+ export type ModelId = OpenAIChatModelId | AnthropicModelId;
5
+
6
+ export async function resolveModel(modelId: ModelId) {
7
+ if (isOpenAIModel(modelId)) {
8
+ const { openai } = await import("@ai-sdk/openai");
9
+ return {
10
+ type: "openai" as const,
11
+ model: openai(modelId as OpenAIChatModelId),
12
+ };
13
+ }
14
+ if (isAnthropicModel(modelId)) {
15
+ const { anthropic } = await import("@ai-sdk/anthropic");
16
+ return {
17
+ type: "anthropic" as const,
18
+ model: anthropic(modelId as AnthropicModelId),
19
+ };
20
+ }
21
+ throw new Error(`Unsupported model ID: ${modelId}`);
22
+ }
@@ -0,0 +1,44 @@
1
+ export const OpenAIModels = [
2
+ // O1 models
3
+ "o1",
4
+ "o1-2024-12-17",
5
+ "o1-mini",
6
+ "o1-mini-2024-09-12",
7
+ "o1-preview",
8
+ "o1-preview-2024-09-12",
9
+
10
+ // GPT-4O models
11
+ "gpt-4o",
12
+ "gpt-4o-2024-05-13",
13
+ "gpt-4o-2024-08-06",
14
+ "gpt-4o-2024-11-20",
15
+ "gpt-4o-audio-preview",
16
+ "gpt-4o-audio-preview-2024-10-01",
17
+ "gpt-4o-audio-preview-2024-12-17",
18
+ "gpt-4o-mini",
19
+ "gpt-4o-mini-2024-07-18",
20
+
21
+ // GPT-4 models
22
+ "gpt-4-turbo",
23
+ "gpt-4-turbo-2024-04-09",
24
+ "gpt-4-turbo-preview",
25
+ "gpt-4-0125-preview",
26
+ "gpt-4-1106-preview",
27
+ "gpt-4",
28
+ "gpt-4-0613",
29
+
30
+ // GPT-3.5 models
31
+ "gpt-3.5-turbo-0125",
32
+ "gpt-3.5-turbo",
33
+ "gpt-3.5-turbo-1106",
34
+ ] as const;
35
+
36
+ export type OpenAIChatModelId = (typeof OpenAIModels)[number] | (string & {});
37
+
38
+ export function isOpenAIModel(model: string): model is OpenAIChatModelId {
39
+ return (
40
+ model.startsWith("gpt-") ||
41
+ model.startsWith("o1") ||
42
+ model.startsWith("gpt-4o")
43
+ );
44
+ }
@@ -0,0 +1,126 @@
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 type { AnthropicModelId } from "./anthropic";
7
+ import { resolveModel } from "./model";
8
+ import type { OpenAIChatModelId } from "./openai";
9
+
10
+ export type ModelId = OpenAIChatModelId | AnthropicModelId;
11
+
12
+ export const PackageJsonSchema = z.object({
13
+ name: z.string(),
14
+ version: z.string(),
15
+ description: z.string().optional(),
16
+ main: z.string().optional(),
17
+ types: z.string().optional(),
18
+ type: z.literal("module"),
19
+ scripts: z.record(z.string()).optional(),
20
+ peerDependencies: z.record(z.string()).optional(),
21
+ peerDependenciesMeta: z
22
+ .record(z.object({ optional: z.boolean() }))
23
+ .optional(),
24
+ devDependencies: z.record(z.string()).optional(),
25
+ keywords: z.array(z.string()).optional(),
26
+ author: z.string().optional(),
27
+ license: z.string().optional(),
28
+ repository: z
29
+ .object({
30
+ type: z.string(),
31
+ url: z.string(),
32
+ })
33
+ .optional(),
34
+ exports: z.unknown().optional(),
35
+ files: z.array(z.string()).optional(),
36
+ });
37
+
38
+ export const PackageInput = z.object({
39
+ /**
40
+ * List of requirements for the package
41
+ * Can include dependencies, dev dependencies, scripts, etc.
42
+ */
43
+ requirements: z.array(z.string()),
44
+
45
+ /**
46
+ * Name of the package
47
+ */
48
+ name: z.string(),
49
+
50
+ /**
51
+ * The ID of the model to use for generating package configuration
52
+ * @default "gpt-4o"
53
+ */
54
+ model: z.string().optional(),
55
+
56
+ /**
57
+ * Temperature setting for model generation
58
+ * @default 0.3
59
+ */
60
+ temperature: z.number().optional(),
61
+
62
+ /**
63
+ * Path to the package.json file to generate
64
+ */
65
+ path: z.string(),
66
+ });
67
+
68
+ export type PackageInput = z.infer<typeof PackageInput>;
69
+ export type PackageOutput = z.infer<typeof PackageJsonSchema>;
70
+
71
+ export class PackageJson extends Agent(
72
+ "code::Package",
73
+ {
74
+ description:
75
+ "This Agent is responsible for generating package.json configuration based on requirements.",
76
+ input: PackageInput,
77
+ output: PackageJsonSchema,
78
+ },
79
+ async (ctx, input) => {
80
+ if (ctx.event === "delete") {
81
+ await unlink(input.path);
82
+ return;
83
+ }
84
+
85
+ // Get the appropriate model
86
+ const { model } = await resolveModel(input.model ?? "gpt-4o");
87
+
88
+ // Generate the package configuration using generateObject for type safety
89
+ const result = await generateObject({
90
+ model,
91
+ schema: PackageJsonSchema,
92
+ temperature: input.temperature ?? 0.3,
93
+ messages: [
94
+ {
95
+ role: "system",
96
+ content:
97
+ "You are an expert at creating Node.js/TypeScript package configurations. You will generate a package.json configuration based on requirements.",
98
+ },
99
+ {
100
+ role: "user",
101
+ content: `Please generate a package.json configuration based on these requirements:
102
+
103
+ Package name: ${input.name}
104
+
105
+ Requirements:
106
+ ${input.requirements.map((req) => `- ${req}`).join("\n")}
107
+
108
+ Rules:
109
+ 1. All dependencies must be peer dependencies
110
+ 2. Use latest stable versions for all dependencies
111
+ 3. Include standard TypeScript configuration in devDependencies
112
+ 4. Use "bun" as the package manager
113
+ 5. Must use ESM modules (type: "module")`,
114
+ },
115
+ ],
116
+ });
117
+
118
+ // Ensure the directory exists
119
+ await mkdir(dirname(input.path), { recursive: true });
120
+
121
+ // Write the package.json file
122
+ await writeFile(input.path, JSON.stringify(result.object, null, 2));
123
+
124
+ return result.object;
125
+ },
126
+ ) {}