From 6afd6f559e836676280af6d5c9968f5e79e09469 Mon Sep 17 00:00:00 2001 From: owenkephart Date: Tue, 28 Jul 2026 10:19:20 -0700 Subject: [PATCH 1/2] feat(eve): add first-class Photon channel Signed-off-by: owenkephart --- .changeset/friendly-photons-wave.md | 5 + docs/channels/meta.json | 1 + docs/channels/photon.mdx | 39 ++++++ packages/eve/package.json | 6 + .../@photon-ai/chat-adapter-imessage.mjs | 28 ++++ .../eve/scripts/vendor-compiled/index.mjs | 2 + .../eve/src/public/channels/photon/index.ts | 9 ++ .../channels/photon/photonChannel.test.ts | 41 ++++++ .../public/channels/photon/photonChannel.ts | 126 ++++++++++++++++++ pnpm-lock.yaml | 3 + pnpm-workspace.yaml | 1 + 11 files changed, 261 insertions(+) create mode 100644 .changeset/friendly-photons-wave.md create mode 100644 docs/channels/photon.mdx create mode 100644 packages/eve/scripts/vendor-compiled/@photon-ai/chat-adapter-imessage.mjs create mode 100644 packages/eve/src/public/channels/photon/index.ts create mode 100644 packages/eve/src/public/channels/photon/photonChannel.test.ts create mode 100644 packages/eve/src/public/channels/photon/photonChannel.ts diff --git a/.changeset/friendly-photons-wave.md b/.changeset/friendly-photons-wave.md new file mode 100644 index 000000000..1188816e0 --- /dev/null +++ b/.changeset/friendly-photons-wave.md @@ -0,0 +1,5 @@ +--- +"eve": patch +--- + +Add `photonChannel`, a first-class Photon iMessage channel with lazy credentials, Vercel OIDC webhook verification, and automatic eve session routing. diff --git a/docs/channels/meta.json b/docs/channels/meta.json index c0bcc03d2..c8119fbce 100644 --- a/docs/channels/meta.json +++ b/docs/channels/meta.json @@ -4,6 +4,7 @@ "overview", "eve", "slack", + "photon", "discord", "teams", "telegram", diff --git a/docs/channels/photon.mdx b/docs/channels/photon.mdx new file mode 100644 index 000000000..26039307e --- /dev/null +++ b/docs/channels/photon.mdx @@ -0,0 +1,39 @@ +--- +title: Photon +description: Connect an eve agent to iMessage through Photon. +--- + +Use `photonChannel` to receive and reply to iMessages through a Photon project. +With Vercel Connect, the channel resolves credentials lazily when the adapter +first initializes: + +```ts title="agent/channels/photon.ts" +import { connectPhotonCredentials } from "@vercel/connect/eve"; +import { photonChannel } from "eve/channels/photon"; + +export default photonChannel({ + credentials: connectPhotonCredentials(process.env.PHOTON_CONNECTOR_ID!), +}); +``` + +The default webhook route is `/eve/v1/photon`. The channel verifies forwarded +webhooks with same-project Vercel OIDC by default, subscribes the first inbound +message thread, and continues the same eve session for later messages. + +Customize inbound dispatch with `onMessage`. Return `null` to ignore a message: + +```ts +export default photonChannel({ + credentials: connectPhotonCredentials(process.env.PHOTON_CONNECTOR_ID!), + async onMessage(ctx, message) { + if (message.author.isBot) return null; + return { + auth: null, + context: ctx.isSubscribed() ? [] : [`The sender is ${message.author.fullName}.`], + }; + }, +}); +``` + +Set `route` to override the webhook path or `webhookVerifier` to use a different +trusted-forwarder verifier. diff --git a/packages/eve/package.json b/packages/eve/package.json index 4f0b64d73..c5f15a315 100644 --- a/packages/eve/package.json +++ b/packages/eve/package.json @@ -231,6 +231,11 @@ "import": "./dist/src/public/channels/chat-sdk/index.js", "default": "./dist/src/public/channels/chat-sdk/index.js" }, + "./channels/photon": { + "types": "./dist/src/public/channels/photon/index.d.ts", + "import": "./dist/src/public/channels/photon/index.js", + "default": "./dist/src/public/channels/photon/index.js" + }, "./channels/github": { "types": "./dist/src/public/channels/github/index.d.ts", "import": "./dist/src/public/channels/github/index.js", @@ -317,6 +322,7 @@ "@nuxt/kit": "^4.0.0", "@opentelemetry/otlp-transformer": "0.214.0", "@opentelemetry/sdk-trace-base": "catalog:", + "@photon-ai/chat-adapter-imessage": "3.2.0", "@standard-schema/spec": "1.1.0", "@sveltejs/kit": "^2.0.0", "@types/json-schema": "7.0.15", diff --git a/packages/eve/scripts/vendor-compiled/@photon-ai/chat-adapter-imessage.mjs b/packages/eve/scripts/vendor-compiled/@photon-ai/chat-adapter-imessage.mjs new file mode 100644 index 000000000..ea70cda0d --- /dev/null +++ b/packages/eve/scripts/vendor-compiled/@photon-ai/chat-adapter-imessage.mjs @@ -0,0 +1,28 @@ +import { createDeclarationCopier } from "../_shared.mjs"; + +export default { + packageName: "@photon-ai/chat-adapter-imessage", + compiledPath: "@photon-ai/chat-adapter-imessage", + copyDeclarations: createDeclarationCopier({ + rewrites: { + chat: { kind: "vendored", compiledPath: "chat" }, + "@chat-adapter/shared": { + kind: "stub", + stubBaseName: "_chat-adapter-shared", + build: () => "export {};\n", + }, + "@spectrum-ts/core": { + kind: "stub", + stubBaseName: "_spectrum-core", + build: () => + "export type AppUrl = unknown;\nexport type ContentBuilder = unknown;\nexport type SpectrumInstance = unknown;\n", + }, + "@spectrum-ts/imessage": { + kind: "stub", + stubBaseName: "_spectrum-imessage", + build: () => + "export type CustomizedMiniAppInput = unknown;\nexport type IMessageMessageEffect = string;\n", + }, + }, + }), +}; diff --git a/packages/eve/scripts/vendor-compiled/index.mjs b/packages/eve/scripts/vendor-compiled/index.mjs index 2b74bfce7..90a35ac9b 100644 --- a/packages/eve/scripts/vendor-compiled/index.mjs +++ b/packages/eve/scripts/vendor-compiled/index.mjs @@ -14,6 +14,7 @@ import providerUtils from "./@ai-sdk/provider-utils.mjs"; import chatAdapterSlack from "./@chat-adapter/slack.mjs"; import chatAdapterStateMemory from "./@chat-adapter/state-memory.mjs"; import chatAdapterTwilio from "./@chat-adapter/twilio.mjs"; +import photonChatAdapterIMessage from "./@photon-ai/chat-adapter-imessage.mjs"; import opentelemetryApi from "./@opentelemetry/api.mjs"; import opentelemetryOtlpTransformer from "./@opentelemetry/otlp-transformer.mjs"; @@ -69,6 +70,7 @@ export const MODULES = [ opentelemetryApi, opentelemetryOtlpTransformer, otel, + photonChatAdapterIMessage, picocolors, provider, providerUtils, diff --git a/packages/eve/src/public/channels/photon/index.ts b/packages/eve/src/public/channels/photon/index.ts new file mode 100644 index 000000000..c76cbdeef --- /dev/null +++ b/packages/eve/src/public/channels/photon/index.ts @@ -0,0 +1,9 @@ +export { + photonChannel, + type PhotonChannel, + type PhotonChannelConfig, + type PhotonChannelCredentials, + type PhotonInboundMessageContext, + type PhotonInboundResult, + type PhotonInboundResultOrPromise, +} from "#public/channels/photon/photonChannel.js"; diff --git a/packages/eve/src/public/channels/photon/photonChannel.test.ts b/packages/eve/src/public/channels/photon/photonChannel.test.ts new file mode 100644 index 000000000..b200dd4c2 --- /dev/null +++ b/packages/eve/src/public/channels/photon/photonChannel.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it, vi } from "vitest"; + +import { isCompiledChannel } from "#channel/compiled-channel.js"; +import { photonChannel } from "#public/channels/photon/photonChannel.js"; + +function routes(channel: unknown): Array<{ method: string; path: string }> { + if (!isCompiledChannel(channel)) throw new Error("Expected compiled channel."); + return channel.routes.map((route) => ({ method: route.method, path: route.path })); +} + +describe("photonChannel", () => { + it("creates the default Photon webhook without eagerly resolving credentials", () => { + const credentials = vi.fn(async () => ({ + projectId: "project-id", + projectSecret: "project-secret", + })); + + const channel = photonChannel({ credentials }); + + expect(credentials).not.toHaveBeenCalled(); + expect(routes(channel)).toEqual([ + { method: "GET", path: "/eve/v1/photon" }, + { method: "POST", path: "/eve/v1/photon" }, + ]); + }); + + it("supports a custom webhook route", () => { + const channel = photonChannel({ + credentials: async () => ({ + projectId: "project-id", + projectSecret: "project-secret", + }), + route: "/hooks/imessage", + }); + + expect(routes(channel)).toEqual([ + { method: "GET", path: "/hooks/imessage" }, + { method: "POST", path: "/hooks/imessage" }, + ]); + }); +}); diff --git a/packages/eve/src/public/channels/photon/photonChannel.ts b/packages/eve/src/public/channels/photon/photonChannel.ts new file mode 100644 index 000000000..0379f7896 --- /dev/null +++ b/packages/eve/src/public/channels/photon/photonChannel.ts @@ -0,0 +1,126 @@ +import type { SessionAuthContext } from "#channel/types.js"; +import { vercelOidc } from "#public/channels/auth.js"; +import { + chatSdkChannel, + messageToUserContent, + type ChatSdkChannel, + type ChatSdkChannelBridge, + type ChatSdkChannelEvents, +} from "#public/channels/chat-sdk/index.js"; +import type { Message, Thread } from "#compiled/chat/index.js"; +import { createMemoryState } from "#compiled/@chat-adapter/state-memory/index.js"; +import { + createiMessageAdapter, + type iMessageAdapter, + type iMessageCredentialProvider, + type iMessageWebhookVerifier, +} from "#compiled/@photon-ai/chat-adapter-imessage/index.js"; + +/** Photon project credentials used by {@link photonChannel}. */ +export type PhotonChannelCredentials = iMessageCredentialProvider; + +/** Context passed to {@link PhotonChannelConfig.onMessage}. */ +export interface PhotonInboundMessageContext { + /** Returns whether this iMessage thread already has an active eve session. */ + isSubscribed(): Promise; + /** Low-level Chat SDK thread for iMessage-specific operations. */ + readonly thread: Thread; +} + +/** Result of {@link PhotonChannelConfig.onMessage}. Return `null` to drop the message. */ +export type PhotonInboundResult = { + readonly auth: SessionAuthContext | null; + readonly context?: readonly string[]; +} | null; + +/** Sync or async {@link PhotonInboundResult}. */ +export type PhotonInboundResultOrPromise = PhotonInboundResult | Promise; + +/** Configuration for {@link photonChannel}. */ +export interface PhotonChannelConfig { + /** Lazy Photon project credentials, such as `connectPhotonCredentials(...)`. */ + readonly credentials: PhotonChannelCredentials; + /** Per-event overrides for the underlying Chat SDK channel. */ + readonly events?: ChatSdkChannelEvents<{ imessage: iMessageAdapter }>; + /** Inbound message policy. Defaults to dispatching every message with no user auth. */ + readonly onMessage?: ( + ctx: PhotonInboundMessageContext, + message: Message, + ) => PhotonInboundResultOrPromise; + /** Override the default webhook route (`/eve/v1/photon`). */ + readonly route?: string; + /** Display name used by the Chat SDK runtime. Defaults to `"eve"`. */ + readonly userName?: string; + /** Trusted webhook verifier. Defaults to same-project Vercel OIDC. */ + readonly webhookVerifier?: iMessageWebhookVerifier; +} + +/** First-class eve channel backed by Photon iMessage. */ +export interface PhotonChannel extends ChatSdkChannel {} + +/** + * Creates an eve channel for Photon-powered iMessage. + * + * @example + * ```ts + * import { connectPhotonCredentials } from "@vercel/connect/eve"; + * import { photonChannel } from "eve/channels/photon"; + * + * export default photonChannel({ + * credentials: connectPhotonCredentials(process.env.PHOTON_CONNECTOR_ID!), + * }); + * ``` + */ +export function photonChannel(config: PhotonChannelConfig): PhotonChannel { + const imessage = createiMessageAdapter({ + credentials: config.credentials, + webhookVerifier: config.webhookVerifier ?? vercelOidc(), + }); + const bridge = chatSdkChannel({ + adapters: { imessage }, + events: config.events, + routes: { imessage: config.route ?? "/eve/v1/photon" }, + state: createMemoryState(), + streaming: false, + userName: config.userName ?? "eve", + }); + const onMessage = config.onMessage ?? defaultOnMessage; + + bridge.bot.onNewMention(async (thread: Thread, message: Message) => { + await dispatchMessage(bridge, onMessage, thread, message, true); + }); + bridge.bot.onSubscribedMessage(async (thread: Thread, message: Message) => { + await dispatchMessage(bridge, onMessage, thread, message, false); + }); + + return bridge.channel; +} + +async function defaultOnMessage(): Promise { + return { auth: null }; +} + +async function dispatchMessage( + bridge: ChatSdkChannelBridge<{ imessage: iMessageAdapter }>, + onMessage: NonNullable, + thread: Thread, + message: Message, + subscribe: boolean, +): Promise { + const result = await onMessage( + { + isSubscribed: () => thread.isSubscribed(), + thread, + }, + message, + ); + if (result === null) return; + if (subscribe) await thread.subscribe(); + await bridge.send( + { + context: [...(result.context ?? [])], + message: messageToUserContent(message), + }, + { auth: result.auth, thread }, + ); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0dac567a9..f301509d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1088,6 +1088,9 @@ importers: '@opentelemetry/sdk-trace-base': specifier: 'catalog:' version: 2.6.1(@opentelemetry/api@1.9.1) + '@photon-ai/chat-adapter-imessage': + specifier: 3.2.0 + version: 3.2.0(ai@7.0.38(zod@4.4.3))(chat@4.34.0(ai@7.0.38(zod@4.4.3))(supports-color@10.2.2)(zod@4.4.3))(supports-color@10.2.2)(typescript@7.0.2)(zod@4.4.3) '@standard-schema/spec': specifier: 1.1.0 version: 1.1.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 302fa1eb2..e44b1dfe1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -77,6 +77,7 @@ minimumReleaseAgeExclude: - chat - chat-adapter-sendblue - "@ai-sdk/*" + - "@photon-ai/chat-adapter-imessage" - "@rolldown/*" - "@typescript/typescript-*" - "@turbo/*" From 4f279a465065e547d802e386b359d4c67504b8e6 Mon Sep 17 00:00:00 2001 From: owenkephart Date: Tue, 28 Jul 2026 13:24:17 -0700 Subject: [PATCH 2/2] fix(eve): complete Photon channel defaults Signed-off-by: owenkephart --- docs/channels/photon.mdx | 13 +- .../public/channels/photon/photonChannel.ts | 34 ++++-- pnpm-lock.yaml | 112 +++++++++++++++++- 3 files changed, 142 insertions(+), 17 deletions(-) diff --git a/docs/channels/photon.mdx b/docs/channels/photon.mdx index 26039307e..9dec08b18 100644 --- a/docs/channels/photon.mdx +++ b/docs/channels/photon.mdx @@ -17,23 +17,26 @@ export default photonChannel({ ``` The default webhook route is `/eve/v1/photon`. The channel verifies forwarded -webhooks with same-project Vercel OIDC by default, subscribes the first inbound -message thread, and continues the same eve session for later messages. +webhooks with same-project Vercel OIDC by default, marks accepted messages as +read, subscribes the first inbound message thread, and continues the same eve +session for later messages. Customize inbound dispatch with `onMessage`. Return `null` to ignore a message: ```ts export default photonChannel({ credentials: connectPhotonCredentials(process.env.PHOTON_CONNECTOR_ID!), - async onMessage(ctx, message) { + onMessage(_ctx, message) { if (message.author.isBot) return null; return { auth: null, - context: ctx.isSubscribed() ? [] : [`The sender is ${message.author.fullName}.`], + context: [`The sender is ${message.author.fullName}.`], }; }, }); ``` Set `route` to override the webhook path or `webhookVerifier` to use a different -trusted-forwarder verifier. +trusted-forwarder verifier. For direct Photon webhooks, pass `webhookSecret` or +set `IMESSAGE_WEBHOOK_SECRET`; the signing secret takes precedence over the +default OIDC verifier. diff --git a/packages/eve/src/public/channels/photon/photonChannel.ts b/packages/eve/src/public/channels/photon/photonChannel.ts index 0379f7896..0e9db2e90 100644 --- a/packages/eve/src/public/channels/photon/photonChannel.ts +++ b/packages/eve/src/public/channels/photon/photonChannel.ts @@ -21,8 +21,6 @@ export type PhotonChannelCredentials = iMessageCredentialProvider; /** Context passed to {@link PhotonChannelConfig.onMessage}. */ export interface PhotonInboundMessageContext { - /** Returns whether this iMessage thread already has an active eve session. */ - isSubscribed(): Promise; /** Low-level Chat SDK thread for iMessage-specific operations. */ readonly thread: Thread; } @@ -51,7 +49,9 @@ export interface PhotonChannelConfig { readonly route?: string; /** Display name used by the Chat SDK runtime. Defaults to `"eve"`. */ readonly userName?: string; - /** Trusted webhook verifier. Defaults to same-project Vercel OIDC. */ + /** Photon webhook signing secret. Falls back to `IMESSAGE_WEBHOOK_SECRET`. */ + readonly webhookSecret?: string; + /** Trusted webhook verifier. Takes precedence over `webhookSecret`. */ readonly webhookVerifier?: iMessageWebhookVerifier; } @@ -72,9 +72,14 @@ export interface PhotonChannel extends ChatSdkChannel {} * ``` */ export function photonChannel(config: PhotonChannelConfig): PhotonChannel { + const webhookSecret = config.webhookSecret ?? process.env.IMESSAGE_WEBHOOK_SECRET; const imessage = createiMessageAdapter({ credentials: config.credentials, - webhookVerifier: config.webhookVerifier ?? vercelOidc(), + ...(config.webhookVerifier + ? { webhookVerifier: config.webhookVerifier } + : webhookSecret + ? { webhookSecret } + : { webhookVerifier: vercelOidc() }), }); const bridge = chatSdkChannel({ adapters: { imessage }, @@ -107,14 +112,9 @@ async function dispatchMessage( message: Message, subscribe: boolean, ): Promise { - const result = await onMessage( - { - isSubscribed: () => thread.isSubscribed(), - thread, - }, - message, - ); + const result = await onMessage({ thread }, message); if (result === null) return; + await markReadBestEffort(bridge.bot.getAdapter("imessage"), thread, message); if (subscribe) await thread.subscribe(); await bridge.send( { @@ -124,3 +124,15 @@ async function dispatchMessage( { auth: result.auth, thread }, ); } + +async function markReadBestEffort( + adapter: iMessageAdapter, + thread: Thread, + message: Message, +): Promise { + try { + await adapter.markRead(thread.id, message.id); + } catch { + // A read receipt should never prevent the user's message from reaching eve. + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f301509d0..91c80a0fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1207,7 +1207,7 @@ importers: version: 7.8.4 shadcn: specifier: 4.16.0 - version: 4.16.0(supports-color@10.2.2)(typescript@6.0.3) + version: 4.16.0(supports-color@10.2.2)(typescript@7.0.2) svelte: specifier: ^5.0.0 version: 5.56.1(@typescript-eslint/types@8.59.4) @@ -18037,6 +18037,20 @@ snapshots: - typescript - zod + '@photon-ai/chat-adapter-imessage@3.2.0(ai@7.0.38(zod@4.4.3))(chat@4.34.0(ai@7.0.38(zod@4.4.3))(supports-color@10.2.2)(zod@4.4.3))(supports-color@10.2.2)(typescript@7.0.2)(zod@4.4.3)': + dependencies: + '@chat-adapter/shared': 4.34.0(ai@7.0.38(zod@4.4.3))(supports-color@10.2.2)(zod@4.4.3) + '@spectrum-ts/core': 10.0.0(supports-color@10.2.2)(typescript@7.0.2) + '@spectrum-ts/imessage': 10.0.0(@spectrum-ts/core@10.0.0(supports-color@10.2.2)(typescript@7.0.2))(supports-color@10.2.2)(typescript@7.0.2) + chat: 4.34.0(ai@7.0.38(zod@4.4.3))(supports-color@10.2.2)(zod@4.4.3) + mime-types: 3.0.2 + transitivePeerDependencies: + - ai + - ffmpeg-static + - supports-color + - typescript + - zod + '@photon-ai/otel@3.3.0(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@opentelemetry/api': 1.9.1 @@ -18058,6 +18072,27 @@ snapshots: transitivePeerDependencies: - supports-color + '@photon-ai/otel@3.3.0(supports-color@10.2.2)(typescript@7.0.2)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.218.0 + '@opentelemetry/context-async-hooks': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-logs-otlp-http': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-metrics-otlp-http': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-http': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.43.0 + typescript: 7.0.2 + optionalDependencies: + '@opentelemetry/instrumentation': 0.219.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-undici': 0.29.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + '@photon-ai/proto@0.2.4': dependencies: '@bufbuild/protobuf': 2.13.0 @@ -19445,6 +19480,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@spectrum-ts/core@10.0.0(supports-color@10.2.2)(typescript@7.0.2)': + dependencies: + '@photon-ai/otel': 3.3.0(supports-color@10.2.2)(typescript@7.0.2) + '@photon-ai/proto': 0.2.4 + '@repeaterjs/repeater': 3.1.0 + marked: 18.0.7 + mime-types: 3.0.2 + open-graph-scraper: 6.12.0 + typescript: 7.0.2 + vcf: 2.1.2 + zod: 4.4.3 + transitivePeerDependencies: + - supports-color + '@spectrum-ts/imessage@10.0.0(@spectrum-ts/core@10.0.0(supports-color@10.2.2)(typescript@6.0.3))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@photon-ai/advanced-imessage': 1.0.0 @@ -19457,6 +19506,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@spectrum-ts/imessage@10.0.0(@spectrum-ts/core@10.0.0(supports-color@10.2.2)(typescript@7.0.2))(supports-color@10.2.2)(typescript@7.0.2)': + dependencies: + '@photon-ai/advanced-imessage': 1.0.0 + '@photon-ai/otel': 3.3.0(supports-color@10.2.2)(typescript@7.0.2) + '@spectrum-ts/core': 10.0.0(supports-color@10.2.2)(typescript@7.0.2) + lru-cache: 11.5.2 + marked: 18.0.7 + typescript: 7.0.2 + zod: 4.4.3 + transitivePeerDependencies: + - supports-color + '@speed-highlight/core@1.2.17': {} '@stablelib/base64@1.0.1': {} @@ -22102,6 +22163,15 @@ snapshots: optionalDependencies: typescript: 6.0.3 + cosmiconfig@9.0.2(typescript@7.0.2): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + optionalDependencies: + typescript: 7.0.2 + crc-32@1.2.2: {} crc32-stream@6.0.0: @@ -28703,6 +28773,46 @@ snapshots: - supports-color - typescript + shadcn@4.16.0(supports-color@10.2.2)(typescript@7.0.2): + dependencies: + '@babel/core': 7.29.0(supports-color@10.2.2) + '@babel/parser': 7.29.7 + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.0(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.0(supports-color@10.2.2))(supports-color@10.2.2) + '@dotenvx/dotenvx': 1.75.1 + '@modelcontextprotocol/sdk': 1.29.0(supports-color@10.2.2)(zod@3.25.76) + '@types/validate-npm-package-name': 4.0.2 + browserslist: 4.28.6 + commander: 14.0.3 + cosmiconfig: 9.0.2(typescript@7.0.2) + dedent: 1.7.2 + deepmerge: 4.3.1 + diff: 8.0.4 + execa: 9.6.1 + fast-glob: 3.3.3 + fs-extra: 11.3.6 + fuzzysort: 3.1.0 + kleur: 4.1.5 + open: 11.0.0 + ora: 8.2.0 + postcss: 8.5.15 + postcss-selector-parser: 7.1.4 + prompts: 2.4.2 + recast: 0.23.12 + stringify-object: 5.0.0 + tailwind-merge: 3.6.0 + ts-morph: 26.0.0 + tsconfig-paths: 4.2.0 + undici: 7.28.0 + validate-npm-package-name: 7.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - '@cfworker/json-schema' + - babel-plugin-macros + - supports-color + - typescript + sharp@0.34.5: dependencies: '@img/colour': 1.1.0