mirror of
https://github.com/block/goose.git
synced 2026-07-03 14:15:10 +02:00
feat: combine TUI UX from alexhancock/tui-goodness with publishing config from jackamadeo/package-tui (#7683)
Co-authored-by: Jack Amadeo <jamadeo@squareup.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
*/bin/
|
||||
@@ -0,0 +1,35 @@
|
||||
# Native Binary Packages
|
||||
|
||||
This directory contains the npm package scaffolding for distributing the
|
||||
`goose-acp-server` Rust binary as platform-specific npm packages.
|
||||
|
||||
## Packages
|
||||
|
||||
| Package | Platform |
|
||||
|---------|----------|
|
||||
| `@block/goose-acp-server-darwin-arm64` | macOS Apple Silicon |
|
||||
| `@block/goose-acp-server-darwin-x64` | macOS Intel |
|
||||
| `@block/goose-acp-server-linux-arm64` | Linux ARM64 |
|
||||
| `@block/goose-acp-server-linux-x64` | Linux x64 |
|
||||
| `@block/goose-acp-server-win32-x64` | Windows x64 |
|
||||
|
||||
## Building
|
||||
|
||||
From the repository root:
|
||||
|
||||
```bash
|
||||
# Build for all platforms (requires cross-compilation toolchains)
|
||||
./ui/text/scripts/build-native-packages.sh
|
||||
|
||||
# Build for a single platform
|
||||
./ui/text/scripts/build-native-packages.sh darwin-arm64
|
||||
```
|
||||
|
||||
The built binaries are placed into `npm/goose-acp-server-{platform}/bin/`.
|
||||
These directories are git-ignored.
|
||||
|
||||
## Publishing
|
||||
|
||||
```bash
|
||||
./ui/text/scripts/publish.sh
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@block/goose-acp-server-darwin-arm64",
|
||||
"version": "0.1.0",
|
||||
"description": "Goose ACP server binary for macOS ARM64",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/block/goose.git"
|
||||
},
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"files": [
|
||||
"bin/goose-acp-server"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@block/goose-acp-server-darwin-x64",
|
||||
"version": "0.1.0",
|
||||
"description": "Goose ACP server binary for macOS x64",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/block/goose.git"
|
||||
},
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"files": [
|
||||
"bin/goose-acp-server"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@block/goose-acp-server-linux-arm64",
|
||||
"version": "0.1.0",
|
||||
"description": "Goose ACP server binary for Linux ARM64",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/block/goose.git"
|
||||
},
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"files": [
|
||||
"bin/goose-acp-server"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@block/goose-acp-server-linux-x64",
|
||||
"version": "0.1.0",
|
||||
"description": "Goose ACP server binary for Linux x64",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/block/goose.git"
|
||||
},
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"files": [
|
||||
"bin/goose-acp-server"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@block/goose-acp-server-win32-x64",
|
||||
"version": "0.1.0",
|
||||
"description": "Goose ACP server binary for Windows x64",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/block/goose.git"
|
||||
},
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"files": [
|
||||
"bin/goose-acp-server.exe"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
dist/
|
||||
node_modules/
|
||||
+7
-4
@@ -1,11 +1,14 @@
|
||||
{
|
||||
"name": "goose-acp-types",
|
||||
"name": "@block/goose-acp",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"generate": "tsx generate-schema.ts",
|
||||
"lint": "tsc --noEmit",
|
||||
"format": "prettier --write src/"
|
||||
|
||||
@@ -27,12 +27,17 @@ import {
|
||||
type SetSessionModelResponse,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
import { GooseExtClient } from "./generated/client.gen.js";
|
||||
import { createHttpStream } from "./http-stream.js";
|
||||
|
||||
export class GooseClient {
|
||||
private conn: ClientSideConnection;
|
||||
private ext: GooseExtClient;
|
||||
|
||||
constructor(toClient: () => Client, stream: Stream) {
|
||||
constructor(toClient: () => Client, streamOrUrl: Stream | string) {
|
||||
const stream =
|
||||
typeof streamOrUrl === "string"
|
||||
? createHttpStream(streamOrUrl)
|
||||
: streamOrUrl;
|
||||
this.conn = new ClientSideConnection(toClient, stream);
|
||||
this.ext = new GooseExtClient(this.conn);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ export function createHttpStream(serverUrl: string): Stream {
|
||||
const msg = JSON.parse(line.slice(6)) as AnyMessage;
|
||||
pushMessage(msg);
|
||||
} catch {
|
||||
// ignore malformed JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,10 +52,6 @@ export function createHttpStream(serverUrl: string): Stream {
|
||||
}
|
||||
}
|
||||
|
||||
// POST initialize (no session header) opens a long-lived SSE stream that receives
|
||||
// ALL subsequent responses and notifications. Later POSTs with the session header
|
||||
// are fire-and-forget for requests (responses arrive on the first stream) or
|
||||
// return 202 immediately for notifications/responses.
|
||||
let isFirstRequest = true;
|
||||
|
||||
const readable = new ReadableStream<AnyMessage>({
|
||||
@@ -69,7 +66,10 @@ export function createHttpStream(serverUrl: string): Stream {
|
||||
const writable = new WritableStream<AnyMessage>({
|
||||
async write(msg) {
|
||||
const isRequest =
|
||||
"method" in msg && "id" in msg && msg.id !== undefined && msg.id !== null;
|
||||
"method" in msg &&
|
||||
"id" in msg &&
|
||||
msg.id !== undefined &&
|
||||
msg.id !== null;
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from "./generated/types.gen.js";
|
||||
export * from "./generated/zod.gen.js";
|
||||
export { GooseClient } from "./goose-client.js";
|
||||
export { createHttpStream } from "./http-stream.js";
|
||||
|
||||
export {
|
||||
ClientSideConnection,
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "nodenext",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
"rootDir": "./src",
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": ["src"],
|
||||
}
|
||||
|
||||
Generated
+2335
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"acp",
|
||||
"text"
|
||||
]
|
||||
}
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Publishes @block/goose-acp, @block/goose, and all native binary packages to npm.
|
||||
#
|
||||
# Usage:
|
||||
# ./ui/scripts/publish.sh # publish all (dry-run)
|
||||
# ./ui/scripts/publish.sh --real # publish for real
|
||||
#
|
||||
# Prerequisites:
|
||||
# - npm login to the @block scope
|
||||
# - Native binaries built via build-native-packages.sh
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
NPM_DIR="${REPO_ROOT}/npm"
|
||||
ACP_DIR="${REPO_ROOT}/ui/acp"
|
||||
TEXT_DIR="${REPO_ROOT}/ui/text"
|
||||
|
||||
DRY_RUN="--dry-run"
|
||||
if [[ "${1:-}" == "--real" ]]; then
|
||||
DRY_RUN=""
|
||||
echo "==> Publishing for real"
|
||||
else
|
||||
echo "==> Dry run (pass --real to publish)"
|
||||
fi
|
||||
|
||||
# Build and publish @block/goose-acp first (dependency of @block/goose)
|
||||
echo "==> Building @block/goose-acp"
|
||||
(cd "${ACP_DIR}" && npm run build)
|
||||
|
||||
echo "==> Publishing @block/goose-acp"
|
||||
(cd "${ACP_DIR}" && npm publish --access public ${DRY_RUN})
|
||||
|
||||
# Build @block/goose
|
||||
echo "==> Building @block/goose"
|
||||
(cd "${TEXT_DIR}" && npm run build)
|
||||
|
||||
NATIVE_PACKAGES=(
|
||||
"goose-acp-server-darwin-arm64"
|
||||
"goose-acp-server-darwin-x64"
|
||||
"goose-acp-server-linux-arm64"
|
||||
"goose-acp-server-linux-x64"
|
||||
"goose-acp-server-win32-x64"
|
||||
)
|
||||
|
||||
# Publish native binary packages
|
||||
for pkg in "${NATIVE_PACKAGES[@]}"; do
|
||||
pkg_dir="${NPM_DIR}/${pkg}"
|
||||
|
||||
if [ ! -f "${pkg_dir}/bin/goose-acp-server" ] && [ ! -f "${pkg_dir}/bin/goose-acp-server.exe" ]; then
|
||||
echo " SKIP ${pkg} (no binary found — run build-native-packages.sh first)"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "==> Publishing @block/${pkg}"
|
||||
(cd "${pkg_dir}" && npm publish --access public ${DRY_RUN})
|
||||
done
|
||||
|
||||
# Publish the main package
|
||||
echo "==> Publishing @block/goose"
|
||||
(cd "${TEXT_DIR}" && npm publish --access public ${DRY_RUN})
|
||||
|
||||
echo "==> Done"
|
||||
@@ -1 +1,3 @@
|
||||
dist
|
||||
node_modules
|
||||
server-binary.json
|
||||
|
||||
Generated
+648
-130
@@ -1,49 +1,50 @@
|
||||
{
|
||||
"name": "goose-text",
|
||||
"name": "@block/goose",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "goose-text",
|
||||
"name": "@block/goose",
|
||||
"version": "0.1.0",
|
||||
"bundleDependencies": [
|
||||
"@block/goose-acp"
|
||||
],
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.14.1",
|
||||
"@goose/acp": "file:../acp",
|
||||
"@block/goose-acp": "file:../acp",
|
||||
"ink": "^5.1.0",
|
||||
"ink-text-input": "^6.0.0",
|
||||
"meow": "^13.2.0",
|
||||
"react": "^18.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"goose": "dist/tui.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.2.3",
|
||||
"@types/react": "^18.3.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"tsx": "^4.19.0",
|
||||
"typescript": "^5.7.0"
|
||||
}
|
||||
},
|
||||
"../acp": {
|
||||
"name": "goose-acp-types",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"zod": "^3.25.76"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.14.1",
|
||||
"@hey-api/openapi-ts": "^0.92.3",
|
||||
"prettier": "^3.8.1",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "~5.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@agentclientprotocol/sdk": "*"
|
||||
"optionalDependencies": {
|
||||
"@block/goose-acp-server-darwin-arm64": "0.1.0",
|
||||
"@block/goose-acp-server-darwin-x64": "0.1.0",
|
||||
"@block/goose-acp-server-linux-arm64": "0.1.0",
|
||||
"@block/goose-acp-server-linux-x64": "0.1.0",
|
||||
"@block/goose-acp-server-win32-x64": "0.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@agentclientprotocol/sdk": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.14.1.tgz",
|
||||
"integrity": "sha512-b6r3PS3Nly+Wyw9U+0nOr47bV8tfS476EgyEMhoKvJCZLbgqoDFN7DJwkxL88RR0aiOqOYV1ZnESHqb+RmdH8w==",
|
||||
"inBundle": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
}
|
||||
@@ -61,10 +62,46 @@
|
||||
"node": ">=14.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@block/goose-acp": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "file:../acp",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"zod": "^3.25.76"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@agentclientprotocol/sdk": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@block/goose-acp-server-darwin-arm64": {
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@block/goose-acp-server-darwin-x64": {
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@block/goose-acp-server-linux-arm64": {
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@block/goose-acp-server-linux-x64": {
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@block/goose-acp-server-win32-x64": {
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@block/goose-acp/node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
|
||||
"integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
|
||||
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -79,9 +116,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
|
||||
"integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
|
||||
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -96,9 +133,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -113,9 +150,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -130,9 +167,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -147,9 +184,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -164,9 +201,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -181,9 +218,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -198,9 +235,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
|
||||
"integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
|
||||
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -215,9 +252,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -232,9 +269,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
|
||||
"integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
|
||||
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -249,9 +286,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
|
||||
"integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
|
||||
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -266,9 +303,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
|
||||
"integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
|
||||
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
@@ -283,9 +320,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
|
||||
"integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
|
||||
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -300,9 +337,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
|
||||
"integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
|
||||
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -317,9 +354,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
|
||||
"integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
|
||||
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -334,9 +371,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -351,9 +388,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -368,9 +405,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -385,9 +422,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -402,9 +439,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -419,9 +456,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -436,9 +473,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -453,9 +490,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
|
||||
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -470,9 +507,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
|
||||
"integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
|
||||
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -487,9 +524,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
|
||||
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -503,10 +540,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@goose/acp": {
|
||||
"resolved": "../acp",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz",
|
||||
@@ -715,9 +748,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
|
||||
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
|
||||
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
@@ -728,32 +761,32 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.27.3",
|
||||
"@esbuild/android-arm": "0.27.3",
|
||||
"@esbuild/android-arm64": "0.27.3",
|
||||
"@esbuild/android-x64": "0.27.3",
|
||||
"@esbuild/darwin-arm64": "0.27.3",
|
||||
"@esbuild/darwin-x64": "0.27.3",
|
||||
"@esbuild/freebsd-arm64": "0.27.3",
|
||||
"@esbuild/freebsd-x64": "0.27.3",
|
||||
"@esbuild/linux-arm": "0.27.3",
|
||||
"@esbuild/linux-arm64": "0.27.3",
|
||||
"@esbuild/linux-ia32": "0.27.3",
|
||||
"@esbuild/linux-loong64": "0.27.3",
|
||||
"@esbuild/linux-mips64el": "0.27.3",
|
||||
"@esbuild/linux-ppc64": "0.27.3",
|
||||
"@esbuild/linux-riscv64": "0.27.3",
|
||||
"@esbuild/linux-s390x": "0.27.3",
|
||||
"@esbuild/linux-x64": "0.27.3",
|
||||
"@esbuild/netbsd-arm64": "0.27.3",
|
||||
"@esbuild/netbsd-x64": "0.27.3",
|
||||
"@esbuild/openbsd-arm64": "0.27.3",
|
||||
"@esbuild/openbsd-x64": "0.27.3",
|
||||
"@esbuild/openharmony-arm64": "0.27.3",
|
||||
"@esbuild/sunos-x64": "0.27.3",
|
||||
"@esbuild/win32-arm64": "0.27.3",
|
||||
"@esbuild/win32-ia32": "0.27.3",
|
||||
"@esbuild/win32-x64": "0.27.3"
|
||||
"@esbuild/aix-ppc64": "0.25.12",
|
||||
"@esbuild/android-arm": "0.25.12",
|
||||
"@esbuild/android-arm64": "0.25.12",
|
||||
"@esbuild/android-x64": "0.25.12",
|
||||
"@esbuild/darwin-arm64": "0.25.12",
|
||||
"@esbuild/darwin-x64": "0.25.12",
|
||||
"@esbuild/freebsd-arm64": "0.25.12",
|
||||
"@esbuild/freebsd-x64": "0.25.12",
|
||||
"@esbuild/linux-arm": "0.25.12",
|
||||
"@esbuild/linux-arm64": "0.25.12",
|
||||
"@esbuild/linux-ia32": "0.25.12",
|
||||
"@esbuild/linux-loong64": "0.25.12",
|
||||
"@esbuild/linux-mips64el": "0.25.12",
|
||||
"@esbuild/linux-ppc64": "0.25.12",
|
||||
"@esbuild/linux-riscv64": "0.25.12",
|
||||
"@esbuild/linux-s390x": "0.25.12",
|
||||
"@esbuild/linux-x64": "0.25.12",
|
||||
"@esbuild/netbsd-arm64": "0.25.12",
|
||||
"@esbuild/netbsd-x64": "0.25.12",
|
||||
"@esbuild/openbsd-arm64": "0.25.12",
|
||||
"@esbuild/openbsd-x64": "0.25.12",
|
||||
"@esbuild/openharmony-arm64": "0.25.12",
|
||||
"@esbuild/sunos-x64": "0.25.12",
|
||||
"@esbuild/win32-arm64": "0.25.12",
|
||||
"@esbuild/win32-ia32": "0.25.12",
|
||||
"@esbuild/win32-x64": "0.25.12"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
@@ -1138,6 +1171,490 @@
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
|
||||
"integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/android-arm": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
|
||||
"integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/android-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
|
||||
"integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
|
||||
"integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
|
||||
"integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
|
||||
"integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
|
||||
"integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
|
||||
"integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
|
||||
"integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
|
||||
"integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx/node_modules/esbuild": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
|
||||
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.27.3",
|
||||
"@esbuild/android-arm": "0.27.3",
|
||||
"@esbuild/android-arm64": "0.27.3",
|
||||
"@esbuild/android-x64": "0.27.3",
|
||||
"@esbuild/darwin-arm64": "0.27.3",
|
||||
"@esbuild/darwin-x64": "0.27.3",
|
||||
"@esbuild/freebsd-arm64": "0.27.3",
|
||||
"@esbuild/freebsd-x64": "0.27.3",
|
||||
"@esbuild/linux-arm": "0.27.3",
|
||||
"@esbuild/linux-arm64": "0.27.3",
|
||||
"@esbuild/linux-ia32": "0.27.3",
|
||||
"@esbuild/linux-loong64": "0.27.3",
|
||||
"@esbuild/linux-mips64el": "0.27.3",
|
||||
"@esbuild/linux-ppc64": "0.27.3",
|
||||
"@esbuild/linux-riscv64": "0.27.3",
|
||||
"@esbuild/linux-s390x": "0.27.3",
|
||||
"@esbuild/linux-x64": "0.27.3",
|
||||
"@esbuild/netbsd-arm64": "0.27.3",
|
||||
"@esbuild/netbsd-x64": "0.27.3",
|
||||
"@esbuild/openbsd-arm64": "0.27.3",
|
||||
"@esbuild/openbsd-x64": "0.27.3",
|
||||
"@esbuild/openharmony-arm64": "0.27.3",
|
||||
"@esbuild/sunos-x64": "0.27.3",
|
||||
"@esbuild/win32-arm64": "0.27.3",
|
||||
"@esbuild/win32-ia32": "0.27.3",
|
||||
"@esbuild/win32-x64": "0.27.3"
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
|
||||
@@ -1234,6 +1751,7 @@
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
||||
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
|
||||
+29
-4
@@ -1,24 +1,49 @@
|
||||
{
|
||||
"name": "goose-text",
|
||||
"name": "@block/goose",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "Goose - an open-source AI agent",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/block/goose.git"
|
||||
},
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"goose": "dist/tui.js"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"scripts/postinstall.mjs",
|
||||
"server-binary.json"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "tsx src/cli.tsx",
|
||||
"start": "tsx src/tui.tsx",
|
||||
"postinstall": "node scripts/postinstall.mjs",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.14.1",
|
||||
"@goose/acp": "file:../acp",
|
||||
"@block/goose-acp": "^0.1.0",
|
||||
"ink": "^5.1.0",
|
||||
"ink-text-input": "^6.0.0",
|
||||
"marked": "^15.0.12",
|
||||
"marked-terminal": "^7.3.0",
|
||||
"meow": "^13.2.0",
|
||||
"react": "^18.3.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@block/goose-acp-server-darwin-arm64": "0.1.0",
|
||||
"@block/goose-acp-server-darwin-x64": "0.1.0",
|
||||
"@block/goose-acp-server-linux-arm64": "0.1.0",
|
||||
"@block/goose-acp-server-linux-x64": "0.1.0",
|
||||
"@block/goose-acp-server-win32-x64": "0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/marked-terminal": "^6.1.1",
|
||||
"@types/node": "^25.2.3",
|
||||
"@types/react": "^18.3.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"tsx": "^4.19.0",
|
||||
"typescript": "^5.7.0"
|
||||
}
|
||||
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Builds the goose-acp-server binary for each target platform and places it
|
||||
# into the corresponding npm package directory under npm/.
|
||||
#
|
||||
# Usage:
|
||||
# ./ui/text/scripts/build-native-packages.sh # build all targets
|
||||
# ./ui/text/scripts/build-native-packages.sh darwin-arm64 # build one target
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Rust cross-compilation toolchains installed for each target
|
||||
# - Run from the repository root
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
NPM_DIR="${REPO_ROOT}/npm"
|
||||
|
||||
declare -A RUST_TARGETS=(
|
||||
["darwin-arm64"]="aarch64-apple-darwin"
|
||||
["darwin-x64"]="x86_64-apple-darwin"
|
||||
["linux-arm64"]="aarch64-unknown-linux-gnu"
|
||||
["linux-x64"]="x86_64-unknown-linux-gnu"
|
||||
["win32-x64"]="x86_64-pc-windows-msvc"
|
||||
)
|
||||
|
||||
build_target() {
|
||||
local platform="$1"
|
||||
local rust_target="${RUST_TARGETS[$platform]}"
|
||||
local pkg_dir="${NPM_DIR}/goose-acp-server-${platform}"
|
||||
local bin_dir="${pkg_dir}/bin"
|
||||
|
||||
echo "==> Building goose-acp-server for ${platform} (${rust_target})"
|
||||
|
||||
cargo build --release --target "${rust_target}" --bin goose-acp-server
|
||||
|
||||
mkdir -p "${bin_dir}"
|
||||
|
||||
local ext=""
|
||||
if [[ "$platform" == win32-* ]]; then
|
||||
ext=".exe"
|
||||
fi
|
||||
|
||||
cp "${REPO_ROOT}/target/${rust_target}/release/goose-acp-server${ext}" "${bin_dir}/goose-acp-server${ext}"
|
||||
chmod +x "${bin_dir}/goose-acp-server${ext}"
|
||||
|
||||
echo " Placed binary at ${bin_dir}/goose-acp-server${ext}"
|
||||
}
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
# Build specific target(s)
|
||||
for target in "$@"; do
|
||||
if [[ -z "${RUST_TARGETS[$target]+x}" ]]; then
|
||||
echo "Unknown target: ${target}"
|
||||
echo "Valid targets: ${!RUST_TARGETS[*]}"
|
||||
exit 1
|
||||
fi
|
||||
build_target "$target"
|
||||
done
|
||||
else
|
||||
# Build all targets
|
||||
for platform in "${!RUST_TARGETS[@]}"; do
|
||||
build_target "$platform"
|
||||
done
|
||||
fi
|
||||
|
||||
echo "==> Done. Native packages staged in ${NPM_DIR}/"
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Resolves the path to the goose-acp-server binary from the platform-specific
|
||||
// optional dependency. Writes the result to a JSON file that the CLI reads at
|
||||
// startup so it can spawn the server automatically.
|
||||
|
||||
import { writeFileSync, mkdirSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const PLATFORMS = {
|
||||
"darwin-arm64": "@block/goose-acp-server-darwin-arm64",
|
||||
"darwin-x64": "@block/goose-acp-server-darwin-x64",
|
||||
"linux-arm64": "@block/goose-acp-server-linux-arm64",
|
||||
"linux-x64": "@block/goose-acp-server-linux-x64",
|
||||
"win32-x64": "@block/goose-acp-server-win32-x64",
|
||||
};
|
||||
|
||||
const key = `${process.platform}-${process.arch}`;
|
||||
const pkg = PLATFORMS[key];
|
||||
|
||||
if (!pkg) {
|
||||
console.warn(
|
||||
`@block/goose: no prebuilt goose-acp-server binary for ${key}. ` +
|
||||
`You will need to provide a server URL manually with --server.`,
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
let binaryPath;
|
||||
try {
|
||||
// Resolve the package directory, then point at the binary inside it
|
||||
const pkgDir = dirname(require.resolve(`${pkg}/package.json`));
|
||||
const binName =
|
||||
process.platform === "win32" ? "goose-acp-server.exe" : "goose-acp-server";
|
||||
binaryPath = join(pkgDir, "bin", binName);
|
||||
} catch {
|
||||
// The optional dependency wasn't installed (e.g. wrong platform). That's fine.
|
||||
console.warn(
|
||||
`@block/goose: optional dependency ${pkg} not installed. ` +
|
||||
`You will need to provide a server URL manually with --server.`,
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const outDir = join(__dirname, "..");
|
||||
mkdirSync(outDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(outDir, "server-binary.json"),
|
||||
JSON.stringify({ binaryPath }, null, 2) + "\n",
|
||||
);
|
||||
|
||||
console.log(`@block/goose: found native server binary at ${binaryPath}`);
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import React from "react";
|
||||
import { render } from "ink";
|
||||
import meow from "meow";
|
||||
import App from "./app.js";
|
||||
|
||||
const cli = meow(
|
||||
`
|
||||
Usage
|
||||
$ goose-text
|
||||
|
||||
Options
|
||||
--server, -s Server URL (default: http://127.0.0.1:3284)
|
||||
--text, -t Send a single prompt and exit
|
||||
`,
|
||||
{
|
||||
importMeta: import.meta,
|
||||
flags: {
|
||||
server: { type: "string", shortFlag: "s", default: "http://127.0.0.1:3284" },
|
||||
text: { type: "string", shortFlag: "t" },
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
render(<App serverUrl={cli.flags.server} initialPrompt={cli.flags.text} />);
|
||||
@@ -0,0 +1,7 @@
|
||||
export const CRANBERRY = "#C0354A";
|
||||
export const TEAL = "#3A7D7B";
|
||||
export const GOLD = "#C4883A";
|
||||
export const TEXT_PRIMARY = "#E8E4DF";
|
||||
export const TEXT_SECONDARY = "#8FA4BD";
|
||||
export const TEXT_DIM = "#5A6D84";
|
||||
export const RULE_COLOR = "#2E3D54";
|
||||
@@ -0,0 +1,10 @@
|
||||
import { marked } from "marked";
|
||||
import { markedTerminal } from "marked-terminal";
|
||||
|
||||
marked.use(markedTerminal({ width: 76, reflowText: true, tab: 2 }) as any);
|
||||
|
||||
export function renderMarkdown(src: string): string {
|
||||
if (!src) return "";
|
||||
const rendered = marked.parse(src) as string;
|
||||
return rendered.replace(/\n+$/, "");
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
import React from "react";
|
||||
import { Box, Text } from "ink";
|
||||
import type {
|
||||
ToolCallContent,
|
||||
ToolCallStatus,
|
||||
ToolKind,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
import { CRANBERRY, TEAL, GOLD, TEXT_SECONDARY, TEXT_DIM } from "./colors.js";
|
||||
|
||||
export interface ToolCallInfo {
|
||||
toolCallId: string;
|
||||
title: string;
|
||||
status: ToolCallStatus;
|
||||
kind?: ToolKind;
|
||||
rawInput?: unknown;
|
||||
rawOutput?: unknown;
|
||||
content?: ToolCallContent[];
|
||||
locations?: Array<{ path: string; line?: number | null }>;
|
||||
}
|
||||
|
||||
const CEDAR = "#6B5344";
|
||||
|
||||
const KIND_ICONS: Record<string, string> = {
|
||||
read: "📖",
|
||||
edit: "✏️",
|
||||
delete: "🗑",
|
||||
move: "📦",
|
||||
search: "🔍",
|
||||
execute: "▶",
|
||||
think: "💭",
|
||||
fetch: "🌐",
|
||||
switch_mode: "🔀",
|
||||
other: "⚙",
|
||||
};
|
||||
|
||||
const STATUS_INDICATORS: Record<string, { icon: string; color: string }> = {
|
||||
pending: { icon: "○", color: TEXT_DIM },
|
||||
in_progress: { icon: "◑", color: GOLD },
|
||||
completed: { icon: "●", color: TEAL },
|
||||
failed: { icon: "✗", color: CRANBERRY },
|
||||
};
|
||||
|
||||
function formatJsonCompact(value: unknown, maxWidth: number): string[] {
|
||||
if (value === undefined || value === null) return [];
|
||||
let raw: string;
|
||||
try {
|
||||
raw = JSON.stringify(value, null, 2);
|
||||
} catch {
|
||||
raw = String(value);
|
||||
}
|
||||
const lines = raw.split("\n");
|
||||
const result: string[] = [];
|
||||
for (const line of lines) {
|
||||
if (line.length <= maxWidth) {
|
||||
result.push(line);
|
||||
} else {
|
||||
let remaining = line;
|
||||
while (remaining.length > maxWidth) {
|
||||
result.push(remaining.slice(0, maxWidth));
|
||||
remaining = remaining.slice(maxWidth);
|
||||
}
|
||||
if (remaining) result.push(remaining);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function extractTextFromContent(content: ToolCallContent[]): string[] {
|
||||
const lines: string[] = [];
|
||||
for (const item of content) {
|
||||
if (item.type === "content" && item.content) {
|
||||
const block = item.content as any;
|
||||
if (block.type === "text" && block.text) {
|
||||
lines.push(...block.text.split("\n"));
|
||||
}
|
||||
} else if (item.type === "diff") {
|
||||
const diff = item as any;
|
||||
lines.push(`diff: ${diff.path || "unknown"}`);
|
||||
} else if (item.type === "terminal") {
|
||||
const term = item as any;
|
||||
lines.push(`terminal: ${term.terminalId || "unknown"}`);
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
function summarizeContent(info: ToolCallInfo): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
if (info.locations && info.locations.length > 0) {
|
||||
for (const loc of info.locations) {
|
||||
parts.push(loc.path + (loc.line ? `:${loc.line}` : ""));
|
||||
}
|
||||
}
|
||||
|
||||
if (info.content && info.content.length > 0) {
|
||||
const textLines = extractTextFromContent(info.content);
|
||||
if (textLines.length > 0) {
|
||||
const first = textLines[0]!.trim();
|
||||
if (first.length > 60) {
|
||||
parts.push(first.slice(0, 57) + "…");
|
||||
} else if (first) {
|
||||
parts.push(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parts.length === 0 && info.rawOutput !== undefined && info.rawOutput !== null) {
|
||||
const raw = String(
|
||||
typeof info.rawOutput === "string" ? info.rawOutput : JSON.stringify(info.rawOutput),
|
||||
);
|
||||
const firstLine = raw.split("\n")[0] ?? "";
|
||||
if (firstLine.length > 60) {
|
||||
parts.push(firstLine.slice(0, 57) + "…");
|
||||
} else if (firstLine) {
|
||||
parts.push(firstLine);
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join(" · ");
|
||||
}
|
||||
|
||||
const MAX_PREVIEW_LINES = 8;
|
||||
|
||||
export function findFeaturedToolCallId(
|
||||
toolCallOrder: string[],
|
||||
toolCalls: Map<string, ToolCallInfo>,
|
||||
): string | undefined {
|
||||
for (let i = toolCallOrder.length - 1; i >= 0; i--) {
|
||||
const tc = toolCalls.get(toolCallOrder[i]!);
|
||||
if (tc && (tc.status === "pending" || tc.status === "in_progress")) {
|
||||
return toolCallOrder[i]!;
|
||||
}
|
||||
}
|
||||
return toolCallOrder[toolCallOrder.length - 1];
|
||||
}
|
||||
|
||||
export function buildToolCallCardLines(
|
||||
info: ToolCallInfo,
|
||||
indent: number,
|
||||
totalWidth: number,
|
||||
expanded: boolean,
|
||||
keyPrefix: string = "card",
|
||||
): React.ReactNode[] {
|
||||
const cardWidth = Math.min(totalWidth - indent - 2, 72);
|
||||
const innerWidth = cardWidth - 2;
|
||||
const contentWidth = innerWidth - 2;
|
||||
const kindIcon = KIND_ICONS[info.kind ?? "other"] ?? "⚙";
|
||||
const statusInfo = STATUS_INDICATORS[info.status] ?? STATUS_INDICATORS.pending!;
|
||||
const borderColor = info.status === "failed" ? CRANBERRY : CEDAR;
|
||||
const dimBorder = info.status !== "failed";
|
||||
|
||||
const hasInput = info.rawInput !== undefined && info.rawInput !== null;
|
||||
const hasOutput = info.rawOutput !== undefined && info.rawOutput !== null;
|
||||
const hasContent = info.content && info.content.length > 0;
|
||||
const hasLocations = info.locations && info.locations.length > 0;
|
||||
|
||||
const inputLines = hasInput ? formatJsonCompact(info.rawInput, contentWidth - 6) : [];
|
||||
const outputLines = hasOutput ? formatJsonCompact(info.rawOutput, contentWidth - 6) : [];
|
||||
const contentLines = hasContent ? extractTextFromContent(info.content!) : [];
|
||||
|
||||
const shownInput = expanded ? inputLines : inputLines.slice(0, MAX_PREVIEW_LINES);
|
||||
const shownOutput = expanded ? outputLines : outputLines.slice(0, MAX_PREVIEW_LINES);
|
||||
const shownContent = expanded ? contentLines : contentLines.slice(0, MAX_PREVIEW_LINES);
|
||||
|
||||
const hasTruncated =
|
||||
inputLines.length > MAX_PREVIEW_LINES ||
|
||||
outputLines.length > MAX_PREVIEW_LINES ||
|
||||
contentLines.length > MAX_PREVIEW_LINES;
|
||||
|
||||
const bodyRows: Array<{ text: string; color?: string; italic?: boolean }> = [];
|
||||
|
||||
const runningText = info.status === "in_progress" ? " running…" : "";
|
||||
const tabHint = hasTruncated && !expanded ? "tab ↔" : "";
|
||||
bodyRows.push({ text: "__HEADER__" });
|
||||
|
||||
if (hasLocations) {
|
||||
for (const loc of info.locations!) {
|
||||
bodyRows.push({ text: ` 📁 ${loc.path}${loc.line ? `:${loc.line}` : ""}`, color: TEXT_DIM });
|
||||
}
|
||||
}
|
||||
|
||||
function addSection(label: string, lines: string[], totalCount: number) {
|
||||
if (lines.length === 0) return;
|
||||
bodyRows.push({ text: ` ▸ ${label}:`, color: TEXT_DIM });
|
||||
for (const line of lines) {
|
||||
bodyRows.push({ text: ` ${line}`, color: TEXT_DIM });
|
||||
}
|
||||
if (!expanded && totalCount > MAX_PREVIEW_LINES) {
|
||||
const remaining = totalCount - MAX_PREVIEW_LINES;
|
||||
bodyRows.push({ text: ` ▸ ${remaining} more lines (tab to expand)`, color: GOLD, italic: true });
|
||||
}
|
||||
}
|
||||
|
||||
addSection("input", shownInput, inputLines.length);
|
||||
addSection("output", shownOutput, outputLines.length);
|
||||
addSection("content", shownContent, contentLines.length);
|
||||
|
||||
const result: React.ReactNode[] = [];
|
||||
const topBorder = "╭" + "─".repeat(innerWidth) + "╮";
|
||||
const botBorder = "╰" + "─".repeat(innerWidth) + "╯";
|
||||
|
||||
result.push(
|
||||
<Box key={`${keyPrefix}-top`} marginLeft={indent} height={1}>
|
||||
<Text color={borderColor} dimColor={dimBorder}>{topBorder}</Text>
|
||||
</Box>,
|
||||
);
|
||||
|
||||
for (let i = 0; i < bodyRows.length; i++) {
|
||||
const row = bodyRows[i]!;
|
||||
|
||||
if (row.text === "__HEADER__") {
|
||||
result.push(
|
||||
<Box key={`${keyPrefix}-row-${i}`} marginLeft={indent} width={cardWidth} height={1}>
|
||||
<Text color={borderColor} dimColor={dimBorder}>│ </Text>
|
||||
<Box flexGrow={1} justifyContent="space-between">
|
||||
<Box>
|
||||
<Text color={statusInfo.color}>{statusInfo.icon} </Text>
|
||||
<Text>{kindIcon} </Text>
|
||||
<Text color={TEXT_SECONDARY} bold>{info.title}</Text>
|
||||
{runningText ? <Text color={TEXT_DIM} italic>{runningText}</Text> : null}
|
||||
</Box>
|
||||
{tabHint ? <Text color={TEXT_DIM} italic>{tabHint}</Text> : null}
|
||||
</Box>
|
||||
<Text color={borderColor} dimColor={dimBorder}> │</Text>
|
||||
</Box>,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push(
|
||||
<Box key={`${keyPrefix}-row-${i}`} marginLeft={indent} width={cardWidth} height={1}>
|
||||
<Text color={borderColor} dimColor={dimBorder}>│</Text>
|
||||
<Box flexGrow={1}>
|
||||
<Text color={row.color} italic={row.italic}> {row.text}</Text>
|
||||
</Box>
|
||||
<Text color={borderColor} dimColor={dimBorder}>│</Text>
|
||||
</Box>,
|
||||
);
|
||||
}
|
||||
|
||||
result.push(
|
||||
<Box key={`${keyPrefix}-bot`} marginLeft={indent} height={1}>
|
||||
<Text color={borderColor} dimColor={dimBorder}>{botBorder}</Text>
|
||||
</Box>,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function ToolCallCompact({
|
||||
info,
|
||||
indent,
|
||||
width,
|
||||
}: {
|
||||
info: ToolCallInfo;
|
||||
indent: number;
|
||||
width: number;
|
||||
}) {
|
||||
const statusInfo = STATUS_INDICATORS[info.status] ?? STATUS_INDICATORS.pending!;
|
||||
const kindIcon = KIND_ICONS[info.kind ?? "other"] ?? "⚙";
|
||||
const summary = summarizeContent(info);
|
||||
const maxSummaryWidth = width - indent - 12 - info.title.length;
|
||||
const trimmedSummary =
|
||||
summary.length > maxSummaryWidth && maxSummaryWidth > 3
|
||||
? summary.slice(0, maxSummaryWidth - 1) + "…"
|
||||
: summary;
|
||||
|
||||
return (
|
||||
<Box marginLeft={indent} height={1}>
|
||||
<Text color={statusInfo.color}>{statusInfo.icon} </Text>
|
||||
<Text>{kindIcon} </Text>
|
||||
<Text color={TEXT_SECONDARY}>{info.title}</Text>
|
||||
{trimmedSummary ? (
|
||||
<Text color={TEXT_DIM}> — {trimmedSummary}</Text>
|
||||
) : null}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,26 @@
|
||||
#!/usr/bin/env node
|
||||
import React, { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { Box, Text, useApp, useInput, useStdout } from "ink";
|
||||
import { Box, Text, render, useApp, useInput, useStdout, measureElement } from "ink";
|
||||
import type { DOMElement } from "ink";
|
||||
import TextInput from "ink-text-input";
|
||||
import meow from "meow";
|
||||
import { spawn } from "node:child_process";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type {
|
||||
SessionNotification,
|
||||
RequestPermissionRequest,
|
||||
RequestPermissionResponse,
|
||||
ToolCallContent,
|
||||
ToolCallStatus,
|
||||
ToolKind,
|
||||
} from "@agentclientprotocol/sdk";
|
||||
import { GooseClient } from "@goose/acp";
|
||||
import { createHttpStream } from "./transport.js";
|
||||
import { GooseClient } from "@block/goose-acp";
|
||||
import { renderMarkdown } from "./markdown.js";
|
||||
import { buildToolCallCardLines, ToolCallCompact, findFeaturedToolCallId } from "./toolcall.js";
|
||||
import type { ToolCallInfo } from "./toolcall.js";
|
||||
import { CRANBERRY, TEAL, GOLD, TEXT_PRIMARY, TEXT_SECONDARY, TEXT_DIM, RULE_COLOR } from "./colors.js";
|
||||
|
||||
interface PendingPermission {
|
||||
toolTitle: string;
|
||||
@@ -15,15 +28,16 @@ interface PendingPermission {
|
||||
resolve: (response: RequestPermissionResponse) => void;
|
||||
}
|
||||
|
||||
const CRANBERRY = "#C0354A";
|
||||
const TEAL = "#3A7D7B";
|
||||
const GOLD = "#C4883A";
|
||||
const CEDAR = "#6B5344";
|
||||
interface Turn {
|
||||
userText: string;
|
||||
toolCalls: Map<string, ToolCallInfo>;
|
||||
toolCallOrder: string[];
|
||||
agentText: string;
|
||||
}
|
||||
|
||||
const TEXT_PRIMARY = "#E8E4DF";
|
||||
const TEXT_SECONDARY = "#8FA4BD";
|
||||
const TEXT_DIM = "#5A6D84";
|
||||
const RULE_COLOR = "#2E3D54";
|
||||
function isErrorStatus(status: string): boolean {
|
||||
return status.startsWith("error") || status.startsWith("failed");
|
||||
}
|
||||
|
||||
const GOOSE_FRAMES = [
|
||||
[
|
||||
@@ -104,30 +118,11 @@ const PERMISSION_KEYS: Record<string, string> = {
|
||||
reject_always: "N",
|
||||
};
|
||||
|
||||
interface Turn {
|
||||
userText: string;
|
||||
toolCalls: string[];
|
||||
agentText: string;
|
||||
}
|
||||
|
||||
// ─── Layout constants ───────────────────────────────────────────────────────
|
||||
//
|
||||
// Every element indents by a multiple of INDENT (3 spaces). This keeps the
|
||||
// left edge of user prompts, agent prose, and tool-call badges on a
|
||||
// predictable grid so the eye can scan vertically without friction.
|
||||
//
|
||||
// col 0 rule / header
|
||||
// col 3 user prompt caret + text, input caret + text
|
||||
// col 5 agent prose, tool badges, loading spinner, permission dialog
|
||||
|
||||
const INDENT = 3;
|
||||
const CONTENT_INDENT = 5;
|
||||
const MAX_PROSE_WIDTH = 76;
|
||||
|
||||
function Rule({ width }: { width: number }) {
|
||||
return (
|
||||
<Text color={RULE_COLOR}>{"─".repeat(Math.max(width, 1))}</Text>
|
||||
);
|
||||
return <Text color={RULE_COLOR}>{"─".repeat(Math.max(width, 1))}</Text>;
|
||||
}
|
||||
|
||||
function Spinner({ idx }: { idx: number }) {
|
||||
@@ -153,9 +148,7 @@ function Header({
|
||||
hasPendingPermission: boolean;
|
||||
turnInfo?: { current: number; total: number };
|
||||
}) {
|
||||
const isError =
|
||||
status.startsWith("error") || status.startsWith("failed");
|
||||
const statusColor = status === "ready" ? TEAL : isError ? CRANBERRY : TEXT_DIM;
|
||||
const statusColor = status === "ready" ? TEAL : isErrorStatus(status) ? CRANBERRY : TEXT_DIM;
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" width={width}>
|
||||
@@ -167,7 +160,10 @@ function Header({
|
||||
<Text color={RULE_COLOR}> · </Text>
|
||||
<Text color={statusColor}>{status}</Text>
|
||||
{loading && !hasPendingPermission && (
|
||||
<Text> <Spinner idx={spinIdx} /></Text>
|
||||
<Text>
|
||||
{" "}
|
||||
<Spinner idx={spinIdx} />
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Box>
|
||||
@@ -198,25 +194,6 @@ function UserPrompt({ text }: { text: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
function ToolBadge({ title, width }: { title: string; width: number }) {
|
||||
const badgeWidth = Math.min(width - CONTENT_INDENT - 2, 68);
|
||||
return (
|
||||
<Box
|
||||
marginLeft={CONTENT_INDENT}
|
||||
paddingX={1}
|
||||
borderStyle="round"
|
||||
borderColor={CEDAR}
|
||||
borderDimColor
|
||||
width={badgeWidth}
|
||||
>
|
||||
<Text color={TEAL}>⚙ </Text>
|
||||
<Text color={TEXT_SECONDARY} italic>
|
||||
{title}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function PermissionDialog({
|
||||
toolTitle,
|
||||
options,
|
||||
@@ -286,16 +263,6 @@ function QueuedMessage({ text }: { text: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
function inputBarHeight(input: string, width: number, queued: boolean): number {
|
||||
// Inner text width: width minus border (2), paddingX (2), prompt "❯ " (2)
|
||||
const textWidth = Math.max(width - 2 - 2 - 2, 1);
|
||||
// +1 for the trailing cursor character
|
||||
const contentLen = input.length + 1;
|
||||
const wrappedLines = Math.max(Math.ceil(contentLen / textWidth), 1);
|
||||
const queuedLine = queued ? 1 : 0;
|
||||
return 2 + wrappedLines + queuedLine + 1;
|
||||
}
|
||||
|
||||
function InputBar({
|
||||
width,
|
||||
input,
|
||||
@@ -327,9 +294,7 @@ function InputBar({
|
||||
</Text>
|
||||
<TextInput value={input} onChange={onChange} onSubmit={onSubmit} />
|
||||
</Box>
|
||||
{scrollHint && (
|
||||
<Text color={TEXT_DIM}>shift+↑↓ history</Text>
|
||||
)}
|
||||
{scrollHint && <Text color={TEXT_DIM}>shift+↑↓ history</Text>}
|
||||
</Box>
|
||||
{queued && (
|
||||
<Box>
|
||||
@@ -343,77 +308,70 @@ function InputBar({
|
||||
);
|
||||
}
|
||||
|
||||
function wrapText(text: string, width: number): string[] {
|
||||
if (width <= 0) return [text];
|
||||
const result: string[] = [];
|
||||
for (const rawLine of text.split("\n")) {
|
||||
if (rawLine.length === 0) {
|
||||
result.push("");
|
||||
continue;
|
||||
}
|
||||
let remaining = rawLine;
|
||||
while (remaining.length > width) {
|
||||
let breakAt = remaining.lastIndexOf(" ", width);
|
||||
if (breakAt <= 0) breakAt = width;
|
||||
result.push(remaining.slice(0, breakAt));
|
||||
remaining = remaining.slice(breakAt).replace(/^ /, "");
|
||||
}
|
||||
result.push(remaining);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function TurnResponseBody({
|
||||
function buildTurnBodyLines({
|
||||
turn,
|
||||
width,
|
||||
height,
|
||||
scrollOffset,
|
||||
loading,
|
||||
status,
|
||||
spinIdx,
|
||||
pendingPermission,
|
||||
permissionIdx,
|
||||
expandedToolCall,
|
||||
}: {
|
||||
turn: Turn;
|
||||
width: number;
|
||||
height: number;
|
||||
scrollOffset: number;
|
||||
loading: boolean;
|
||||
status: string;
|
||||
spinIdx: number;
|
||||
pendingPermission: PendingPermission | null;
|
||||
permissionIdx: number;
|
||||
}) {
|
||||
const allLines: React.ReactNode[] = [];
|
||||
const proseWidth = Math.min(width - CONTENT_INDENT - 1, MAX_PROSE_WIDTH);
|
||||
expandedToolCall: string | null;
|
||||
}): React.ReactNode[] {
|
||||
const toolCallIds = turn.toolCallOrder;
|
||||
const toolCalls = turn.toolCalls;
|
||||
const featuredId = findFeaturedToolCallId(toolCallIds, toolCalls);
|
||||
|
||||
// blank line between user prompt and response content
|
||||
allLines.push(<Box key="gap-top" height={1} />);
|
||||
const lines: React.ReactNode[] = [];
|
||||
|
||||
for (const tc of turn.toolCalls) {
|
||||
allLines.push(
|
||||
<ToolBadge key={`tc-${allLines.length}`} title={tc} width={width} />,
|
||||
);
|
||||
lines.push(<Box key="gap-top" height={1} />);
|
||||
|
||||
for (const tcId of toolCallIds) {
|
||||
const tc = toolCalls.get(tcId);
|
||||
if (!tc) continue;
|
||||
|
||||
if (tcId === featuredId || expandedToolCall === tcId) {
|
||||
const cardLines = buildToolCallCardLines(tc, CONTENT_INDENT, width, expandedToolCall === tcId, `tc-${tcId}`);
|
||||
lines.push(...cardLines);
|
||||
} else {
|
||||
lines.push(
|
||||
<ToolCallCompact
|
||||
key={`tc-${tcId}`}
|
||||
info={tc}
|
||||
indent={CONTENT_INDENT}
|
||||
width={width}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (turn.agentText) {
|
||||
// visual break between tool calls and prose
|
||||
if (turn.toolCalls.length > 0) {
|
||||
allLines.push(<Box key="gap-tools" height={1} />);
|
||||
if (toolCallIds.length > 0) {
|
||||
lines.push(<Box key="gap-agent" height={1} />);
|
||||
}
|
||||
const wrapped = wrapText(turn.agentText, proseWidth);
|
||||
for (const line of wrapped) {
|
||||
allLines.push(
|
||||
<Box key={`al-${allLines.length}`} paddingLeft={CONTENT_INDENT}>
|
||||
<Text color={TEXT_PRIMARY}>{line}</Text>
|
||||
const rendered = renderMarkdown(turn.agentText);
|
||||
const mdLines = rendered.split("\n");
|
||||
for (let i = 0; i < mdLines.length; i++) {
|
||||
lines.push(
|
||||
<Box key={`md-${i}`} paddingLeft={CONTENT_INDENT}>
|
||||
<Text>{mdLines[i]}</Text>
|
||||
</Box>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (loading && !pendingPermission) {
|
||||
allLines.push(
|
||||
<Box key={`load-${allLines.length}`} paddingLeft={CONTENT_INDENT} marginTop={turn.agentText ? 0 : 0}>
|
||||
lines.push(
|
||||
<Box key="loading" paddingLeft={CONTENT_INDENT}>
|
||||
<Spinner idx={spinIdx} />
|
||||
<Text color={TEXT_DIM} italic>
|
||||
{" "}
|
||||
@@ -424,9 +382,9 @@ function TurnResponseBody({
|
||||
}
|
||||
|
||||
if (pendingPermission) {
|
||||
allLines.push(
|
||||
lines.push(
|
||||
<PermissionDialog
|
||||
key={`perm-${allLines.length}`}
|
||||
key="permission"
|
||||
toolTitle={pendingPermission.toolTitle}
|
||||
options={pendingPermission.options}
|
||||
selectedIdx={permissionIdx}
|
||||
@@ -435,25 +393,64 @@ function TurnResponseBody({
|
||||
);
|
||||
}
|
||||
|
||||
const totalLines = allLines.length;
|
||||
const visibleCount = Math.max(height, 1);
|
||||
const maxOffset = Math.max(totalLines - visibleCount, 0);
|
||||
const offset = Math.min(Math.max(scrollOffset, 0), maxOffset);
|
||||
const visible = allLines.slice(offset, offset + visibleCount);
|
||||
const hasAbove = offset > 0;
|
||||
const hasBelow = offset + visibleCount < totalLines;
|
||||
return lines;
|
||||
}
|
||||
|
||||
function ScrollableBody({
|
||||
lines,
|
||||
width,
|
||||
scrollOffset,
|
||||
}: {
|
||||
lines: React.ReactNode[];
|
||||
width: number;
|
||||
scrollOffset: number;
|
||||
}) {
|
||||
const ref = useRef<DOMElement>(null);
|
||||
const [measured, setMeasured] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
const { height } = measureElement(ref.current);
|
||||
if (height !== measured) setMeasured(height);
|
||||
}
|
||||
});
|
||||
|
||||
const total = lines.length;
|
||||
const availableHeight = measured || total;
|
||||
const needsScroll = total > availableHeight;
|
||||
const viewSize = needsScroll
|
||||
? Math.max(availableHeight - 2, 1)
|
||||
: availableHeight;
|
||||
const maxOffset = Math.max(total - viewSize, 0);
|
||||
const clampedOffset = Math.min(Math.max(scrollOffset, 0), maxOffset);
|
||||
const endIdx = total - clampedOffset;
|
||||
const startIdx = Math.max(endIdx - viewSize, 0);
|
||||
const visible = lines.slice(startIdx, endIdx);
|
||||
|
||||
const hiddenAbove = startIdx;
|
||||
const hiddenBelow = Math.max(total - endIdx, 0);
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" height={height} overflowY="hidden">
|
||||
{hasAbove && (
|
||||
<Box justifyContent="center" width={width}>
|
||||
<Text color={TEXT_DIM}>▲ more</Text>
|
||||
<Box ref={ref} flexDirection="column" flexGrow={1}>
|
||||
{needsScroll && (
|
||||
<Box justifyContent="center" width={width} height={1}>
|
||||
{hiddenAbove > 0 ? (
|
||||
<Text color={TEXT_DIM}>▲ {hiddenAbove} more (↑)</Text>
|
||||
) : (
|
||||
<Text> </Text>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
{visible}
|
||||
{hasBelow && !hasAbove && visible.length > 1 && (
|
||||
<Box justifyContent="center" width={width}>
|
||||
<Text color={TEXT_DIM}>▼ more</Text>
|
||||
<Box flexDirection="column" flexGrow={1} overflowY="hidden">
|
||||
{visible}
|
||||
</Box>
|
||||
{needsScroll && (
|
||||
<Box justifyContent="center" width={width} height={1}>
|
||||
{hiddenBelow > 0 ? (
|
||||
<Text color={TEXT_DIM}>▼ {hiddenBelow} more (↓)</Text>
|
||||
) : (
|
||||
<Text> </Text>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
@@ -484,9 +481,7 @@ function SplashScreen({
|
||||
onInputSubmit: (v: string) => void;
|
||||
}) {
|
||||
const frame = GOOSE_FRAMES[animFrame % GOOSE_FRAMES.length]!;
|
||||
const isError =
|
||||
status.startsWith("error") || status.startsWith("failed");
|
||||
const statusColor = status === "ready" ? TEAL : isError ? CRANBERRY : TEXT_DIM;
|
||||
const statusColor = status === "ready" ? TEAL : isErrorStatus(status) ? CRANBERRY : TEXT_DIM;
|
||||
const inputWidth = Math.min(56, width - 8);
|
||||
|
||||
return (
|
||||
@@ -497,7 +492,6 @@ function SplashScreen({
|
||||
width={width}
|
||||
height={height}
|
||||
>
|
||||
{/* Goose art */}
|
||||
<Box flexDirection="column" alignItems="center">
|
||||
{frame.map((line, i) => (
|
||||
<Text key={i} color={TEXT_PRIMARY}>
|
||||
@@ -506,7 +500,6 @@ function SplashScreen({
|
||||
))}
|
||||
</Box>
|
||||
|
||||
{/* Title + subtitle */}
|
||||
<Box marginTop={1}>
|
||||
<Text color={TEXT_PRIMARY} bold>
|
||||
goose
|
||||
@@ -514,7 +507,6 @@ function SplashScreen({
|
||||
</Box>
|
||||
<Text color={TEXT_DIM}>your on-machine AI agent</Text>
|
||||
|
||||
{/* Input or status */}
|
||||
{showInput ? (
|
||||
<Box flexDirection="column" alignItems="center" marginTop={2}>
|
||||
<Box width={inputWidth}>
|
||||
@@ -546,7 +538,7 @@ function SplashScreen({
|
||||
);
|
||||
}
|
||||
|
||||
export default function App({
|
||||
function App({
|
||||
serverUrl,
|
||||
initialPrompt,
|
||||
}: {
|
||||
@@ -571,6 +563,7 @@ export default function App({
|
||||
const [queuedMessages, setQueuedMessages] = useState<string[]>([]);
|
||||
|
||||
const [viewTurnIdx, setViewTurnIdx] = useState(-1);
|
||||
const [expandedToolCall, setExpandedToolCall] = useState<string | null>(null);
|
||||
const [scrollOffset, setScrollOffset] = useState(0);
|
||||
|
||||
const clientRef = useRef<GooseClient | null>(null);
|
||||
@@ -592,10 +585,10 @@ export default function App({
|
||||
if (turns.length > 0) setBannerVisible(false);
|
||||
}, [turns]);
|
||||
|
||||
const turnsLen = turns.length;
|
||||
useEffect(() => {
|
||||
if (viewTurnIdx === -1) setScrollOffset(0);
|
||||
}, [turnsLen, viewTurnIdx]);
|
||||
setExpandedToolCall(null);
|
||||
setScrollOffset(0);
|
||||
}, [viewTurnIdx, turns.length]);
|
||||
|
||||
const appendAgent = useCallback((text: string) => {
|
||||
setTurns((prev) => {
|
||||
@@ -606,21 +599,89 @@ export default function App({
|
||||
});
|
||||
}, []);
|
||||
|
||||
const appendToolCall = useCallback((title: string) => {
|
||||
setTurns((prev) => {
|
||||
if (prev.length === 0) return prev;
|
||||
const last = { ...prev[prev.length - 1]! };
|
||||
last.toolCalls = [...last.toolCalls, title];
|
||||
return [...prev.slice(0, -1), last];
|
||||
});
|
||||
}, []);
|
||||
const handleToolCall = useCallback(
|
||||
(tc: {
|
||||
toolCallId: string;
|
||||
title: string;
|
||||
status?: ToolCallStatus;
|
||||
kind?: ToolKind;
|
||||
rawInput?: unknown;
|
||||
rawOutput?: unknown;
|
||||
content?: ToolCallContent[];
|
||||
locations?: Array<{ path: string; line?: number | null }>;
|
||||
}) => {
|
||||
setTurns((prev) => {
|
||||
if (prev.length === 0) return prev;
|
||||
const last = { ...prev[prev.length - 1]! };
|
||||
const newMap = new Map(last.toolCalls);
|
||||
const info: ToolCallInfo = {
|
||||
toolCallId: tc.toolCallId,
|
||||
title: tc.title,
|
||||
status: tc.status ?? "pending",
|
||||
kind: tc.kind,
|
||||
rawInput: tc.rawInput,
|
||||
rawOutput: tc.rawOutput,
|
||||
content: tc.content,
|
||||
locations: tc.locations,
|
||||
};
|
||||
newMap.set(tc.toolCallId, info);
|
||||
const newOrder = last.toolCallOrder.includes(tc.toolCallId)
|
||||
? last.toolCallOrder
|
||||
: [...last.toolCallOrder, tc.toolCallId];
|
||||
return [
|
||||
...prev.slice(0, -1),
|
||||
{ ...last, toolCalls: newMap, toolCallOrder: newOrder },
|
||||
];
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const handleToolCallUpdate = useCallback(
|
||||
(update: {
|
||||
toolCallId: string;
|
||||
title?: string | null;
|
||||
status?: ToolCallStatus | null;
|
||||
kind?: ToolKind | null;
|
||||
rawInput?: unknown;
|
||||
rawOutput?: unknown;
|
||||
content?: ToolCallContent[] | null;
|
||||
locations?: Array<{ path: string; line?: number | null }> | null;
|
||||
}) => {
|
||||
setTurns((prev) => {
|
||||
if (prev.length === 0) return prev;
|
||||
const last = { ...prev[prev.length - 1]! };
|
||||
const newMap = new Map(last.toolCalls);
|
||||
const existing = newMap.get(update.toolCallId);
|
||||
if (!existing) return prev;
|
||||
const updated: ToolCallInfo = { ...existing };
|
||||
if (update.title != null) updated.title = update.title;
|
||||
if (update.status != null) updated.status = update.status;
|
||||
if (update.kind != null) updated.kind = update.kind;
|
||||
if (update.rawInput !== undefined) updated.rawInput = update.rawInput;
|
||||
if (update.rawOutput !== undefined)
|
||||
updated.rawOutput = update.rawOutput;
|
||||
if (update.content != null) updated.content = update.content;
|
||||
if (update.locations != null) updated.locations = update.locations;
|
||||
newMap.set(update.toolCallId, updated);
|
||||
return [...prev.slice(0, -1), { ...last, toolCalls: newMap }];
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const addUserTurn = useCallback((text: string) => {
|
||||
setTurns((prev) => [
|
||||
...prev,
|
||||
{ userText: text, toolCalls: [], agentText: "" },
|
||||
{
|
||||
userText: text,
|
||||
toolCalls: new Map(),
|
||||
toolCallOrder: [],
|
||||
agentText: "",
|
||||
},
|
||||
]);
|
||||
setViewTurnIdx(-1);
|
||||
setExpandedToolCall(null);
|
||||
setScrollOffset(0);
|
||||
}, []);
|
||||
|
||||
@@ -641,48 +702,7 @@ export default function App({
|
||||
[pendingPermission],
|
||||
);
|
||||
|
||||
const processQueue = useCallback(async () => {
|
||||
if (isProcessingRef.current) return;
|
||||
isProcessingRef.current = true;
|
||||
|
||||
while (queueRef.current.length > 0) {
|
||||
const next = queueRef.current.shift()!;
|
||||
setQueuedMessages([...queueRef.current]);
|
||||
|
||||
const client = clientRef.current;
|
||||
const sid = sessionIdRef.current;
|
||||
if (!client || !sid) break;
|
||||
|
||||
addUserTurn(next);
|
||||
setLoading(true);
|
||||
setStatus("thinking…");
|
||||
streamBuf.current = "";
|
||||
|
||||
try {
|
||||
const result = await client.prompt({
|
||||
sessionId: sid,
|
||||
prompt: [{ type: "text", text: next }],
|
||||
});
|
||||
|
||||
if (streamBuf.current) appendAgent("");
|
||||
|
||||
setStatus(
|
||||
result.stopReason === "end_turn"
|
||||
? "ready"
|
||||
: `stopped: ${result.stopReason}`,
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
const errMsg = e instanceof Error ? e.message : String(e);
|
||||
setStatus(`error: ${errMsg}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
isProcessingRef.current = false;
|
||||
}, [appendAgent, addUserTurn]);
|
||||
|
||||
const sendPrompt = useCallback(
|
||||
const executePrompt = useCallback(
|
||||
async (text: string) => {
|
||||
const client = clientRef.current;
|
||||
const sid = sessionIdRef.current;
|
||||
@@ -711,10 +731,30 @@ export default function App({
|
||||
setStatus(`error: ${errMsg}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
if (queueRef.current.length > 0) processQueue();
|
||||
}
|
||||
},
|
||||
[appendAgent, addUserTurn, processQueue],
|
||||
[appendAgent, addUserTurn],
|
||||
);
|
||||
|
||||
const processQueue = useCallback(async () => {
|
||||
if (isProcessingRef.current) return;
|
||||
isProcessingRef.current = true;
|
||||
|
||||
while (queueRef.current.length > 0) {
|
||||
const next = queueRef.current.shift()!;
|
||||
setQueuedMessages([...queueRef.current]);
|
||||
await executePrompt(next);
|
||||
}
|
||||
|
||||
isProcessingRef.current = false;
|
||||
}, [executePrompt]);
|
||||
|
||||
const sendPrompt = useCallback(
|
||||
async (text: string) => {
|
||||
await executePrompt(text);
|
||||
if (queueRef.current.length > 0) processQueue();
|
||||
},
|
||||
[executePrompt, processQueue],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -723,7 +763,6 @@ export default function App({
|
||||
(async () => {
|
||||
try {
|
||||
setStatus("initializing…");
|
||||
const stream = createHttpStream(serverUrl);
|
||||
|
||||
const client = new GooseClient(
|
||||
() => ({
|
||||
@@ -736,7 +775,27 @@ export default function App({
|
||||
appendAgent(update.content.text);
|
||||
}
|
||||
} else if (update.sessionUpdate === "tool_call") {
|
||||
appendToolCall(update.title || "tool");
|
||||
handleToolCall({
|
||||
toolCallId: update.toolCallId,
|
||||
title: update.title,
|
||||
status: update.status,
|
||||
kind: update.kind,
|
||||
rawInput: update.rawInput,
|
||||
rawOutput: update.rawOutput,
|
||||
content: update.content,
|
||||
locations: update.locations,
|
||||
});
|
||||
} else if (update.sessionUpdate === "tool_call_update") {
|
||||
handleToolCallUpdate({
|
||||
toolCallId: update.toolCallId,
|
||||
title: update.title,
|
||||
status: update.status,
|
||||
kind: update.kind,
|
||||
rawInput: update.rawInput,
|
||||
rawOutput: update.rawOutput,
|
||||
content: update.content,
|
||||
locations: update.locations,
|
||||
});
|
||||
}
|
||||
},
|
||||
requestPermission: async (
|
||||
@@ -754,7 +813,7 @@ export default function App({
|
||||
});
|
||||
},
|
||||
}),
|
||||
stream,
|
||||
serverUrl,
|
||||
);
|
||||
|
||||
if (cancelled) return;
|
||||
@@ -783,7 +842,7 @@ export default function App({
|
||||
if (initialPrompt && !sentInitialPrompt.current) {
|
||||
sentInitialPrompt.current = true;
|
||||
await sendPrompt(initialPrompt);
|
||||
if (initialPrompt) setTimeout(() => exit(), 100);
|
||||
setTimeout(() => exit(), 100);
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
if (cancelled) return;
|
||||
@@ -796,7 +855,15 @@ export default function App({
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [serverUrl, initialPrompt, sendPrompt, appendAgent, appendToolCall, exit]);
|
||||
}, [
|
||||
serverUrl,
|
||||
initialPrompt,
|
||||
sendPrompt,
|
||||
appendAgent,
|
||||
handleToolCall,
|
||||
handleToolCallUpdate,
|
||||
exit,
|
||||
]);
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
(value: string) => {
|
||||
@@ -804,6 +871,7 @@ export default function App({
|
||||
if (!trimmed) return;
|
||||
setInput("");
|
||||
setViewTurnIdx(-1);
|
||||
setExpandedToolCall(null);
|
||||
setScrollOffset(0);
|
||||
|
||||
if (loading || isProcessingRef.current) {
|
||||
@@ -825,7 +893,6 @@ export default function App({
|
||||
exit();
|
||||
}
|
||||
|
||||
// Permission navigation
|
||||
if (pendingPermission) {
|
||||
const opts = pendingPermission.options;
|
||||
|
||||
@@ -857,13 +924,34 @@ export default function App({
|
||||
return;
|
||||
}
|
||||
|
||||
// Turn navigation: shift+arrow
|
||||
if (key.tab) {
|
||||
const effectiveIdx =
|
||||
viewTurnIdx === -1 ? turns.length - 1 : viewTurnIdx;
|
||||
const currentTurn = turns[effectiveIdx];
|
||||
if (!currentTurn || currentTurn.toolCallOrder.length === 0) return;
|
||||
|
||||
const featuredId = findFeaturedToolCallId(currentTurn.toolCallOrder, currentTurn.toolCalls);
|
||||
if (!featuredId) return;
|
||||
|
||||
setExpandedToolCall((prev) => (prev === featuredId ? null : featuredId));
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.upArrow && !key.shift && !key.meta) {
|
||||
setScrollOffset((prev) => prev + 3);
|
||||
return;
|
||||
}
|
||||
if (key.downArrow && !key.shift && !key.meta) {
|
||||
setScrollOffset((prev) => Math.max(prev - 3, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.upArrow && key.shift) {
|
||||
setTurns((currentTurns) => {
|
||||
if (currentTurns.length <= 1) return currentTurns;
|
||||
setViewTurnIdx((prev) => {
|
||||
const effectiveIdx = prev === -1 ? currentTurns.length - 1 : prev;
|
||||
setScrollOffset(0);
|
||||
const effectiveIdx =
|
||||
prev === -1 ? currentTurns.length - 1 : prev;
|
||||
return Math.max(effectiveIdx - 1, 0);
|
||||
});
|
||||
return currentTurns;
|
||||
@@ -876,44 +964,16 @@ export default function App({
|
||||
setViewTurnIdx((prev) => {
|
||||
if (prev === -1) return -1;
|
||||
const next = prev + 1;
|
||||
setScrollOffset(0);
|
||||
return next >= currentTurns.length ? -1 : next;
|
||||
});
|
||||
return currentTurns;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Scroll within turn
|
||||
if (key.pageUp || (key.upArrow && key.meta)) {
|
||||
setScrollOffset((prev) => Math.max(prev - 5, 0));
|
||||
return;
|
||||
}
|
||||
if (key.pageDown || (key.downArrow && key.meta)) {
|
||||
setScrollOffset((prev) => prev + 5);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// ── Layout math ───────────────────────────────────────────────────────
|
||||
//
|
||||
// The vertical budget is:
|
||||
// header (2 lines: title row + rule)
|
||||
// user prompt (2 lines: blank line above + prompt text)
|
||||
// body (flex: remaining space)
|
||||
// input bar (dynamic: border top/bottom + wrapped content lines + margin bottom) — absent in pipe mode
|
||||
|
||||
const GUTTER = 2;
|
||||
const innerWidth = Math.max(termWidth - GUTTER * 2, 20);
|
||||
const headerLines = 2;
|
||||
const userPromptLines = 2;
|
||||
const inputLines = initialPrompt
|
||||
? 0
|
||||
: inputBarHeight(input, innerWidth, queuedMessages.length > 0);
|
||||
const bodyHeight = Math.max(
|
||||
termHeight - headerLines - userPromptLines - inputLines - 1,
|
||||
3,
|
||||
);
|
||||
|
||||
if (bannerVisible) {
|
||||
return (
|
||||
@@ -941,6 +1001,33 @@ export default function App({
|
||||
viewTurnIdx !== -1 && viewTurnIdx < turns.length - 1;
|
||||
const isLatest = !isViewingHistory;
|
||||
|
||||
const emptyTurn: Turn = {
|
||||
userText: "",
|
||||
toolCalls: new Map(),
|
||||
toolCallOrder: [],
|
||||
agentText: "",
|
||||
};
|
||||
|
||||
const bodyLines = buildTurnBodyLines({
|
||||
turn: currentTurn ?? emptyTurn,
|
||||
width: innerWidth,
|
||||
loading: isLatest && loading,
|
||||
status,
|
||||
spinIdx,
|
||||
pendingPermission: isLatest ? pendingPermission : null,
|
||||
permissionIdx,
|
||||
expandedToolCall,
|
||||
});
|
||||
|
||||
const allBodyLines = isLatest
|
||||
? [
|
||||
...bodyLines,
|
||||
...queuedMessages.map((text, i) => (
|
||||
<QueuedMessage key={`q-${i}`} text={text} />
|
||||
)),
|
||||
]
|
||||
: bodyLines;
|
||||
|
||||
return (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
@@ -965,36 +1052,14 @@ export default function App({
|
||||
<>
|
||||
<UserPrompt text={currentTurn.userText} />
|
||||
|
||||
<Box flexDirection="column" flexGrow={1} height={bodyHeight}>
|
||||
<TurnResponseBody
|
||||
turn={currentTurn}
|
||||
width={innerWidth}
|
||||
height={
|
||||
bodyHeight -
|
||||
(isLatest && queuedMessages.length > 0
|
||||
? queuedMessages.length
|
||||
: 0)
|
||||
}
|
||||
scrollOffset={scrollOffset}
|
||||
loading={isLatest && loading}
|
||||
status={status}
|
||||
spinIdx={spinIdx}
|
||||
pendingPermission={isLatest ? pendingPermission : null}
|
||||
permissionIdx={permissionIdx}
|
||||
/>
|
||||
|
||||
{isLatest &&
|
||||
queuedMessages.map((text, i) => (
|
||||
<QueuedMessage key={`q-${i}`} text={text} />
|
||||
))}
|
||||
</Box>
|
||||
<ScrollableBody
|
||||
lines={allBodyLines}
|
||||
width={innerWidth}
|
||||
scrollOffset={scrollOffset}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
flexGrow={1}
|
||||
height={bodyHeight + userPromptLines}
|
||||
/>
|
||||
<Box flexDirection="column" flexGrow={1} />
|
||||
)}
|
||||
|
||||
{isViewingHistory && (
|
||||
@@ -1022,3 +1087,123 @@ export default function App({
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const cli = meow(
|
||||
`
|
||||
Usage
|
||||
$ goose
|
||||
|
||||
Options
|
||||
--server, -s Server URL (default: auto-launch bundled server)
|
||||
--text, -t Send a single prompt and exit
|
||||
`,
|
||||
{
|
||||
importMeta: import.meta,
|
||||
flags: {
|
||||
server: { type: "string", shortFlag: "s" },
|
||||
text: { type: "string", shortFlag: "t" },
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const DEFAULT_PORT = 3284;
|
||||
const DEFAULT_URL = `http://127.0.0.1:${DEFAULT_PORT}`;
|
||||
|
||||
function findServerBinary(): string | null {
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const candidates = [
|
||||
join(__dirname, "..", "server-binary.json"),
|
||||
join(__dirname, "server-binary.json"),
|
||||
];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
try {
|
||||
const data = JSON.parse(readFileSync(candidate, "utf-8"));
|
||||
return data.binaryPath ?? null;
|
||||
} catch {
|
||||
// not found here, try next
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function waitForServer(url: string, timeoutMs = 10_000): Promise<void> {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
try {
|
||||
const res = await fetch(`${url}/status`);
|
||||
if (res.ok) return;
|
||||
} catch {
|
||||
// server not ready yet
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 200));
|
||||
}
|
||||
throw new Error(
|
||||
`Server did not become ready at ${url} within ${timeoutMs}ms`,
|
||||
);
|
||||
}
|
||||
|
||||
let serverProcess: ReturnType<typeof spawn> | null = null;
|
||||
|
||||
async function main() {
|
||||
let serverUrl = cli.flags.server;
|
||||
|
||||
if (!serverUrl) {
|
||||
const binary = findServerBinary();
|
||||
if (binary) {
|
||||
serverProcess = spawn(binary, ["--port", String(DEFAULT_PORT)], {
|
||||
stdio: "ignore",
|
||||
detached: false,
|
||||
});
|
||||
|
||||
serverProcess.on("error", (err) => {
|
||||
console.error(`Failed to start goose-acp-server: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
try {
|
||||
await waitForServer(DEFAULT_URL);
|
||||
} catch (err) {
|
||||
console.error((err as Error).message);
|
||||
serverProcess.kill();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
serverUrl = DEFAULT_URL;
|
||||
} else {
|
||||
serverUrl = DEFAULT_URL;
|
||||
}
|
||||
}
|
||||
|
||||
const { waitUntilExit } = render(
|
||||
<App serverUrl={serverUrl} initialPrompt={cli.flags.text} />,
|
||||
);
|
||||
|
||||
await waitUntilExit();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
if (serverProcess && !serverProcess.killed) {
|
||||
serverProcess.kill();
|
||||
}
|
||||
}
|
||||
|
||||
process.on("exit", cleanup);
|
||||
process.on("SIGINT", () => {
|
||||
cleanup();
|
||||
process.exit(0);
|
||||
});
|
||||
process.on("SIGTERM", () => {
|
||||
cleanup();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
cleanup();
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user