diff --git a/bun.lock b/bun.lock index 70c3c2998..509151589 100644 --- a/bun.lock +++ b/bun.lock @@ -143,6 +143,7 @@ "@standard-schema/spec": "1.0.0", "@zip.js/zip.js": "2.7.62", "ai": "catalog:", + "chokidar": "4.0.3", "decimal.js": "10.5.0", "diff": "8.0.2", "gray-matter": "4.0.3", diff --git a/cloud/function/sst-env.d.ts b/cloud/function/sst-env.d.ts index 09c60c7c2..270d0bcf7 100644 --- a/cloud/function/sst-env.d.ts +++ b/cloud/function/sst-env.d.ts @@ -30,6 +30,10 @@ declare module "sst" { "type": "sst.sst.Linkable" "username": string } + "Desktop": { + "type": "sst.cloudflare.StaticSite" + "url": string + } "FIREWORKS_API_KEY": { "type": "sst.sst.Secret" "value": string diff --git a/cloud/resource/sst-env.d.ts b/cloud/resource/sst-env.d.ts index 09c60c7c2..270d0bcf7 100644 --- a/cloud/resource/sst-env.d.ts +++ b/cloud/resource/sst-env.d.ts @@ -30,6 +30,10 @@ declare module "sst" { "type": "sst.sst.Linkable" "username": string } + "Desktop": { + "type": "sst.cloudflare.StaticSite" + "url": string + } "FIREWORKS_API_KEY": { "type": "sst.sst.Secret" "value": string diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index 09c60c7c2..270d0bcf7 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -30,6 +30,10 @@ declare module "sst" { "type": "sst.sst.Linkable" "username": string } + "Desktop": { + "type": "sst.cloudflare.StaticSite" + "url": string + } "FIREWORKS_API_KEY": { "type": "sst.sst.Secret" "value": string diff --git a/packages/opencode/package.json b/packages/opencode/package.json index dada6368c..56a869334 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -37,6 +37,7 @@ "@standard-schema/spec": "1.0.0", "@zip.js/zip.js": "2.7.62", "ai": "catalog:", + "chokidar": "4.0.3", "decimal.js": "10.5.0", "diff": "8.0.2", "gray-matter": "4.0.3", diff --git a/packages/opencode/src/file/watch.ts b/packages/opencode/src/file/watch.ts index 526b29c9e..319dbd949 100644 --- a/packages/opencode/src/file/watch.ts +++ b/packages/opencode/src/file/watch.ts @@ -1,13 +1,12 @@ import z from "zod/v4" import { Bus } from "../bus" -import fs from "fs" -import { Log } from "../util/log" +import path from "path" +import chokidar from "chokidar" +import ignore from "ignore" import { Flag } from "../flag/flag" import { Instance } from "../project/instance" export namespace FileWatcher { - const log = Log.create({ service: "file.watcher" }) - export const Event = { Updated: Bus.event( "file.watcher.updated", @@ -18,21 +17,39 @@ export namespace FileWatcher { ), } const state = Instance.state( - () => { + async () => { if (Instance.project.vcs !== "git") return {} - try { - const watcher = fs.watch(Instance.directory, { recursive: true }, (event, file) => { - log.info("change", { file, event }) - if (!file) return - Bus.publish(Event.Updated, { - file, - event, - }) - }) - return { watcher } - } catch { - return {} + const ig = ignore() + const glob = new Bun.Glob("**/.gitignore") + for await (const gitignorePath of glob.scan({ + cwd: Instance.directory, + absolute: true, + onlyFiles: true, + dot: true, + })) { + const relativePath = path.relative(Instance.directory, gitignorePath) + const dir = path.dirname(relativePath) + const prefix = dir === "." ? "" : dir + "/" + const content = await Bun.file(gitignorePath).text() + const prefixed = content + .split("\n") + .map((line) => (line ? prefix + line : line)) + .join("\n") + ig.add(prefixed) } + const watcher = chokidar.watch(Instance.directory, { + ignored: (filePath) => ig.ignores(filePath), + }) + watcher.on("change", (file) => { + Bus.publish(Event.Updated, { file, event: "change" }) + }) + watcher.on("add", (file) => { + Bus.publish(Event.Updated, { file, event: "change" }) + }) + watcher.on("unlink", (file) => { + Bus.publish(Event.Updated, { file, event: "change" }) + }) + return { watcher } }, async (state) => { state.watcher?.close() @@ -40,7 +57,7 @@ export namespace FileWatcher { ) export function init() { - if (Flag.OPENCODE_DISABLE_WATCHER || true) return + if (!Flag.OPENCODE_EXPERIMENTAL_WATCHER) return state() } } diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts index 418f32283..a05379073 100644 --- a/packages/opencode/src/flag/flag.ts +++ b/packages/opencode/src/flag/flag.ts @@ -1,6 +1,5 @@ export namespace Flag { export const OPENCODE_AUTO_SHARE = truthy("OPENCODE_AUTO_SHARE") - export const OPENCODE_DISABLE_WATCHER = truthy("OPENCODE_DISABLE_WATCHER") export const OPENCODE_CONFIG = process.env["OPENCODE_CONFIG"] export const OPENCODE_CONFIG_CONTENT = process.env["OPENCODE_CONFIG_CONTENT"] export const OPENCODE_DISABLE_AUTOUPDATE = truthy("OPENCODE_DISABLE_AUTOUPDATE") @@ -10,6 +9,9 @@ export namespace Flag { export const OPENCODE_ENABLE_EXPERIMENTAL_MODELS = truthy("OPENCODE_ENABLE_EXPERIMENTAL_MODELS") export const OPENCODE_DISABLE_AUTOCOMPACT = truthy("OPENCODE_DISABLE_AUTOCOMPACT") + // Experimental + export const OPENCODE_EXPERIMENTAL_WATCHER = truthy("OPENCODE_EXPERIMENTAL_WATCHER") + function truthy(key: string) { const value = process.env[key]?.toLowerCase() return value === "true" || value === "1" diff --git a/sst-env.d.ts b/sst-env.d.ts index e45b4e5ea..0cae618c5 100644 --- a/sst-env.d.ts +++ b/sst-env.d.ts @@ -44,6 +44,10 @@ declare module "sst" { "type": "sst.sst.Linkable" "username": string } + "Desktop": { + "type": "sst.cloudflare.StaticSite" + "url": string + } "FIREWORKS_API_KEY": { "type": "sst.sst.Secret" "value": string