From 9add1fe8f60add5522a3b51b0ef3ff13782d63a7 Mon Sep 17 00:00:00 2001 From: sadfun Date: Sun, 23 Aug 2026 16:29:32 +0200 Subject: [PATCH 1/2] Migrate to Bun 1.4 and MIT --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- CHANGELOG.md | 3 + Dockerfile | 2 +- LICENSE.md | 105 +++------------------------------- README.md | 4 +- bun.lock | 2 +- package.json | 5 +- 8 files changed, 20 insertions(+), 105 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69b54d7..2cb80e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 with: - bun-version: latest + bun-version: 1.4.0 - run: bun install --frozen-lockfile - run: bun run check - run: bun run compile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3106354..d932fc4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 with: - bun-version: latest + bun-version: 1.4.0 - name: Verify tag and package version run: | package_version=$(bun -p 'require("./package.json").version') diff --git a/CHANGELOG.md b/CHANGELOG.md index c54ed55..5fb2af9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ All notable changes to Wirebot are documented in this file. ### Changed +- The project now requires Bun 1.4.0 across local development, CI, release builds, and Docker, + including Bun's v2 text lockfile format. +- Wirebot is now distributed under the MIT License. - Codex itself is now the config validator: the Mini App previews check structure only, and semantic errors surface from the version-checked `config/batchWrite` at save time. The hand-maintained mirror of Codex's config rules is gone, and the feature list comes entirely diff --git a/Dockerfile b/Dockerfile index a167909..7af6d05 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ # Build stage: compiles the Mini App assets, the wirebot executable, and # downloads the pinned Codex CLI for the target architecture. Runs on the # build platform and cross-compiles, so no emulation is needed here. -FROM --platform=$BUILDPLATFORM oven/bun:1.3 AS build +FROM --platform=$BUILDPLATFORM oven/bun:1.4.0 AS build ARG TARGETARCH WORKDIR /app diff --git a/LICENSE.md b/LICENSE.md index 69aa537..b69aad3 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,102 +1,13 @@ -# Functional Source License, Version 1.1, MIT Future License +# MIT License -## Abbreviation +Copyright (c) 2026 sadfun -FSL-1.1-MIT - -## Notice - -Copyright 2026 sadfun - -## Terms and Conditions - -### Licensor ("We") - -The party offering the Software under these Terms and Conditions. - -### The Software - -The "Software" is each version of the software that we make available under -these Terms and Conditions, as indicated by our inclusion of these Terms and -Conditions with the Software. - -### License Grant - -Subject to your compliance with this License Grant and the Patents, -Redistribution and Trademark clauses below, we hereby grant you the right to -use, copy, modify, create derivative works, publicly perform, publicly display -and redistribute the Software for any Permitted Purpose identified below. - -### Permitted Purpose - -A Permitted Purpose is any purpose other than a Competing Use. A Competing Use -means making the Software available to others in a commercial product or -service that: - -1. substitutes for the Software; - -2. substitutes for any other product or service we offer using the Software - that exists as of the date we make the Software available; or - -3. offers the same or substantially similar functionality as the Software. - -Permitted Purposes specifically include using the Software: - -1. for your internal use and access; - -2. for non-commercial education; - -3. for non-commercial research; and - -4. in connection with professional services that you provide to a licensee - using the Software in accordance with these Terms and Conditions. - -### Patents - -To the extent your use for a Permitted Purpose would necessarily infringe our -patents, the license grant above includes a license under our patents. If you -make a claim against any party that the Software infringes or contributes to -the infringement of any patent, then your patent license to the Software ends -immediately. - -### Redistribution - -The Terms and Conditions apply to all copies, modifications and derivatives of -the Software. - -If you redistribute any copies, modifications or derivatives of the Software, -you must include a copy of or a link to these Terms and Conditions and not -remove any copyright notices provided in or with the Software. - -### Disclaimer - -THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR -PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT. - -IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE -SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, -EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE. - -### Trademarks - -Except for displaying the License Details and identifying us as the origin of -the Software, you have no right under these Terms and Conditions to use our -trademarks, trade names, service marks or product names. - -## Grant of Future License - -We hereby irrevocably grant you an additional license to use the Software under -the MIT license that is effective on the second anniversary of the date we make -the Software available. On or after that date, you may use the Software under -the MIT license, in which case the following will apply: - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. diff --git a/README.md b/README.md index 309fc5c..95e8453 100644 --- a/README.md +++ b/README.md @@ -222,7 +222,7 @@ The runtime card in the Mini App shows the current outcome and offers **Apply ch ## Source development -Requirements: [Bun](https://bun.com) 1.3 or newer. +Requirements: [Bun](https://bun.com) 1.4 or newer. ```sh git clone https://github.com/sadfun/wirebot.git @@ -278,4 +278,4 @@ Removing the `wirebot-data` volume permanently deletes conversations, Codex conf ## License -Wirebot is released under the [Functional Source License 1.1 with an MIT future license](./LICENSE.md). Each published version becomes available under MIT two years after its release date. +Wirebot is released under the [MIT License](./LICENSE.md). diff --git a/bun.lock b/bun.lock index 7ac4f48..73bc43f 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,5 @@ { - "lockfileVersion": 1, + "lockfileVersion": 2, "configVersion": 1, "workspaces": { "": { diff --git a/package.json b/package.json index 86d55f2..5fcb010 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,9 @@ "version": "0.2.1", "private": true, "type": "module", + "packageManager": "bun@1.4.0", "description": "Wirebot — a Telegram, Slack, and Discord bridge for the OpenAI Codex app-server.", - "license": "FSL-1.1-MIT", + "license": "MIT", "homepage": "https://github.com/sadfun/wirebot#readme", "bugs": { "url": "https://github.com/sadfun/wirebot/issues" @@ -14,7 +15,7 @@ "url": "git+https://github.com/sadfun/wirebot.git" }, "engines": { - "bun": ">=1.3.0" + "bun": ">=1.4.0" }, "scripts": { "build": "bun scripts/build-assets.ts && tailwindcss -i ./src/miniapp/styles.css -o ./dist/miniapp/public/app.css --minify", From 69bf261ad7ad155f8b4e609931bd2c1403fe8952 Mon Sep 17 00:00:00 2001 From: sadfun Date: Sun, 23 Aug 2026 16:56:33 +0200 Subject: [PATCH 2/2] Use Bun native Markdown and archive APIs --- CHANGELOG.md | 2 ++ bun.lock | 3 --- package.json | 1 - scripts/bun-globals.d.ts | 27 +++++++++++++++++++++- src/codex/output-files.ts | 17 +++++++++++--- src/codex/toolchain.ts | 8 ++----- test/output-files.test.ts | 48 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 test/output-files.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fb2af9..6f05ea6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to Wirebot are documented in this file. - The project now requires Bun 1.4.0 across local development, CI, release builds, and Docker, including Bun's v2 text lockfile format. +- Bun's native Markdown and archive APIs now replace the `marked` dependency and the external + `tar` process previously used to install Codex packages. - Wirebot is now distributed under the MIT License. - Codex itself is now the config validator: the Mini App previews check structure only, and semantic errors surface from the version-checked `config/batchWrite` at save time. The diff --git a/bun.lock b/bun.lock index 73bc43f..06b7c03 100644 --- a/bun.lock +++ b/bun.lock @@ -13,7 +13,6 @@ "discord.js": "^14.27.0", "grammy": "^1.45.1", "lucide-react": "^0.468.0", - "marked": "^18.0.6", "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^10.1.0", @@ -404,8 +403,6 @@ "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], - "marked": ["marked@18.0.9", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-/Sa4qiiHZxf0/FQdBBowr9q4r10krCwMvpK48FUBdXdUXScDxiQGR9zCPrFgRVR5LU3iySOiIjy09ZQvADir1w=="], - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], diff --git a/package.json b/package.json index 5fcb010..f796a04 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "discord.js": "^14.27.0", "grammy": "^1.45.1", "lucide-react": "^0.468.0", - "marked": "^18.0.6", "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^10.1.0", diff --git a/scripts/bun-globals.d.ts b/scripts/bun-globals.d.ts index 7c45788..61eb5c5 100644 --- a/scripts/bun-globals.d.ts +++ b/scripts/bun-globals.d.ts @@ -1,5 +1,5 @@ /** - * Minimal declaration of the Bun runtime surface the build scripts use. + * Minimal declaration of the Bun runtime surface the project uses. * The full @types/bun package conflicts with @types/node under * skipLibCheck: false, so only what is needed is declared here. */ @@ -16,4 +16,29 @@ declare const Bun: { success: boolean; logs: readonly { message: string }[]; }>; + markdown: { + render( + input: string, + callbacks: Readonly<{ + link?: ( + children: string, + meta: Readonly<{ href: string; title?: string }>, + ) => string | null | undefined; + image?: ( + children: string, + meta: Readonly<{ src: string; title?: string }>, + ) => string | null | undefined; + }>, + ): string; + }; + Archive: { + new ( + data: Blob | ArrayBufferLike | Uint8Array, + ): { + extract( + path: string, + options?: Readonly<{ glob?: string | readonly string[] }>, + ): Promise; + }; + }; }; diff --git a/src/codex/output-files.ts b/src/codex/output-files.ts index d38f871..ac8df28 100644 --- a/src/codex/output-files.ts +++ b/src/codex/output-files.ts @@ -2,7 +2,6 @@ import { createWriteStream } from "node:fs"; import { mkdir, open, realpath, rm, stat } from "node:fs/promises"; import { basename, isAbsolute, join, resolve } from "node:path"; import { pipeline } from "node:stream/promises"; -import { lexer, walkTokens } from "marked"; import type { OutboundAttachment } from "../core/channel.js"; import type { ThreadItem } from "../generated/codex/v2/ThreadItem.js"; import { isPathWithin } from "../shared/fs.js"; @@ -30,12 +29,24 @@ export function unavailableAttachmentsWarning( function extractMarkdownFileTargets(text: string): readonly string[] { const targets: string[] = []; - walkTokens(lexer(text), (token) => { - if (token.type === "link" || token.type === "image") targets.push(token.href); + Bun.markdown.render(text, { + link: (children, { href }) => { + targets.push(unescapeMarkdownTarget(href)); + return children; + }, + image: (children, { src }) => { + targets.push(unescapeMarkdownTarget(src)); + return children; + }, }); return targets; } +/** Bun returns CommonMark backslash escapes verbatim in link destinations. */ +function unescapeMarkdownTarget(target: string): string { + return target.replaceAll(/\\([!-/:-@[-`{-~])/gu, "$1"); +} + export async function resolveOutboundAttachments( workspace: string, generatedImagesDirectory: string, diff --git a/src/codex/toolchain.ts b/src/codex/toolchain.ts index 561bfad..923d0b8 100644 --- a/src/codex/toolchain.ts +++ b/src/codex/toolchain.ts @@ -1,5 +1,5 @@ import { createHash } from "node:crypto"; -import { access, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises"; +import { access, mkdir, readFile, rename, rm } from "node:fs/promises"; import { join } from "node:path"; import { z } from "zod"; import pinnedCodexVersionText from "../../codex.version" with { type: "text" }; @@ -7,7 +7,6 @@ import { delay } from "../shared/async.js"; import { BridgeError, errorMessage } from "../shared/errors.js"; import { atomicWriteJson, ensureDirectory } from "../shared/fs.js"; import type { Logger } from "../shared/logger.js"; -import { runCommand } from "../shared/process.js"; const versionSchema = z.string().regex(/^[0-9A-Za-z][0-9A-Za-z.+-]*$/); @@ -192,10 +191,7 @@ export class CodexToolchainManager { const stageDirectory = `${versionDirectory}.stage`; await rm(stageDirectory, { recursive: true, force: true }); - await ensureDirectory(stageDirectory); - const archivePath = join(stageDirectory, "codex.tgz"); - await writeFile(archivePath, payload); - await runCommand("tar", ["-xzf", "codex.tgz", "package/vendor"], { cwd: stageDirectory }); + await new Bun.Archive(payload).extract(stageDirectory, { glob: "package/vendor/**" }); await rm(versionDirectory, { recursive: true, force: true }); await rename(join(stageDirectory, "package"), versionDirectory); await rm(stageDirectory, { recursive: true, force: true }); diff --git a/test/output-files.test.ts b/test/output-files.test.ts new file mode 100644 index 0000000..8a1939f --- /dev/null +++ b/test/output-files.test.ts @@ -0,0 +1,48 @@ +import { afterEach, describe, expect, it } from "bun:test"; +import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { resolveOutboundAttachments } from "../src/codex/output-files.js"; + +const temporaryDirectories: string[] = []; + +afterEach(async () => { + await Promise.all( + temporaryDirectories.splice(0).map((directory) => rm(directory, { recursive: true })), + ); +}); + +describe("resolveOutboundAttachments", () => { + it("discovers local Markdown links and images with Bun.markdown", async () => { + const root = await mkdtemp(join(tmpdir(), "wirebot-output-files-")); + temporaryDirectories.push(root); + const workspace = join(root, "workspace"); + await mkdir(join(workspace, "out"), { recursive: true }); + await mkdir(join(workspace, "docs"), { recursive: true }); + await Promise.all([ + writeFile(join(workspace, "report.pdf"), "report"), + writeFile(join(workspace, "out", "chart.png"), "chart"), + writeFile(join(workspace, "docs", "a(1).pdf"), "escaped"), + ]); + + const resolution = await resolveOutboundAttachments( + workspace, + join(root, "generated"), + join(root, "staging"), + [ + "[report](report.pdf)", + "![plot](out/chart.png)", + "[escaped](docs/a\\(1\\).pdf)", + "[remote](https://example.com/file.txt)", + ].join("\n"), + [], + ); + + expect(resolution.attachments.map(({ filename }) => filename)).toEqual([ + "report.pdf", + "chart.png", + "a(1).pdf", + ]); + expect(resolution.unavailable).toEqual([]); + }); +});