From bf5b109c1f94fa75bb10df37323de60797bbf53f Mon Sep 17 00:00:00 2001 From: Jay V Date: Thu, 7 Aug 2025 18:51:54 -0400 Subject: [PATCH 001/178] docs: edit agent doc --- packages/web/src/content/docs/docs/agents.mdx | 287 +++++++-------- packages/web/src/content/docs/docs/modes.mdx | 331 ++++++++++++++++++ 2 files changed, 479 insertions(+), 139 deletions(-) create mode 100644 packages/web/src/content/docs/docs/modes.mdx diff --git a/packages/web/src/content/docs/docs/agents.mdx b/packages/web/src/content/docs/docs/agents.mdx index 950f910fd..10c37e045 100644 --- a/packages/web/src/content/docs/docs/agents.mdx +++ b/packages/web/src/content/docs/docs/agents.mdx @@ -3,75 +3,108 @@ title: Agents description: Configure and use specialized agents in opencode. --- +agents allow opencode to operate different for specific tasks +you can cycle them with tab, or @ to delegate a subtask to them +explain built in build/plan agent +explain how to make your own agent +examples + Agents are specialized AI assistants that can be configured for specific tasks and workflows. They allow you to create focused tools with custom prompts, models, and tool access. :::tip Use the plan agent to analyze code and review suggestions without making any code changes. ::: -You can switch between agents during a session or configure them in your config file. +You can switch between agents during a session or invoke them with the `@` mention. --- -## Agent Types +## Types -opencode has two types of agents: +There are two types of agents in opencode; primary agents and subagents. -### Primary Agents +--- -Primary agents are the main assistants you interact with directly. You can cycle through them using the **Tab** key (or your configured `switch_agent` keybind). These agents handle your main conversation and can access all configured tools. +### Primary agents -**Built-in Primary Agents:** +Primary agents are the main assistants you interact with directly. You can cycle through them using the **Tab** key, or your configured `switch_agent` keybind. These agents handle your main conversation and can access all configured tools. -- **Build** - The default agent with all tools enabled. Standard for development work where you need full access to file operations and system commands. -- **Plan** - A restricted agent for planning and analysis. Has `write`, `edit`, `patch`, and `bash` tools disabled by default. Useful for analyzing code and suggesting changes without making modifications. +:::tip +You can use the **Tab** key to switch between primary agents during a session. +::: + +opencode comes with two built-in primary agents, **Build** and **Plan**. We'll +look at these below. + +--- ### Subagents -Subagents are specialized assistants that primary agents can invoke for specific tasks. You can also manually invoke them by **@ mentioning** them in your messages (e.g., `@general help me search for this function`). +Subagents are specialized assistants that primary agents can invoke for specific tasks. You can also manually invoke them by **@ mentioning** them in your messages. -**Built-in Subagents:** - -- **General** - General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. Use when searching for keywords or files and you're not confident you'll find the right match in the first few tries. +opencode comes with one built-in subagent, **General**. We'll look at this below. --- -## Using Agents +## Built-in -### Switching Primary Agents - -Use the **Tab** key to cycle through available primary agents during a session. You can also use your configured `switch_agent` keybind. - -### Invoking Subagents - -- **Automatic**: Primary agents will automatically use subagents for specialized tasks based on their descriptions -- **Manual**: @ mention a subagent in your message: `@general search for authentication code` - -See also: [Formatters](/docs/formatters) for information about code formatting configuration. +opencode comes with two built-in primary agents and one built-in subagent. --- -## Creating Agents +### Build -You can create new agents using the `opencode agent create` command. This interactive command will: +_Mode_: `primary` -1. Ask where to save the agent (global or project-specific) -2. Prompt for a description of what the agent should do -3. Generate an appropriate system prompt and identifier -4. Let you select which tools the agent can access -5. Create a markdown file with the agent configuration +Build is the **default** primary agent with all tools enabled. This is the standard agent for development work where you need full access to file operations and system commands. -```bash -opencode agent create -``` +--- -The command will guide you through the process and automatically generate a well-structured agent based on your requirements. +### Plan -## Configuration +_Mode_: `primary` + +A restricted agent designed for planning and analysis. In the plan agent, the following tools are disabled by default: + +- `write` - Cannot create new files +- `edit` - Cannot modify existing files +- `patch` - Cannot apply patches +- `bash` - Cannot execute shell commands + +This agent is useful when you want the LLM to analyze code, suggest changes, or create plans without making any actual modifications to your codebase. + +--- + +### General + +_Mode_: `subagent` + +A general-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. Use when searching for keywords or files and you're not confident you'll find the right match in the first few tries. + +--- + +## Usage + +1. For primary agents, use the **Tab** key to cycle through them during a session. You can also use your configured `switch_agent` keybind. + +2. Subagents can be invoked: + + - **Automatically** by primary agents for specialized tasks based on their descriptions. + - Manually by **@ mentioning** a subagent in your message. For example. + + ```txt frame="none" + @general help me search for this function + ``` + +--- + +## Configure You can customize the built-in agents or create your own through configuration. Agents can be configured in two ways: -### JSON Configuration +--- + +### JSON Configure agents in your `opencode.json` config file: @@ -112,12 +145,14 @@ Configure agents in your `opencode.json` config file: } ``` -### Markdown Configuration +--- + +### Markdown You can also define agents using markdown files. Place them in: - Global: `~/.config/opencode/agent/` -- Project: `.opencode/agent/` +- Per-project: `.opencode/agent/` ```markdown title="~/.config/opencode/agent/review.md" --- @@ -141,31 +176,41 @@ You are in code review mode. Focus on: Provide constructive feedback without making direct changes. ``` -The markdown file name becomes the agent name (e.g., `review.md` creates a `review` agent). +The markdown file name becomes the agent name. For example, `review.md` creates a `review` agent. Let's look at these configuration options in detail. --- -### Model +## Options -Use the `model` config to override the default model for this agent. Useful for using different models optimized for different tasks. For example, a faster model for planning, a more capable model for implementation. +Let's look at these configuration options in detail. + +--- + +### Description + +Use the `description` option to provide a brief description of what the agent does and when to use it. ```json title="opencode.json" { "agent": { - "plan": { - "model": "anthropic/claude-haiku-4-20250514" + "review": { + "description": "Reviews code for best practices and potential issues" } } } ``` +This is a **required** config option. + --- ### Temperature -Control the randomness and creativity of the AI's responses with the `temperature` config. Lower values make responses more focused and deterministic, while higher values increase creativity and variability. +Control the randomness and creativity of the LLM's responses with the `temperature` config. + +Lower values make responses more focused and deterministic, while higher values increase creativity and variability. ```json title="opencode.json" { @@ -204,7 +249,23 @@ Temperature values typically range from 0.0 to 1.0: } ``` -If no temperature is specified, opencode uses model-specific defaults (typically 0 for most models, 0.55 for Qwen models). +If no temperature is specified, opencode uses model-specific defaults; typically 0 for most models, 0.55 for Qwen models. + +--- + +### Disable + +Set to `true` to disable the agent. + +```json title="opencode.json" +{ + "agent": { + "review": { + "disable": true + } + } +} +``` --- @@ -226,26 +287,27 @@ This path is relative to where the config file is located. So this works for bot --- -## Agent Properties +### Model -### Required +Use the `model` config to override the default model for this agent. Useful for using different models optimized for different tasks. For example, a faster model for planning, a more capable model for implementation. -- **description** - Brief description of what the agent does and when to use it +```json title="opencode.json" +{ + "agent": { + "plan": { + "model": "anthropic/claude-haiku-4-20250514" + } + } +} +``` -### Optional - -- **model** - Specific model to use (defaults to your configured model) -- **prompt** - Custom system prompt for the agent -- **tools** - Object specifying which tools the agent can access (true/false for each tool) -- **temperature** - Control response randomness (0.0-1.0) -- **mode** - Agent type: `"primary"` (can be cycled with Tab), `"subagent"` (invoked by @ mention), or `"all"` (both) -- **disable** - Set to true to disable the agent +--- ### Tools Control which tools are available in this agent with the `tools` config. You can enable or disable specific tools by setting them to `true` or `false`. -```json +```json title="opencode.json" { "agent": { "readonly": { @@ -286,104 +348,43 @@ Here are all the tools can be controlled through the agent config. --- -## Tool Access +### Mode -By default, agents inherit the same tool access as the main assistant. You can restrict or enable specific tools as shown in the Tools section above. +Control the agent's mode with the `mode` config. The `mode` option is used to determine how the agent can be used. -## Agent Modes - -The `mode` property determines how an agent can be used: - -- **`"primary"`** - Can be cycled through with Tab key as your main assistant -- **`"subagent"`** - Can be invoked by @ mentioning or automatically by primary agents -- **`"all"`** - Can be used both as primary and subagent (default for custom agents) - -## Best Practices - -1. **Clear descriptions** - Write specific descriptions that help the main assistant know when to use each agent -2. **Focused prompts** - Keep agent prompts focused on their specific role -3. **Appropriate tool access** - Only give agents the tools they need for their tasks -4. **Consistent naming** - Use descriptive, consistent names for your agents -5. **Project-specific agents** - Use `.opencode/agent/` for project-specific workflows - -## Custom Agents - -You can create your own custom agents by adding them to the configuration. Here are examples using both approaches: - -### Using JSON configuration - -```json title="opencode.json" {4-14} +```json title="opencode.json" { - "$schema": "https://opencode.ai/config.json", "agent": { - "docs": { - "prompt": "{file:./prompts/documentation.txt}", - "tools": { - "write": true, - "edit": true, - "bash": false, - "read": true, - "grep": true, - "glob": true - } + "review": { + "mode": "subagent" } } } ``` -### Using markdown files +The `mode` option can be set to `primary`, `subagent`, or `all`. If no `mode` is specified, it defaults to `all`. -Create agent files in `.opencode/agent/` for project-specific agents or `~/.config/opencode/agent/` for global agents: - -```markdown title=".opencode/agent/debug.md" ---- -temperature: 0.1 -tools: - bash: true - read: true - grep: true - write: false - edit: false --- -You are in debug mode. Your primary goal is to help investigate and diagnose issues. +## Create agents -Focus on: +You can create new agents using the following command: -- Understanding the problem through careful analysis -- Using bash commands to inspect system state -- Reading relevant files and logs -- Searching for patterns and anomalies -- Providing clear explanations of findings - -Do not make any changes to files. Only investigate and report. +```bash +opencode agent create ``` -```markdown title="~/.config/opencode/agent/refactor.md" ---- -model: anthropic/claude-sonnet-4-20250514 -temperature: 0.2 -tools: - edit: true - read: true - grep: true - glob: true ---- +This interactive command will: -You are in refactoring mode. Focus on improving code quality without changing functionality. - -Priorities: - -- Improve code readability and maintainability -- Apply consistent naming conventions -- Reduce code duplication -- Optimize performance where appropriate -- Ensure all tests continue to pass -``` +1. Ask where to save the agent; global or project-specific. +2. Description of what the agent should do. +3. Generate an appropriate system prompt and identifier. +4. Let you select which tools the agent can access. +5. Finally, create a markdown file with the agent configuration. --- -### Use cases +## Use cases Here are some common use cases for different agents. @@ -393,13 +394,19 @@ Here are some common use cases for different agents. - **Debug agent**: Focused on investigation with bash and read tools enabled - **Docs agent**: Documentation writing with file operations but no system commands -You might also find different models are good for different use cases. - --- ## Examples -### Documentation Agent +Here are some examples agents you might find useful. + +:::tip +Do you have an agent you'd like to share? [Submit a PR](https://github.com/sst/opencode). +::: + +--- + +### Documentation agent ```markdown title="~/.config/opencode/agent/docs-writer.md" --- @@ -419,7 +426,9 @@ Focus on: - User-friendly language ``` -### Security Auditor +--- + +### Security auditor ```markdown title="~/.config/opencode/agent/security-auditor.md" --- diff --git a/packages/web/src/content/docs/docs/modes.mdx b/packages/web/src/content/docs/docs/modes.mdx new file mode 100644 index 000000000..ae14c2f32 --- /dev/null +++ b/packages/web/src/content/docs/docs/modes.mdx @@ -0,0 +1,331 @@ +--- +title: Modes +description: Different modes for different use cases. +--- + +:::caution +Modes are now configured through the `agent` option in the opencode config. The +`mode` option is now deprecated. [Learn more](/docs/agents). +::: + +Modes in opencode allow you to customize the behavior, tools, and prompts for different use cases. + +It comes with two built-in modes: **build** and **plan**. You can customize +these or configure your own through the opencode config. + +You can switch between modes during a session or configure them in your config file. + +--- + +## Built-in + +opencode comes with two built-in modes. + +--- + +### Build + +Build is the **default** mode with all tools enabled. This is the standard mode for development work where you need full access to file operations and system commands. + +--- + +### Plan + +A restricted mode designed for planning and analysis. In plan mode, the following tools are disabled by default: + +- `write` - Cannot create new files +- `edit` - Cannot modify existing files +- `patch` - Cannot apply patches +- `bash` - Cannot execute shell commands + +This mode is useful when you want the AI to analyze code, suggest changes, or create plans without making any actual modifications to your codebase. + +--- + +## Switching + +You can switch between modes during a session using the _Tab_ key. Or your configured `switch_mode` keybind. + +See also: [Formatters](/docs/formatters) for information about code formatting configuration. + +--- + +## Configure + +You can customize the built-in modes or create your own through configuration. Modes can be configured in two ways: + +### JSON Configuration + +Configure modes in your `opencode.json` config file: + +```json title="opencode.json" +{ + "$schema": "https://opencode.ai/config.json", + "mode": { + "build": { + "model": "anthropic/claude-sonnet-4-20250514", + "prompt": "{file:./prompts/build.txt}", + "tools": { + "write": true, + "edit": true, + "bash": true + } + }, + "plan": { + "model": "anthropic/claude-haiku-4-20250514", + "tools": { + "write": false, + "edit": false, + "bash": false + } + } + } +} +``` + +### Markdown Configuration + +You can also define modes using markdown files. Place them in: + +- Global: `~/.config/opencode/mode/` +- Project: `.opencode/mode/` + +```markdown title="~/.config/opencode/mode/review.md" +--- +model: anthropic/claude-sonnet-4-20250514 +temperature: 0.1 +tools: + write: false + edit: false + bash: false +--- + +You are in code review mode. Focus on: + +- Code quality and best practices +- Potential bugs and edge cases +- Performance implications +- Security considerations + +Provide constructive feedback without making direct changes. +``` + +The markdown file name becomes the mode name (e.g., `review.md` creates a `review` mode). + +Let's look at these configuration options in detail. + +--- + +### Model + +Use the `model` config to override the default model for this mode. Useful for using different models optimized for different tasks. For example, a faster model for planning, a more capable model for implementation. + +```json title="opencode.json" +{ + "mode": { + "plan": { + "model": "anthropic/claude-haiku-4-20250514" + } + } +} +``` + +--- + +### Temperature + +Control the randomness and creativity of the AI's responses with the `temperature` config. Lower values make responses more focused and deterministic, while higher values increase creativity and variability. + +```json title="opencode.json" +{ + "mode": { + "plan": { + "temperature": 0.1 + }, + "creative": { + "temperature": 0.8 + } + } +} +``` + +Temperature values typically range from 0.0 to 1.0: + +- **0.0-0.2**: Very focused and deterministic responses, ideal for code analysis and planning +- **0.3-0.5**: Balanced responses with some creativity, good for general development tasks +- **0.6-1.0**: More creative and varied responses, useful for brainstorming and exploration + +```json title="opencode.json" +{ + "mode": { + "analyze": { + "temperature": 0.1, + "prompt": "{file:./prompts/analysis.txt}" + }, + "build": { + "temperature": 0.3 + }, + "brainstorm": { + "temperature": 0.7, + "prompt": "{file:./prompts/creative.txt}" + } + } +} +``` + +If no temperature is specified, opencode uses model-specific defaults (typically 0 for most models, 0.55 for Qwen models). + +--- + +### Prompt + +Specify a custom system prompt file for this mode with the `prompt` config. The prompt file should contain instructions specific to the mode's purpose. + +```json title="opencode.json" +{ + "mode": { + "review": { + "prompt": "{file:./prompts/code-review.txt}" + } + } +} +``` + +This path is relative to where the config file is located. So this works for +both the global opencode config and the project specific config. + +--- + +### Tools + +Control which tools are available in this mode with the `tools` config. You can enable or disable specific tools by setting them to `true` or `false`. + +```json +{ + "mode": { + "readonly": { + "tools": { + "write": false, + "edit": false, + "bash": false, + "read": true, + "grep": true, + "glob": true + } + } + } +} +``` + +If no tools are specified, all tools are enabled by default. + +--- + +#### Available tools + +Here are all the tools can be controlled through the mode config. + +| Tool | Description | +| ----------- | ----------------------- | +| `bash` | Execute shell commands | +| `edit` | Modify existing files | +| `write` | Create new files | +| `read` | Read file contents | +| `grep` | Search file contents | +| `glob` | Find files by pattern | +| `list` | List directory contents | +| `patch` | Apply patches to files | +| `todowrite` | Manage todo lists | +| `todoread` | Read todo lists | +| `webfetch` | Fetch web content | + +--- + +## Custom modes + +You can create your own custom modes by adding them to the configuration. Here are examples using both approaches: + +### Using JSON configuration + +```json title="opencode.json" {4-14} +{ + "$schema": "https://opencode.ai/config.json", + "mode": { + "docs": { + "prompt": "{file:./prompts/documentation.txt}", + "tools": { + "write": true, + "edit": true, + "bash": false, + "read": true, + "grep": true, + "glob": true + } + } + } +} +``` + +### Using markdown files + +Create mode files in `.opencode/mode/` for project-specific modes or `~/.config/opencode/mode/` for global modes: + +```markdown title=".opencode/mode/debug.md" +--- +temperature: 0.1 +tools: + bash: true + read: true + grep: true + write: false + edit: false +--- + +You are in debug mode. Your primary goal is to help investigate and diagnose issues. + +Focus on: + +- Understanding the problem through careful analysis +- Using bash commands to inspect system state +- Reading relevant files and logs +- Searching for patterns and anomalies +- Providing clear explanations of findings + +Do not make any changes to files. Only investigate and report. +``` + +```markdown title="~/.config/opencode/mode/refactor.md" +--- +model: anthropic/claude-sonnet-4-20250514 +temperature: 0.2 +tools: + edit: true + read: true + grep: true + glob: true +--- + +You are in refactoring mode. Focus on improving code quality without changing functionality. + +Priorities: + +- Improve code readability and maintainability +- Apply consistent naming conventions +- Reduce code duplication +- Optimize performance where appropriate +- Ensure all tests continue to pass +``` + +--- + +### Use cases + +Here are some common use cases for different modes. + +- **Build mode**: Full development work with all tools enabled +- **Plan mode**: Analysis and planning without making changes +- **Review mode**: Code review with read-only access plus documentation tools +- **Debug mode**: Focused on investigation with bash and read tools enabled +- **Docs mode**: Documentation writing with file operations but no system commands + +You might also find different models are good for different use cases. From 160f839b250911edf06829f6496bb06260387c30 Mon Sep 17 00:00:00 2001 From: Jay V Date: Thu, 7 Aug 2025 19:24:08 -0400 Subject: [PATCH 002/178] docs: update cli --- packages/web/src/content/docs/docs/cli.mdx | 150 +++++++++++++++++---- 1 file changed, 123 insertions(+), 27 deletions(-) diff --git a/packages/web/src/content/docs/docs/cli.mdx b/packages/web/src/content/docs/docs/cli.mdx index 9d93c2330..69f2c39a6 100644 --- a/packages/web/src/content/docs/docs/cli.mdx +++ b/packages/web/src/content/docs/docs/cli.mdx @@ -101,22 +101,16 @@ Both the `/editor` and `/export` commands use the editor specified in your `EDIT - ```bash - export EDITOR=nano # or vim, code, etc. - ``` - To make it permanent, add this to your shell profile; `~/.bashrc`, `~/.zshrc`, etc. + ```bash export EDITOR=nano # or vim, code, etc. ``` To make it permanent, add this to your shell profile; + `~/.bashrc`, `~/.zshrc`, etc. - ```bash - set EDITOR=notepad # or code, vim, etc. - ``` - To make it permanent, use **System Properties** > **Environment Variables**. + ```bash set EDITOR=notepad # or code, vim, etc. ``` To make it permanent, use **System Properties** > **Environment + Variables**. - ```bash - $env:EDITOR = "notepad" # or "code", "vim", etc. - ``` - To make it permanent, add this to your PowerShell profile. + ```bash $env:EDITOR = "notepad" # or "code", "vim", etc. ``` To make it permanent, add this to your PowerShell + profile. @@ -136,28 +130,25 @@ The opencode CLI also has the following commands. --- -### run +### agent -Run opencode in non-interactive mode by passing a prompt directly. +Manage agents for opencode. ```bash -opencode run [message..] +opencode agent [command] ``` -This is useful for scripting, automation, or when you want a quick answer without launching the full TUI. For example. +--- -```bash "opencode run" -opencode run Explain the use of context in Go +#### create + +Create a new agent with custom configuration. + +```bash +opencode agent create ``` -#### Flags - -| Flag | Short | Description | -| ------------ | ----- | ------------------------------------------ | -| `--continue` | `-c` | Continue the last session | -| `--session` | `-s` | Session ID to continue | -| `--share` | | Share the session | -| `--model` | `-m` | Model to use in the form of provider/model | +This command will guide you through creating a new agent with a custom system prompt and tool configuration. --- @@ -209,6 +200,102 @@ opencode auth logout --- +### github + +Manage the GitHub agent for repository automation. + +```bash +opencode github [command] +``` + +--- + +#### install + +Install the GitHub agent in your repository. + +```bash +opencode github install +``` + +This sets up the necessary GitHub Actions workflow and guides you through the configuration process. [Learn more](/docs/github). + +--- + +#### run + +Run the GitHub agent. This is typically used in GitHub Actions. + +```bash +opencode github run +``` + +##### Flags + +| Flag | Description | +| --------- | ------------------------------------------------ | +| `--event` | GitHub mock event to run the agent for | +| `--token` | GitHub personal access token | + +--- + +### models + +List all available models from configured providers. + +```bash +opencode models +``` + +This command displays all models available across your configured providers in the format `provider/model`. + +--- + +### run + +Run opencode in non-interactive mode by passing a prompt directly. + +```bash +opencode run [message..] +``` + +This is useful for scripting, automation, or when you want a quick answer without launching the full TUI. For example. + +```bash "opencode run" +opencode run Explain the use of context in Go +``` + +#### Flags + +| Flag | Short | Description | +| ------------ | ----- | ------------------------------------------ | +| `--continue` | `-c` | Continue the last session | +| `--session` | `-s` | Session ID to continue | +| `--share` | | Share the session | +| `--model` | `-m` | Model to use in the form of provider/model | +| `--agent` | | Agent to use | + +--- + +### serve + +Start a headless opencode server for API access. + +```bash +opencode serve +``` + +This starts an HTTP server that provides API access to opencode functionality without the TUI interface. + +#### Flags + +| Flag | Short | Description | +| ------------ | ----- | ------------------------------------------ | +| `--port` | `-p` | Port to listen on | +| `--hostname` | `-h` | Hostname to listen on | + +--- + ### upgrade Updates opencode to the latest version or a specific version. @@ -229,17 +316,26 @@ To upgrade to a specific version. opencode upgrade v0.1.48 ``` +#### Flags + +| Flag | Short | Description | +| ---------- | ----- | --------------------------------------------------------- | +| `--method` | `-m` | The installation method that was used; curl, npm, pnpm, bun, brew | + --- ## Flags -The opencode CLI takes the following flags. +The opencode CLI takes the following global flags. | Flag | Short | Description | | -------------- | ----- | ------------------------------------------ | | `--help` | `-h` | Display help | | `--version` | | Print version number | | `--print-logs` | | Print logs to stderr | +| `--log-level` | | Log level (DEBUG, INFO, WARN, ERROR) | | `--prompt` | `-p` | Prompt to use | | `--model` | `-m` | Model to use in the form of provider/model | | `--mode` | | Mode to use | +| `--port` | | Port to listen on | +| `--hostname` | | Hostname to listen on | From 3e74107e368f236fd9dc05624d9667ff6bdc6431 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 7 Aug 2025 19:02:21 -0400 Subject: [PATCH 003/178] looser todo tool schema --- packages/opencode/src/tool/todo.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/tool/todo.ts b/packages/opencode/src/tool/todo.ts index adb0c5099..7a11470f0 100644 --- a/packages/opencode/src/tool/todo.ts +++ b/packages/opencode/src/tool/todo.ts @@ -5,8 +5,8 @@ import { App } from "../app/app" const TodoInfo = z.object({ content: z.string().describe("Brief description of the task"), - status: z.enum(["pending", "in_progress", "completed", "cancelled"]).describe("Current status of the task"), - priority: z.enum(["high", "medium", "low"]).describe("Priority level of the task"), + status: z.string().describe("Current status of the task: pending, in_progress, completed, cancelled"), + priority: z.string().describe("Priority level of the task: high, medium, low"), id: z.string().describe("Unique identifier for the todo item"), }) type TodoInfo = z.infer From 8750744068516912af06d1c1f1a8c48a1ebcc596 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 7 Aug 2025 21:47:24 -0400 Subject: [PATCH 004/178] renable todo tool --- .../opencode/src/session/prompt/beast.txt | 12 ---------- packages/opencode/src/tool/registry.ts | 22 ++----------------- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/packages/opencode/src/session/prompt/beast.txt b/packages/opencode/src/session/prompt/beast.txt index 3f0a9f84c..21db5dcb5 100644 --- a/packages/opencode/src/session/prompt/beast.txt +++ b/packages/opencode/src/session/prompt/beast.txt @@ -93,18 +93,6 @@ Carefully read the issue and think hard about a plan to solve it before coding. - To test hypotheses, you can also add test statements or functions - Revisit your assumptions if unexpected behavior occurs. -# How to create a Todo List -Use the following format to create a todo list: -```markdown -- [ ] Step 1: Description of the first step -- [ ] Step 2: Description of the second step -- [ ] Step 3: Description of the third step -``` - -Do not ever use HTML tags or any other formatting for the todo list, as it will not be rendered correctly. Always use the markdown format shown above. Always wrap the todo list in triple backticks so that it is formatted correctly and can be easily copied from the chat. - -Always show the completed todo list to the user as the last item in your message, so that they can see that you have addressed all of the steps. - # Communication Guidelines Always communicate clearly and concisely in a casual, friendly yet professional tone. diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index 1ff89f727..5b805ac69 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -66,9 +66,10 @@ export namespace ToolRegistry { return result } - export async function enabled(_providerID: string, modelID: string): Promise> { + export async function enabled(_providerID: string, _modelID: string): Promise> { const cfg = await Config.get() const result: Record = {} + result["patch"] = false if (cfg.permission?.edit === "deny") { result["edit"] = false @@ -79,25 +80,6 @@ export namespace ToolRegistry { result["bash"] = false } - if (modelID.toLowerCase().includes("claude")) { - result["patch"] = false - return result - } - - if ( - modelID.toLowerCase().includes("qwen") || - modelID.includes("gpt-") || - modelID.includes("o1") || - modelID.includes("o3") || - modelID.includes("codex") - ) { - result["patch"] = false - result["todowrite"] = false - result["todoread"] = false - - return result - } - return result } From 02ef96f89b103f54901b3bc58cac3fed72be3956 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 7 Aug 2025 21:49:18 -0400 Subject: [PATCH 005/178] docs: fix --- packages/web/src/content/docs/docs/agents.mdx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/web/src/content/docs/docs/agents.mdx b/packages/web/src/content/docs/docs/agents.mdx index 10c37e045..659c8ee5f 100644 --- a/packages/web/src/content/docs/docs/agents.mdx +++ b/packages/web/src/content/docs/docs/agents.mdx @@ -3,12 +3,6 @@ title: Agents description: Configure and use specialized agents in opencode. --- -agents allow opencode to operate different for specific tasks -you can cycle them with tab, or @ to delegate a subtask to them -explain built in build/plan agent -explain how to make your own agent -examples - Agents are specialized AI assistants that can be configured for specific tasks and workflows. They allow you to create focused tools with custom prompts, models, and tool access. :::tip From 4f164c53d2fa57bc52904618f473680205f0e5c7 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 7 Aug 2025 22:54:59 -0400 Subject: [PATCH 006/178] temporary fix for max output token --- packages/opencode/src/provider/provider.ts | 28 +++++++++++----------- packages/opencode/src/session/index.ts | 14 +++++------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 6e3ea85fe..759e3b917 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -97,7 +97,7 @@ export namespace Provider { Array.isArray(msg.content) && msg.content.some((part: any) => part.type === "image_url"), ) } - } catch {} + } catch { } const headers: Record = { ...init.headers, ...copilot.HEADERS, @@ -283,26 +283,26 @@ export namespace Provider { cost: !model.cost && !existing?.cost ? { - input: 0, - output: 0, - cache_read: 0, - cache_write: 0, - } + input: 0, + output: 0, + cache_read: 0, + cache_write: 0, + } : { - cache_read: 0, - cache_write: 0, - ...existing?.cost, - ...model.cost, - }, + cache_read: 0, + cache_write: 0, + ...existing?.cost, + ...model.cost, + }, options: { ...existing?.options, ...model.options, }, limit: model.limit ?? existing?.limit ?? { - context: 0, - output: 0, - }, + context: 0, + output: 0, + }, } parsed.models[modelID] = parsedModel } diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index d37528ebc..b562c433a 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -322,9 +322,9 @@ export namespace Session { for (const child of await children(sessionID)) { await remove(child.id, false) } - await unshare(sessionID).catch(() => {}) - await Storage.remove(`session/info/${sessionID}`).catch(() => {}) - await Storage.removeDir(`session/message/${sessionID}/`).catch(() => {}) + await unshare(sessionID).catch(() => { }) + await Storage.remove(`session/info/${sessionID}`).catch(() => { }) + await Storage.removeDir(`session/message/${sessionID}/`).catch(() => { }) state().sessions.delete(sessionID) state().messages.delete(sessionID) if (emitEvent) { @@ -523,7 +523,7 @@ export namespace Session { sessionID: input.sessionID, abort: new AbortController().signal, messageID: userMsg.id, - metadata: async () => {}, + metadata: async () => { }, }), ) return [ @@ -632,7 +632,7 @@ export namespace Session { // mark session as updated // used for session list sorting (indicates when session was most recently interacted with) - await update(input.sessionID, (_draft) => {}) + await update(input.sessionID, (_draft) => { }) if (isLocked(input.sessionID)) { return new Promise((resolve) => { @@ -712,7 +712,7 @@ export namespace Session { draft.title = title.trim() }) }) - .catch(() => {}) + .catch(() => { }) } const agent = await Agent.get(inputAgent) @@ -927,7 +927,7 @@ export namespace Session { }, maxRetries: 3, activeTools: Object.keys(tools).filter((x) => x !== "invalid"), - maxOutputTokens: outputLimit, + maxOutputTokens: model.info.id.startsWith("gpt-5") ? undefined : outputLimit, abortSignal: abort.signal, stopWhen: stepCountIs(1000), providerOptions: { From 063d67a0469ca8b91011a1aaa8e12bea67e8def8 Mon Sep 17 00:00:00 2001 From: opencode Date: Fri, 8 Aug 2025 03:01:03 +0000 Subject: [PATCH 007/178] release: v0.4.1 --- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/sdk/js/src/gen/sdk.gen.ts | 32 +++---- packages/sdk/js/src/gen/types.gen.ts | 121 ++++++++++++++++++--------- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 8 files changed, 104 insertions(+), 61 deletions(-) diff --git a/packages/function/package.json b/packages/function/package.json index de70b7bcc..1a256447d 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.3.133", + "version": "0.4.1", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 4a1ed555e..2b83805f2 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.3.133", + "version": "0.4.1", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 0ff4449bb..15f7c3d4c 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.3.133", + "version": "0.4.1", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index dee651977..1a11d9ccd 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.3.133", + "version": "0.4.1", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/src/gen/sdk.gen.ts b/packages/sdk/js/src/gen/sdk.gen.ts index 647ab7f86..44ed1d5f6 100644 --- a/packages/sdk/js/src/gen/sdk.gen.ts +++ b/packages/sdk/js/src/gen/sdk.gen.ts @@ -15,10 +15,10 @@ import type { SessionCreateData, SessionCreateResponses, SessionCreateErrors, - SessionGetData, - SessionGetResponses, SessionDeleteData, SessionDeleteResponses, + SessionGetData, + SessionGetResponses, SessionInitData, SessionInitResponses, SessionAbortData, @@ -55,8 +55,8 @@ import type { FileStatusResponses, AppLogData, AppLogResponses, - AppModesData, - AppModesResponses, + AppAgentsData, + AppAgentsResponses, TuiAppendPromptData, TuiAppendPromptResponses, TuiOpenHelpData, @@ -151,11 +151,11 @@ class App extends _HeyApiClient { } /** - * List all modes + * List all agents */ - public modes(options?: Options) { - return (options?.client ?? this._client).get({ - url: "/mode", + public agents(options?: Options) { + return (options?.client ?? this._client).get({ + url: "/agent", ...options, }) } @@ -205,20 +205,20 @@ class Session extends _HeyApiClient { } /** - * Get session + * Delete a session and all its data */ - public get(options: Options) { - return (options.client ?? this._client).get({ - url: "/session/{sessionID}", + public delete(options: Options) { + return (options.client ?? this._client).delete({ + url: "/session/{id}", ...options, }) } /** - * Delete a session and all its data + * Get session */ - public delete(options: Options) { - return (options.client ?? this._client).delete({ + public get(options: Options) { + return (options.client ?? this._client).get({ url: "/session/{id}", ...options, }) @@ -471,7 +471,7 @@ class Tui extends _HeyApiClient { } /** - * Execute a TUI command (e.g. switch_mode) + * Execute a TUI command (e.g. switch_agent) */ public executeCommand(options?: Options) { return (options?.client ?? this._client).post({ diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 25d962a57..3a97c2835 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -200,6 +200,9 @@ export type Part = | ({ type: "patch" } & PatchPart) + | ({ + type: "agent" + } & AgentPart) export type TextPart = { id: string @@ -374,6 +377,19 @@ export type PatchPart = { files: Array } +export type AgentPart = { + id: string + sessionID: string + messageID: string + type: string + name: string + source?: { + value: string + start: number + end: number + } +} + export type EventMessagePartRemoved = { type: string properties: { @@ -567,17 +583,19 @@ export type Config = { */ username?: string /** - * Modes configuration, see https://opencode.ai/docs/modes + * @deprecated Use `agent` field instead. */ mode?: { - build?: ModeConfig - plan?: ModeConfig - [key: string]: ModeConfig | undefined + build?: AgentConfig + plan?: AgentConfig + [key: string]: AgentConfig | undefined } /** - * Modes configuration, see https://opencode.ai/docs/modes + * Agent configuration, see https://opencode.ai/docs/agent */ agent?: { + plan?: AgentConfig + build?: AgentConfig general?: AgentConfig [key: string]: AgentConfig | undefined } @@ -704,13 +722,21 @@ export type KeybindsConfig = { */ app_help: string /** - * Next mode + * @deprecated use switch_agent. Next mode */ switch_mode: string /** - * Previous Mode + * @deprecated use switch_agent_reverse. Previous mode */ switch_mode_reverse: string + /** + * Next agent + */ + switch_agent: string + /** + * Previous agent + */ + switch_agent_reverse: string /** * Open external editor */ @@ -849,7 +875,7 @@ export type KeybindsConfig = { app_exit: string } -export type ModeConfig = { +export type AgentConfig = { model?: string temperature?: number top_p?: number @@ -858,10 +884,11 @@ export type ModeConfig = { [key: string]: boolean } disable?: boolean -} - -export type AgentConfig = ModeConfig & { - description: string + /** + * Description of when to use the agent + */ + description?: string + mode?: string } export type Provider = { @@ -968,6 +995,17 @@ export type FilePartInput = { source?: FilePartSource } +export type AgentPartInput = { + id?: string + type: string + name: string + source?: { + value: string + start: number + end: number + } +} + export type Symbol = { name: string kind: number @@ -984,10 +1022,12 @@ export type File = { status: "added" | "deleted" | "modified" } -export type Mode = { +export type Agent = { name: string - temperature?: number + description?: string + mode: string topP?: number + temperature?: number model?: { modelID: string providerID: string @@ -1103,24 +1143,6 @@ export type SessionCreateResponses = { export type SessionCreateResponse = SessionCreateResponses[keyof SessionCreateResponses] -export type SessionGetData = { - body?: never - path: { - sessionID: string - } - query?: never - url: "/session/{sessionID}" -} - -export type SessionGetResponses = { - /** - * Get session - */ - 200: Session -} - -export type SessionGetResponse = SessionGetResponses[keyof SessionGetResponses] - export type SessionDeleteData = { body?: never path: { @@ -1139,6 +1161,24 @@ export type SessionDeleteResponses = { export type SessionDeleteResponse = SessionDeleteResponses[keyof SessionDeleteResponses] +export type SessionGetData = { + body?: never + path: { + id: string + } + query?: never + url: "/session/{id}" +} + +export type SessionGetResponses = { + /** + * Get session + */ + 200: Session +} + +export type SessionGetResponse = SessionGetResponses[keyof SessionGetResponses] + export type SessionInitData = { body?: { messageID: string @@ -1271,7 +1311,7 @@ export type SessionChatData = { messageID?: string providerID: string modelID: string - mode?: string + agent?: string system?: string tools?: { [key: string]: boolean @@ -1283,6 +1323,9 @@ export type SessionChatData = { | ({ type: "file" } & FilePartInput) + | ({ + type: "agent" + } & AgentPartInput) > } path: { @@ -1556,21 +1599,21 @@ export type AppLogResponses = { export type AppLogResponse = AppLogResponses[keyof AppLogResponses] -export type AppModesData = { +export type AppAgentsData = { body?: never path?: never query?: never - url: "/mode" + url: "/agent" } -export type AppModesResponses = { +export type AppAgentsResponses = { /** - * List of modes + * List of agents */ - 200: Array + 200: Array } -export type AppModesResponse = AppModesResponses[keyof AppModesResponses] +export type AppAgentsResponse = AppAgentsResponses[keyof AppAgentsResponses] export type TuiAppendPromptData = { body?: { diff --git a/packages/web/package.json b/packages/web/package.json index 06c368a65..8d5b82fe5 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.3.133", + "version": "0.4.1", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index f5314dfea..2b34f8d83 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.3.133", + "version": "0.4.1", "publisher": "sst-dev", "repository": { "type": "git", From e444d15b575927602c907906802e6b6709d1e32d Mon Sep 17 00:00:00 2001 From: Timo Clasen Date: Fri, 8 Aug 2025 12:36:55 +0200 Subject: [PATCH 008/178] fix(TUI): enable general (sub-) agent for @ referencing (#1705) --- packages/tui/internal/completions/agents.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tui/internal/completions/agents.go b/packages/tui/internal/completions/agents.go index cda2be883..c39fe3036 100644 --- a/packages/tui/internal/completions/agents.go +++ b/packages/tui/internal/completions/agents.go @@ -45,7 +45,7 @@ func (cg *agentsContextGroup) GetChildEntries( if query != "" && !strings.Contains(strings.ToLower(agent.Name), strings.ToLower(query)) { continue } - if agent.Mode == opencode.AgentModePrimary || agent.Name == "general" { + if agent.Mode == opencode.AgentModePrimary { continue } From c7bb19ad0712469063eab35589aa5d3602b0c5b1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 8 Aug 2025 12:04:40 +0000 Subject: [PATCH 009/178] ignore: update download stats 2025-08-08 --- STATS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/STATS.md b/STATS.md index 6e1a82900..0101a72c6 100644 --- a/STATS.md +++ b/STATS.md @@ -41,3 +41,4 @@ | 2025-08-05 | 141,596 (+5,330) | 155,752 (+2,492) | 297,348 (+7,822) | | 2025-08-06 | 147,067 (+5,471) | 158,309 (+2,557) | 305,376 (+8,028) | | 2025-08-07 | 152,591 (+5,524) | 160,889 (+2,580) | 313,480 (+8,104) | +| 2025-08-08 | 158,187 (+5,596) | 163,448 (+2,559) | 321,635 (+8,155) | From 183e0911b76025a1f2a82e979d9834fec2131d0e Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 8 Aug 2025 13:22:54 -0400 Subject: [PATCH 010/178] wip: gateway --- cloud/core/drizzle.config.ts | 20 + cloud/core/migrations/0000_amused_mojo.sql | 66 + cloud/core/migrations/0001_thankful_chat.sql | 8 + cloud/core/migrations/0002_stale_jackal.sql | 14 + .../0003_tranquil_spencer_smythe.sql | 1 + cloud/core/migrations/meta/0000_snapshot.json | 461 ++++++ cloud/core/migrations/meta/0001_snapshot.json | 515 +++++++ cloud/core/migrations/meta/0002_snapshot.json | 615 ++++++++ cloud/core/migrations/meta/0003_snapshot.json | 609 ++++++++ cloud/core/migrations/meta/_journal.json | 34 + cloud/core/package.json | 22 + cloud/core/src/account.ts | 67 + cloud/core/src/actor.ts | 75 + cloud/core/src/billing.ts | 71 + cloud/core/src/context.ts | 21 + cloud/core/src/drizzle/index.ts | 94 ++ cloud/core/src/drizzle/types.ts | 29 + cloud/core/src/identifier.ts | 26 + cloud/core/src/schema/account.sql.ts | 12 + cloud/core/src/schema/billing.sql.ts | 45 + cloud/core/src/schema/key.sql.ts | 16 + cloud/core/src/schema/user.sql.ts | 16 + cloud/core/src/schema/workspace.sql.ts | 25 + cloud/core/src/util/fn.ts | 14 + cloud/core/src/util/log.ts | 55 + cloud/core/src/util/price.ts | 3 + cloud/core/src/workspace.ts | 48 + cloud/core/sst-env.d.ts | 9 + cloud/core/tsconfig.json | 9 + cloud/function/package.json | 23 + cloud/function/src/auth.ts | 68 + {packages => cloud}/function/src/gateway.ts | 446 +++++- cloud/function/sst-env.d.ts | 88 ++ cloud/function/tsconfig.json | 9 + cloud/web/.gitignore | 2 + cloud/web/index.html | 38 + cloud/web/npm-debug.log | 29 + cloud/web/package.json | 32 + cloud/web/public/favicon-dark.svg | 3 + cloud/web/public/favicon.ico | Bin 0 -> 171515 bytes cloud/web/public/favicon.svg | 3 + cloud/web/public/social-share.png | Bin 0 -> 25855 bytes cloud/web/scripts/render.mjs | 24 + cloud/web/src/app.tsx | 42 + cloud/web/src/assets/screenshot.png | Bin 0 -> 449252 bytes cloud/web/src/components/context-account.tsx | 99 ++ cloud/web/src/components/context-openauth.tsx | 180 +++ cloud/web/src/components/context-theme.tsx | 39 + cloud/web/src/entry-client.tsx | 13 + cloud/web/src/entry-server.tsx | 7 + cloud/web/src/pages/[workspace].tsx | 11 + .../src/pages/[workspace]/billing.module.css | 56 + cloud/web/src/pages/[workspace]/billing.tsx | 132 ++ .../pages/[workspace]/components/system.txt | 11 + .../src/pages/[workspace]/components/tool.ts | 271 ++++ .../src/pages/[workspace]/index.module.css | 239 +++ cloud/web/src/pages/[workspace]/index.tsx | 18 + .../web/src/pages/[workspace]/keys.module.css | 97 ++ cloud/web/src/pages/[workspace]/keys.tsx | 151 ++ .../web/src/pages/components/context-api.tsx | 24 + .../pages/components/context-workspace.tsx | 38 + .../src/pages/components/layout.module.css | 199 +++ cloud/web/src/pages/components/layout.tsx | 96 ++ cloud/web/src/pages/index.tsx | 36 + cloud/web/src/pages/lander.module.css | 169 +++ cloud/web/src/pages/test/design.module.css | 204 +++ cloud/web/src/pages/test/design.tsx | 562 +++++++ cloud/web/src/sst-env.d.ts | 12 + cloud/web/src/ui/button.tsx | 24 + cloud/web/src/ui/context-dialog.tsx | 120 ++ cloud/web/src/ui/dialog-select.module.css | 36 + cloud/web/src/ui/dialog-select.tsx | 124 ++ cloud/web/src/ui/dialog-string.tsx | 70 + cloud/web/src/ui/dialog.tsx | 27 + cloud/web/src/ui/style/component/button.css | 78 + cloud/web/src/ui/style/component/dialog.css | 84 ++ cloud/web/src/ui/style/component/input.css | 34 + cloud/web/src/ui/style/component/label.css | 17 + .../web/src/ui/style/component/title-bar.css | 32 + cloud/web/src/ui/style/index.css | 50 + cloud/web/src/ui/style/token/animation.css | 23 + cloud/web/src/ui/style/token/color.css | 88 ++ cloud/web/src/ui/style/token/font.css | 20 + cloud/web/src/ui/style/token/reset.css | 212 +++ cloud/web/src/ui/style/token/space.css | 38 + cloud/web/src/ui/svg/icons.tsx | 1292 +++++++++++++++++ cloud/web/src/ui/svg/index.tsx | 67 + cloud/web/src/util/context.tsx | 26 + cloud/web/sst-env.d.ts | 9 + cloud/web/tsconfig.json | 12 + cloud/web/vite.config.ts | 63 + infra/app.ts | 22 +- infra/cloud.ts | 105 ++ infra/stage.ts | 5 + opencode.json | 20 + package.json | 5 + packages/function/package.json | 5 - packages/function/sst-env.d.ts | 40 +- packages/opencode/package.json | 2 +- sst-env.d.ts | 45 +- sst.config.ts | 9 +- 101 files changed, 9218 insertions(+), 57 deletions(-) create mode 100644 cloud/core/drizzle.config.ts create mode 100644 cloud/core/migrations/0000_amused_mojo.sql create mode 100644 cloud/core/migrations/0001_thankful_chat.sql create mode 100644 cloud/core/migrations/0002_stale_jackal.sql create mode 100644 cloud/core/migrations/0003_tranquil_spencer_smythe.sql create mode 100644 cloud/core/migrations/meta/0000_snapshot.json create mode 100644 cloud/core/migrations/meta/0001_snapshot.json create mode 100644 cloud/core/migrations/meta/0002_snapshot.json create mode 100644 cloud/core/migrations/meta/0003_snapshot.json create mode 100644 cloud/core/migrations/meta/_journal.json create mode 100644 cloud/core/package.json create mode 100644 cloud/core/src/account.ts create mode 100644 cloud/core/src/actor.ts create mode 100644 cloud/core/src/billing.ts create mode 100644 cloud/core/src/context.ts create mode 100644 cloud/core/src/drizzle/index.ts create mode 100644 cloud/core/src/drizzle/types.ts create mode 100644 cloud/core/src/identifier.ts create mode 100644 cloud/core/src/schema/account.sql.ts create mode 100644 cloud/core/src/schema/billing.sql.ts create mode 100644 cloud/core/src/schema/key.sql.ts create mode 100644 cloud/core/src/schema/user.sql.ts create mode 100644 cloud/core/src/schema/workspace.sql.ts create mode 100644 cloud/core/src/util/fn.ts create mode 100644 cloud/core/src/util/log.ts create mode 100644 cloud/core/src/util/price.ts create mode 100644 cloud/core/src/workspace.ts create mode 100644 cloud/core/sst-env.d.ts create mode 100644 cloud/core/tsconfig.json create mode 100644 cloud/function/package.json create mode 100644 cloud/function/src/auth.ts rename {packages => cloud}/function/src/gateway.ts (54%) create mode 100644 cloud/function/sst-env.d.ts create mode 100644 cloud/function/tsconfig.json create mode 100644 cloud/web/.gitignore create mode 100644 cloud/web/index.html create mode 100644 cloud/web/npm-debug.log create mode 100644 cloud/web/package.json create mode 100644 cloud/web/public/favicon-dark.svg create mode 100644 cloud/web/public/favicon.ico create mode 100644 cloud/web/public/favicon.svg create mode 100644 cloud/web/public/social-share.png create mode 100644 cloud/web/scripts/render.mjs create mode 100644 cloud/web/src/app.tsx create mode 100644 cloud/web/src/assets/screenshot.png create mode 100644 cloud/web/src/components/context-account.tsx create mode 100644 cloud/web/src/components/context-openauth.tsx create mode 100644 cloud/web/src/components/context-theme.tsx create mode 100644 cloud/web/src/entry-client.tsx create mode 100644 cloud/web/src/entry-server.tsx create mode 100644 cloud/web/src/pages/[workspace].tsx create mode 100644 cloud/web/src/pages/[workspace]/billing.module.css create mode 100644 cloud/web/src/pages/[workspace]/billing.tsx create mode 100644 cloud/web/src/pages/[workspace]/components/system.txt create mode 100644 cloud/web/src/pages/[workspace]/components/tool.ts create mode 100644 cloud/web/src/pages/[workspace]/index.module.css create mode 100644 cloud/web/src/pages/[workspace]/index.tsx create mode 100644 cloud/web/src/pages/[workspace]/keys.module.css create mode 100644 cloud/web/src/pages/[workspace]/keys.tsx create mode 100644 cloud/web/src/pages/components/context-api.tsx create mode 100644 cloud/web/src/pages/components/context-workspace.tsx create mode 100644 cloud/web/src/pages/components/layout.module.css create mode 100644 cloud/web/src/pages/components/layout.tsx create mode 100644 cloud/web/src/pages/index.tsx create mode 100644 cloud/web/src/pages/lander.module.css create mode 100644 cloud/web/src/pages/test/design.module.css create mode 100644 cloud/web/src/pages/test/design.tsx create mode 100644 cloud/web/src/sst-env.d.ts create mode 100644 cloud/web/src/ui/button.tsx create mode 100644 cloud/web/src/ui/context-dialog.tsx create mode 100644 cloud/web/src/ui/dialog-select.module.css create mode 100644 cloud/web/src/ui/dialog-select.tsx create mode 100644 cloud/web/src/ui/dialog-string.tsx create mode 100644 cloud/web/src/ui/dialog.tsx create mode 100644 cloud/web/src/ui/style/component/button.css create mode 100644 cloud/web/src/ui/style/component/dialog.css create mode 100644 cloud/web/src/ui/style/component/input.css create mode 100644 cloud/web/src/ui/style/component/label.css create mode 100644 cloud/web/src/ui/style/component/title-bar.css create mode 100644 cloud/web/src/ui/style/index.css create mode 100644 cloud/web/src/ui/style/token/animation.css create mode 100644 cloud/web/src/ui/style/token/color.css create mode 100644 cloud/web/src/ui/style/token/font.css create mode 100644 cloud/web/src/ui/style/token/reset.css create mode 100644 cloud/web/src/ui/style/token/space.css create mode 100644 cloud/web/src/ui/svg/icons.tsx create mode 100644 cloud/web/src/ui/svg/index.tsx create mode 100644 cloud/web/src/util/context.tsx create mode 100644 cloud/web/sst-env.d.ts create mode 100644 cloud/web/tsconfig.json create mode 100644 cloud/web/vite.config.ts create mode 100644 infra/cloud.ts create mode 100644 infra/stage.ts diff --git a/cloud/core/drizzle.config.ts b/cloud/core/drizzle.config.ts new file mode 100644 index 000000000..c65363cb8 --- /dev/null +++ b/cloud/core/drizzle.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from "drizzle-kit" +import { Resource } from "sst" + +export default defineConfig({ + out: "./migrations/", + strict: true, + schema: ["./src/**/*.sql.ts"], + verbose: true, + dialect: "postgresql", + dbCredentials: { + database: Resource.Database.database, + host: Resource.Database.host, + user: Resource.Database.username, + password: Resource.Database.password, + port: Resource.Database.port, + ssl: { + rejectUnauthorized: false, + }, + }, +}) diff --git a/cloud/core/migrations/0000_amused_mojo.sql b/cloud/core/migrations/0000_amused_mojo.sql new file mode 100644 index 000000000..75441ad24 --- /dev/null +++ b/cloud/core/migrations/0000_amused_mojo.sql @@ -0,0 +1,66 @@ +CREATE TABLE "billing" ( + "id" varchar(30) NOT NULL, + "workspace_id" varchar(30) NOT NULL, + "time_created" timestamp with time zone DEFAULT now() NOT NULL, + "time_deleted" timestamp with time zone, + "customer_id" varchar(255), + "payment_method_id" varchar(255), + "payment_method_last4" varchar(4), + "balance" bigint NOT NULL, + "reload" boolean, + CONSTRAINT "billing_workspace_id_id_pk" PRIMARY KEY("workspace_id","id") +); +--> statement-breakpoint +CREATE TABLE "payment" ( + "id" varchar(30) NOT NULL, + "workspace_id" varchar(30) NOT NULL, + "time_created" timestamp with time zone DEFAULT now() NOT NULL, + "time_deleted" timestamp with time zone, + "customer_id" varchar(255), + "payment_id" varchar(255), + "amount" bigint NOT NULL, + CONSTRAINT "payment_workspace_id_id_pk" PRIMARY KEY("workspace_id","id") +); +--> statement-breakpoint +CREATE TABLE "usage" ( + "id" varchar(30) NOT NULL, + "workspace_id" varchar(30) NOT NULL, + "time_created" timestamp with time zone DEFAULT now() NOT NULL, + "time_deleted" timestamp with time zone, + "request_id" varchar(255), + "model" varchar(255) NOT NULL, + "input_tokens" integer NOT NULL, + "output_tokens" integer NOT NULL, + "reasoning_tokens" integer, + "cache_read_tokens" integer, + "cache_write_tokens" integer, + "cost" bigint NOT NULL, + CONSTRAINT "usage_workspace_id_id_pk" PRIMARY KEY("workspace_id","id") +); +--> statement-breakpoint +CREATE TABLE "user" ( + "id" varchar(30) NOT NULL, + "workspace_id" varchar(30) NOT NULL, + "time_created" timestamp with time zone DEFAULT now() NOT NULL, + "time_deleted" timestamp with time zone, + "email" text NOT NULL, + "name" varchar(255) NOT NULL, + "time_seen" timestamp with time zone, + "color" integer, + CONSTRAINT "user_workspace_id_id_pk" PRIMARY KEY("workspace_id","id") +); +--> statement-breakpoint +CREATE TABLE "workspace" ( + "id" varchar(30) PRIMARY KEY NOT NULL, + "slug" varchar(255), + "name" varchar(255), + "time_created" timestamp with time zone DEFAULT now() NOT NULL, + "time_deleted" timestamp with time zone +); +--> statement-breakpoint +ALTER TABLE "billing" ADD CONSTRAINT "billing_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "payment" ADD CONSTRAINT "payment_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "usage" ADD CONSTRAINT "usage_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "user" ADD CONSTRAINT "user_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "user_email" ON "user" USING btree ("workspace_id","email");--> statement-breakpoint +CREATE UNIQUE INDEX "slug" ON "workspace" USING btree ("slug"); \ No newline at end of file diff --git a/cloud/core/migrations/0001_thankful_chat.sql b/cloud/core/migrations/0001_thankful_chat.sql new file mode 100644 index 000000000..9c66a6ac4 --- /dev/null +++ b/cloud/core/migrations/0001_thankful_chat.sql @@ -0,0 +1,8 @@ +CREATE TABLE "account" ( + "id" varchar(30) NOT NULL, + "time_created" timestamp with time zone DEFAULT now() NOT NULL, + "time_deleted" timestamp with time zone, + "email" varchar(255) NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX "email" ON "account" USING btree ("email"); \ No newline at end of file diff --git a/cloud/core/migrations/0002_stale_jackal.sql b/cloud/core/migrations/0002_stale_jackal.sql new file mode 100644 index 000000000..267dff273 --- /dev/null +++ b/cloud/core/migrations/0002_stale_jackal.sql @@ -0,0 +1,14 @@ +CREATE TABLE "key" ( + "id" varchar(30) NOT NULL, + "workspace_id" varchar(30) NOT NULL, + "time_created" timestamp with time zone DEFAULT now() NOT NULL, + "time_deleted" timestamp with time zone, + "user_id" text NOT NULL, + "name" varchar(255) NOT NULL, + "key" varchar(255) NOT NULL, + "time_used" timestamp with time zone, + CONSTRAINT "key_workspace_id_id_pk" PRIMARY KEY("workspace_id","id") +); +--> statement-breakpoint +ALTER TABLE "key" ADD CONSTRAINT "key_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "global_key" ON "key" USING btree ("key"); \ No newline at end of file diff --git a/cloud/core/migrations/0003_tranquil_spencer_smythe.sql b/cloud/core/migrations/0003_tranquil_spencer_smythe.sql new file mode 100644 index 000000000..4f57f779f --- /dev/null +++ b/cloud/core/migrations/0003_tranquil_spencer_smythe.sql @@ -0,0 +1 @@ +ALTER TABLE "usage" DROP COLUMN "request_id"; \ No newline at end of file diff --git a/cloud/core/migrations/meta/0000_snapshot.json b/cloud/core/migrations/meta/0000_snapshot.json new file mode 100644 index 000000000..3b86bed25 --- /dev/null +++ b/cloud/core/migrations/meta/0000_snapshot.json @@ -0,0 +1,461 @@ +{ + "id": "9b5cec8c-8b59-4d7a-bb5c-76ade1c83d6f", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.billing": { + "name": "billing", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_method_id": { + "name": "payment_method_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_method_last4": { + "name": "payment_method_last4", + "type": "varchar(4)", + "primaryKey": false, + "notNull": false + }, + "balance": { + "name": "balance", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "reload": { + "name": "reload", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "billing_workspace_id_workspace_id_fk": { + "name": "billing_workspace_id_workspace_id_fk", + "tableFrom": "billing", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "billing_workspace_id_id_pk": { + "name": "billing_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment": { + "name": "payment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_id": { + "name": "payment_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "payment_workspace_id_workspace_id_fk": { + "name": "payment_workspace_id_workspace_id_fk", + "tableFrom": "payment", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "payment_workspace_id_id_pk": { + "name": "payment_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usage": { + "name": "usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "request_id": { + "name": "request_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reasoning_tokens": { + "name": "reasoning_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cache_read_tokens": { + "name": "cache_read_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cache_write_tokens": { + "name": "cache_write_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "usage_workspace_id_workspace_id_fk": { + "name": "usage_workspace_id_workspace_id_fk", + "tableFrom": "usage", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "usage_workspace_id_id_pk": { + "name": "usage_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "time_seen": { + "name": "time_seen", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_email": { + "name": "user_email", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_workspace_id_workspace_id_fk": { + "name": "user_workspace_id_workspace_id_fk", + "tableFrom": "user", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_workspace_id_id_pk": { + "name": "user_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace": { + "name": "workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "slug": { + "name": "slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/cloud/core/migrations/meta/0001_snapshot.json b/cloud/core/migrations/meta/0001_snapshot.json new file mode 100644 index 000000000..69d66ebc6 --- /dev/null +++ b/cloud/core/migrations/meta/0001_snapshot.json @@ -0,0 +1,515 @@ +{ + "id": "bf9e9084-4073-4ecb-8e56-5610816c9589", + "prevId": "9b5cec8c-8b59-4d7a-bb5c-76ade1c83d6f", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "email": { + "name": "email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing": { + "name": "billing", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_method_id": { + "name": "payment_method_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_method_last4": { + "name": "payment_method_last4", + "type": "varchar(4)", + "primaryKey": false, + "notNull": false + }, + "balance": { + "name": "balance", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "reload": { + "name": "reload", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "billing_workspace_id_workspace_id_fk": { + "name": "billing_workspace_id_workspace_id_fk", + "tableFrom": "billing", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "billing_workspace_id_id_pk": { + "name": "billing_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment": { + "name": "payment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_id": { + "name": "payment_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "payment_workspace_id_workspace_id_fk": { + "name": "payment_workspace_id_workspace_id_fk", + "tableFrom": "payment", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "payment_workspace_id_id_pk": { + "name": "payment_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usage": { + "name": "usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "request_id": { + "name": "request_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reasoning_tokens": { + "name": "reasoning_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cache_read_tokens": { + "name": "cache_read_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cache_write_tokens": { + "name": "cache_write_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "usage_workspace_id_workspace_id_fk": { + "name": "usage_workspace_id_workspace_id_fk", + "tableFrom": "usage", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "usage_workspace_id_id_pk": { + "name": "usage_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "time_seen": { + "name": "time_seen", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_email": { + "name": "user_email", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_workspace_id_workspace_id_fk": { + "name": "user_workspace_id_workspace_id_fk", + "tableFrom": "user", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_workspace_id_id_pk": { + "name": "user_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace": { + "name": "workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "slug": { + "name": "slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/cloud/core/migrations/meta/0002_snapshot.json b/cloud/core/migrations/meta/0002_snapshot.json new file mode 100644 index 000000000..7d970ab02 --- /dev/null +++ b/cloud/core/migrations/meta/0002_snapshot.json @@ -0,0 +1,615 @@ +{ + "id": "351e4956-74e0-4282-a23b-02f1a73fa38c", + "prevId": "bf9e9084-4073-4ecb-8e56-5610816c9589", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "email": { + "name": "email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing": { + "name": "billing", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_method_id": { + "name": "payment_method_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_method_last4": { + "name": "payment_method_last4", + "type": "varchar(4)", + "primaryKey": false, + "notNull": false + }, + "balance": { + "name": "balance", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "reload": { + "name": "reload", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "billing_workspace_id_workspace_id_fk": { + "name": "billing_workspace_id_workspace_id_fk", + "tableFrom": "billing", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "billing_workspace_id_id_pk": { + "name": "billing_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment": { + "name": "payment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_id": { + "name": "payment_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "payment_workspace_id_workspace_id_fk": { + "name": "payment_workspace_id_workspace_id_fk", + "tableFrom": "payment", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "payment_workspace_id_id_pk": { + "name": "payment_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usage": { + "name": "usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "request_id": { + "name": "request_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reasoning_tokens": { + "name": "reasoning_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cache_read_tokens": { + "name": "cache_read_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cache_write_tokens": { + "name": "cache_write_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "usage_workspace_id_workspace_id_fk": { + "name": "usage_workspace_id_workspace_id_fk", + "tableFrom": "usage", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "usage_workspace_id_id_pk": { + "name": "usage_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.key": { + "name": "key", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "time_used": { + "name": "time_used", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "global_key": { + "name": "global_key", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "key_workspace_id_workspace_id_fk": { + "name": "key_workspace_id_workspace_id_fk", + "tableFrom": "key", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "key_workspace_id_id_pk": { + "name": "key_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "time_seen": { + "name": "time_seen", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_email": { + "name": "user_email", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_workspace_id_workspace_id_fk": { + "name": "user_workspace_id_workspace_id_fk", + "tableFrom": "user", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_workspace_id_id_pk": { + "name": "user_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace": { + "name": "workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "slug": { + "name": "slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/cloud/core/migrations/meta/0003_snapshot.json b/cloud/core/migrations/meta/0003_snapshot.json new file mode 100644 index 000000000..e1202ddbf --- /dev/null +++ b/cloud/core/migrations/meta/0003_snapshot.json @@ -0,0 +1,609 @@ +{ + "id": "fa935883-9e51-4811-90c7-8967eefe458c", + "prevId": "351e4956-74e0-4282-a23b-02f1a73fa38c", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "email": { + "name": "email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing": { + "name": "billing", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_method_id": { + "name": "payment_method_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_method_last4": { + "name": "payment_method_last4", + "type": "varchar(4)", + "primaryKey": false, + "notNull": false + }, + "balance": { + "name": "balance", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "reload": { + "name": "reload", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "billing_workspace_id_workspace_id_fk": { + "name": "billing_workspace_id_workspace_id_fk", + "tableFrom": "billing", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "billing_workspace_id_id_pk": { + "name": "billing_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment": { + "name": "payment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "payment_id": { + "name": "payment_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "payment_workspace_id_workspace_id_fk": { + "name": "payment_workspace_id_workspace_id_fk", + "tableFrom": "payment", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "payment_workspace_id_id_pk": { + "name": "payment_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usage": { + "name": "usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reasoning_tokens": { + "name": "reasoning_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cache_read_tokens": { + "name": "cache_read_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cache_write_tokens": { + "name": "cache_write_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "usage_workspace_id_workspace_id_fk": { + "name": "usage_workspace_id_workspace_id_fk", + "tableFrom": "usage", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "usage_workspace_id_id_pk": { + "name": "usage_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.key": { + "name": "key", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "time_used": { + "name": "time_used", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "global_key": { + "name": "global_key", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "key_workspace_id_workspace_id_fk": { + "name": "key_workspace_id_workspace_id_fk", + "tableFrom": "key", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "key_workspace_id_id_pk": { + "name": "key_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "time_seen": { + "name": "time_seen", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_email": { + "name": "user_email", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_workspace_id_workspace_id_fk": { + "name": "user_workspace_id_workspace_id_fk", + "tableFrom": "user", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_workspace_id_id_pk": { + "name": "user_workspace_id_id_pk", + "columns": [ + "workspace_id", + "id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace": { + "name": "workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(30)", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "time_created": { + "name": "time_created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "time_deleted": { + "name": "time_deleted", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "slug": { + "name": "slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/cloud/core/migrations/meta/_journal.json b/cloud/core/migrations/meta/_journal.json new file mode 100644 index 000000000..ceba11e26 --- /dev/null +++ b/cloud/core/migrations/meta/_journal.json @@ -0,0 +1,34 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1754518198186, + "tag": "0000_amused_mojo", + "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1754609655262, + "tag": "0001_thankful_chat", + "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1754627626945, + "tag": "0002_stale_jackal", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1754672464106, + "tag": "0003_tranquil_spencer_smythe", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/cloud/core/package.json b/cloud/core/package.json new file mode 100644 index 000000000..eeebf4392 --- /dev/null +++ b/cloud/core/package.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "name": "@opencode/cloud-core", + "version": "0.0.0", + "private": true, + "type": "module", + "dependencies": { + "@aws-sdk/client-sts": "3.782.0", + "drizzle-orm": "0.41.0", + "stripe": "18.0.0", + "ulid": "3.0.0" + }, + "exports": { + "./*": "./src/*" + }, + "scripts": { + "db": "sst shell drizzle-kit" + }, + "devDependencies": { + "drizzle-kit": "0.30.5" + } +} diff --git a/cloud/core/src/account.ts b/cloud/core/src/account.ts new file mode 100644 index 000000000..cb123e048 --- /dev/null +++ b/cloud/core/src/account.ts @@ -0,0 +1,67 @@ +import { z } from "zod" +import { and, eq, getTableColumns, isNull } from "drizzle-orm" +import { fn } from "./util/fn" +import { Database } from "./drizzle" +import { Identifier } from "./identifier" +import { AccountTable } from "./schema/account.sql" +import { Actor } from "./actor" +import { WorkspaceTable } from "./schema/workspace.sql" +import { UserTable } from "./schema/user.sql" + +export namespace Account { + export const create = fn( + z.object({ + email: z.string().email(), + id: z.string().optional(), + }), + async (input) => + Database.transaction(async (tx) => { + const id = input.id ?? Identifier.create("account") + await tx.insert(AccountTable).values({ + id, + email: input.email, + }) + return id + }), + ) + + export const fromID = fn(z.string(), async (id) => + Database.transaction(async (tx) => { + return tx + .select() + .from(AccountTable) + .where(eq(AccountTable.id, id)) + .execute() + .then((rows) => rows[0]) + }), + ) + + export const fromEmail = fn(z.string().email(), async (email) => + Database.transaction(async (tx) => { + return tx + .select() + .from(AccountTable) + .where(eq(AccountTable.email, email)) + .execute() + .then((rows) => rows[0]) + }), + ) + + export const workspaces = async () => { + const actor = Actor.assert("account") + return Database.transaction(async (tx) => + tx + .select(getTableColumns(WorkspaceTable)) + .from(WorkspaceTable) + .innerJoin(UserTable, eq(UserTable.workspaceID, WorkspaceTable.id)) + .where( + and( + eq(UserTable.email, actor.properties.email), + isNull(UserTable.timeDeleted), + isNull(WorkspaceTable.timeDeleted), + ), + ) + .execute(), + ) + } +} diff --git a/cloud/core/src/actor.ts b/cloud/core/src/actor.ts new file mode 100644 index 000000000..beb292bb8 --- /dev/null +++ b/cloud/core/src/actor.ts @@ -0,0 +1,75 @@ +import { Context } from "./context" +import { Log } from "./util/log" + +export namespace Actor { + interface Account { + type: "account" + properties: { + accountID: string + email: string + } + } + + interface Public { + type: "public" + properties: {} + } + + interface User { + type: "user" + properties: { + userID: string + workspaceID: string + email: string + } + } + + interface System { + type: "system" + properties: { + workspaceID: string + } + } + + export type Info = Account | Public | User | System + + const ctx = Context.create() + export const use = ctx.use + + const log = Log.create().tag("namespace", "actor") + + export function provide( + type: T, + properties: Extract["properties"], + cb: () => R, + ) { + return ctx.provide( + { + type, + properties, + } as any, + () => { + return Log.provide({ ...properties }, () => { + log.info("provided") + return cb() + }) + }, + ) + } + + export function assert(type: T) { + const actor = use() + if (actor.type !== type) { + throw new Error(`Expected actor type ${type}, got ${actor.type}`) + } + return actor as Extract + } + + export function workspace() { + const actor = use() + if ("workspaceID" in actor.properties) { + return actor.properties.workspaceID + } + throw new Error(`actor of type "${actor.type}" is not associated with a workspace`) + } +} diff --git a/cloud/core/src/billing.ts b/cloud/core/src/billing.ts new file mode 100644 index 000000000..1a7bb2946 --- /dev/null +++ b/cloud/core/src/billing.ts @@ -0,0 +1,71 @@ +import { Resource } from "sst" +import { Stripe } from "stripe" +import { Database, eq, sql } from "./drizzle" +import { BillingTable, UsageTable } from "./schema/billing.sql" +import { Actor } from "./actor" +import { fn } from "./util/fn" +import { z } from "zod" +import { Identifier } from "./identifier" +import { centsToMicroCents } from "./util/price" + +export namespace Billing { + export const stripe = () => + new Stripe(Resource.STRIPE_SECRET_KEY.value, { + apiVersion: "2025-03-31.basil", + }) + + export const get = async () => { + return Database.use(async (tx) => + tx + .select({ + customerID: BillingTable.customerID, + paymentMethodID: BillingTable.paymentMethodID, + balance: BillingTable.balance, + reload: BillingTable.reload, + }) + .from(BillingTable) + .where(eq(BillingTable.workspaceID, Actor.workspace())) + .then((r) => r[0]), + ) + } + + export const consume = fn( + z.object({ + requestID: z.string().optional(), + model: z.string(), + inputTokens: z.number(), + outputTokens: z.number(), + reasoningTokens: z.number().optional(), + cacheReadTokens: z.number().optional(), + cacheWriteTokens: z.number().optional(), + costInCents: z.number(), + }), + async (input) => { + const workspaceID = Actor.workspace() + const cost = centsToMicroCents(input.costInCents) + + return await Database.transaction(async (tx) => { + await tx.insert(UsageTable).values({ + workspaceID, + id: Identifier.create("usage"), + requestID: input.requestID, + model: input.model, + inputTokens: input.inputTokens, + outputTokens: input.outputTokens, + reasoningTokens: input.reasoningTokens, + cacheReadTokens: input.cacheReadTokens, + cacheWriteTokens: input.cacheWriteTokens, + cost, + }) + const [updated] = await tx + .update(BillingTable) + .set({ + balance: sql`${BillingTable.balance} - ${cost}`, + }) + .where(eq(BillingTable.workspaceID, workspaceID)) + .returning() + return updated.balance + }) + }, + ) +} diff --git a/cloud/core/src/context.ts b/cloud/core/src/context.ts new file mode 100644 index 000000000..c2ca6a313 --- /dev/null +++ b/cloud/core/src/context.ts @@ -0,0 +1,21 @@ +import { AsyncLocalStorage } from "node:async_hooks" + +export namespace Context { + export class NotFound extends Error {} + + export function create() { + const storage = new AsyncLocalStorage() + return { + use() { + const result = storage.getStore() + if (!result) { + throw new NotFound() + } + return result + }, + provide(value: T, fn: () => R) { + return storage.run(value, fn) + }, + } + } +} diff --git a/cloud/core/src/drizzle/index.ts b/cloud/core/src/drizzle/index.ts new file mode 100644 index 000000000..76220f2a2 --- /dev/null +++ b/cloud/core/src/drizzle/index.ts @@ -0,0 +1,94 @@ +import { drizzle } from "drizzle-orm/postgres-js" +import { Resource } from "sst" +export * from "drizzle-orm" +import postgres from "postgres" + +function createClient() { + const client = postgres({ + idle_timeout: 30000, + connect_timeout: 30000, + host: Resource.Database.host, + database: Resource.Database.database, + user: Resource.Database.username, + password: Resource.Database.password, + port: Resource.Database.port, + ssl: { + rejectUnauthorized: false, + }, + max: 1, + }) + + return drizzle(client, {}) +} + +import { PgTransaction, type PgTransactionConfig } from "drizzle-orm/pg-core" +import type { ExtractTablesWithRelations } from "drizzle-orm" +import type { PostgresJsQueryResultHKT } from "drizzle-orm/postgres-js" +import { Context } from "../context" + +export namespace Database { + export type Transaction = PgTransaction< + PostgresJsQueryResultHKT, + Record, + ExtractTablesWithRelations> + > + + export type TxOrDb = Transaction | ReturnType + + const TransactionContext = Context.create<{ + tx: TxOrDb + effects: (() => void | Promise)[] + }>() + + export async function use(callback: (trx: TxOrDb) => Promise) { + try { + const { tx } = TransactionContext.use() + return tx.transaction(callback) + } catch (err) { + if (err instanceof Context.NotFound) { + const client = createClient() + const effects: (() => void | Promise)[] = [] + const result = await TransactionContext.provide( + { + effects, + tx: client, + }, + () => callback(client), + ) + await Promise.all(effects.map((x) => x())) + return result + } + throw err + } + } + export async function fn(callback: (input: Input, trx: TxOrDb) => Promise) { + return (input: Input) => use(async (tx) => callback(input, tx)) + } + + export async function effect(effect: () => any | Promise) { + try { + const { effects } = TransactionContext.use() + effects.push(effect) + } catch { + await effect() + } + } + + export async function transaction(callback: (tx: TxOrDb) => Promise, config?: PgTransactionConfig) { + try { + const { tx } = TransactionContext.use() + return callback(tx) + } catch (err) { + if (err instanceof Context.NotFound) { + const client = createClient() + const effects: (() => void | Promise)[] = [] + const result = await client.transaction(async (tx) => { + return TransactionContext.provide({ tx, effects }, () => callback(tx)) + }, config) + await Promise.all(effects.map((x) => x())) + return result + } + throw err + } + } +} diff --git a/cloud/core/src/drizzle/types.ts b/cloud/core/src/drizzle/types.ts new file mode 100644 index 000000000..5ae95d011 --- /dev/null +++ b/cloud/core/src/drizzle/types.ts @@ -0,0 +1,29 @@ +import { bigint, timestamp, varchar } from "drizzle-orm/pg-core" + +export const ulid = (name: string) => varchar(name, { length: 30 }) + +export const workspaceColumns = { + get id() { + return ulid("id").notNull() + }, + get workspaceID() { + return ulid("workspace_id").notNull() + }, +} + +export const id = () => ulid("id").notNull() + +export const utc = (name: string) => + timestamp(name, { + withTimezone: true, + }) + +export const currency = (name: string) => + bigint(name, { + mode: "number", + }) + +export const timestamps = { + timeCreated: utc("time_created").notNull().defaultNow(), + timeDeleted: utc("time_deleted"), +} diff --git a/cloud/core/src/identifier.ts b/cloud/core/src/identifier.ts new file mode 100644 index 000000000..f8e73852e --- /dev/null +++ b/cloud/core/src/identifier.ts @@ -0,0 +1,26 @@ +import { ulid } from "ulid" +import { z } from "zod" + +export namespace Identifier { + const prefixes = { + account: "acc", + billing: "bil", + key: "key", + payment: "pay", + usage: "usg", + user: "usr", + workspace: "wrk", + } as const + + export function create(prefix: keyof typeof prefixes, given?: string): string { + if (given) { + if (given.startsWith(prefixes[prefix])) return given + throw new Error(`ID ${given} does not start with ${prefixes[prefix]}`) + } + return [prefixes[prefix], ulid()].join("_") + } + + export function schema(prefix: keyof typeof prefixes) { + return z.string().startsWith(prefixes[prefix]) + } +} diff --git a/cloud/core/src/schema/account.sql.ts b/cloud/core/src/schema/account.sql.ts new file mode 100644 index 000000000..1733f0a15 --- /dev/null +++ b/cloud/core/src/schema/account.sql.ts @@ -0,0 +1,12 @@ +import { pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core" +import { id, timestamps } from "../drizzle/types" + +export const AccountTable = pgTable( + "account", + { + id: id(), + ...timestamps, + email: varchar("email", { length: 255 }).notNull(), + }, + (table) => [uniqueIndex("email").on(table.email)], +) diff --git a/cloud/core/src/schema/billing.sql.ts b/cloud/core/src/schema/billing.sql.ts new file mode 100644 index 000000000..96b29f5de --- /dev/null +++ b/cloud/core/src/schema/billing.sql.ts @@ -0,0 +1,45 @@ +import { bigint, boolean, integer, pgTable, varchar } from "drizzle-orm/pg-core" +import { timestamps, workspaceColumns } from "../drizzle/types" +import { workspaceIndexes } from "./workspace.sql" + +export const BillingTable = pgTable( + "billing", + { + ...workspaceColumns, + ...timestamps, + customerID: varchar("customer_id", { length: 255 }), + paymentMethodID: varchar("payment_method_id", { length: 255 }), + paymentMethodLast4: varchar("payment_method_last4", { length: 4 }), + balance: bigint("balance", { mode: "number" }).notNull(), + reload: boolean("reload"), + }, + (table) => [...workspaceIndexes(table)], +) + +export const PaymentTable = pgTable( + "payment", + { + ...workspaceColumns, + ...timestamps, + customerID: varchar("customer_id", { length: 255 }), + paymentID: varchar("payment_id", { length: 255 }), + amount: bigint("amount", { mode: "number" }).notNull(), + }, + (table) => [...workspaceIndexes(table)], +) + +export const UsageTable = pgTable( + "usage", + { + ...workspaceColumns, + ...timestamps, + model: varchar("model", { length: 255 }).notNull(), + inputTokens: integer("input_tokens").notNull(), + outputTokens: integer("output_tokens").notNull(), + reasoningTokens: integer("reasoning_tokens"), + cacheReadTokens: integer("cache_read_tokens"), + cacheWriteTokens: integer("cache_write_tokens"), + cost: bigint("cost", { mode: "number" }).notNull(), + }, + (table) => [...workspaceIndexes(table)], +) diff --git a/cloud/core/src/schema/key.sql.ts b/cloud/core/src/schema/key.sql.ts new file mode 100644 index 000000000..240736b86 --- /dev/null +++ b/cloud/core/src/schema/key.sql.ts @@ -0,0 +1,16 @@ +import { text, pgTable, varchar, uniqueIndex } from "drizzle-orm/pg-core" +import { timestamps, utc, workspaceColumns } from "../drizzle/types" +import { workspaceIndexes } from "./workspace.sql" + +export const KeyTable = pgTable( + "key", + { + ...workspaceColumns, + ...timestamps, + userID: text("user_id").notNull(), + name: varchar("name", { length: 255 }).notNull(), + key: varchar("key", { length: 255 }).notNull(), + timeUsed: utc("time_used"), + }, + (table) => [...workspaceIndexes(table), uniqueIndex("global_key").on(table.key)], +) diff --git a/cloud/core/src/schema/user.sql.ts b/cloud/core/src/schema/user.sql.ts new file mode 100644 index 000000000..34cbd6beb --- /dev/null +++ b/cloud/core/src/schema/user.sql.ts @@ -0,0 +1,16 @@ +import { text, pgTable, uniqueIndex, varchar, integer } from "drizzle-orm/pg-core" +import { timestamps, utc, workspaceColumns } from "../drizzle/types" +import { workspaceIndexes } from "./workspace.sql" + +export const UserTable = pgTable( + "user", + { + ...workspaceColumns, + ...timestamps, + email: text("email").notNull(), + name: varchar("name", { length: 255 }).notNull(), + timeSeen: utc("time_seen"), + color: integer("color"), + }, + (table) => [...workspaceIndexes(table), uniqueIndex("user_email").on(table.workspaceID, table.email)], +) diff --git a/cloud/core/src/schema/workspace.sql.ts b/cloud/core/src/schema/workspace.sql.ts new file mode 100644 index 000000000..3e9379e1f --- /dev/null +++ b/cloud/core/src/schema/workspace.sql.ts @@ -0,0 +1,25 @@ +import { primaryKey, foreignKey, pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core" +import { timestamps, ulid } from "../drizzle/types" + +export const WorkspaceTable = pgTable( + "workspace", + { + id: ulid("id").notNull().primaryKey(), + slug: varchar("slug", { length: 255 }), + name: varchar("name", { length: 255 }), + ...timestamps, + }, + (table) => [uniqueIndex("slug").on(table.slug)], +) + +export function workspaceIndexes(table: any) { + return [ + primaryKey({ + columns: [table.workspaceID, table.id], + }), + foreignKey({ + foreignColumns: [WorkspaceTable.id], + columns: [table.workspaceID], + }), + ] +} diff --git a/cloud/core/src/util/fn.ts b/cloud/core/src/util/fn.ts new file mode 100644 index 000000000..038a50719 --- /dev/null +++ b/cloud/core/src/util/fn.ts @@ -0,0 +1,14 @@ +import { z } from "zod" + +export function fn( + schema: T, + cb: (input: z.output) => Result, +) { + const result = (input: z.input) => { + const parsed = schema.parse(input) + return cb(parsed) + } + result.force = (input: z.input) => cb(input) + result.schema = schema + return result +} diff --git a/cloud/core/src/util/log.ts b/cloud/core/src/util/log.ts new file mode 100644 index 000000000..4f2d25c13 --- /dev/null +++ b/cloud/core/src/util/log.ts @@ -0,0 +1,55 @@ +import { Context } from "../context" + +export namespace Log { + const ctx = Context.create<{ + tags: Record + }>() + + export function create(tags?: Record) { + tags = tags || {} + + const result = { + info(message?: any, extra?: Record) { + const prefix = Object.entries({ + ...use().tags, + ...tags, + ...extra, + }) + .map(([key, value]) => `${key}=${value}`) + .join(" ") + console.log(prefix, message) + return result + }, + tag(key: string, value: string) { + if (tags) tags[key] = value + return result + }, + clone() { + return Log.create({ ...tags }) + }, + } + + return result + } + + export function provide(tags: Record, cb: () => R) { + const existing = use() + return ctx.provide( + { + tags: { + ...existing.tags, + ...tags, + }, + }, + cb, + ) + } + + function use() { + try { + return ctx.use() + } catch (e) { + return { tags: {} } + } + } +} diff --git a/cloud/core/src/util/price.ts b/cloud/core/src/util/price.ts new file mode 100644 index 000000000..abdbca032 --- /dev/null +++ b/cloud/core/src/util/price.ts @@ -0,0 +1,3 @@ +export function centsToMicroCents(amount: number) { + return Math.round(amount * 1000000) +} diff --git a/cloud/core/src/workspace.ts b/cloud/core/src/workspace.ts new file mode 100644 index 000000000..532b22963 --- /dev/null +++ b/cloud/core/src/workspace.ts @@ -0,0 +1,48 @@ +import { z } from "zod" +import { fn } from "./util/fn" +import { centsToMicroCents } from "./util/price" +import { Actor } from "./actor" +import { Database, eq } from "./drizzle" +import { Identifier } from "./identifier" +import { UserTable } from "./schema/user.sql" +import { BillingTable } from "./schema/billing.sql" +import { WorkspaceTable } from "./schema/workspace.sql" + +export namespace Workspace { + export const create = fn(z.void(), async () => { + const account = Actor.assert("account") + const workspaceID = Identifier.create("workspace") + await Database.transaction(async (tx) => { + await tx.insert(WorkspaceTable).values({ + id: workspaceID, + }) + await tx.insert(UserTable).values({ + workspaceID, + id: Identifier.create("user"), + email: account.properties.email, + name: "", + }) + await tx.insert(BillingTable).values({ + workspaceID, + id: Identifier.create("billing"), + balance: centsToMicroCents(100), + }) + }) + return workspaceID + }) + + export async function list() { + const account = Actor.assert("account") + return Database.use(async (tx) => { + return tx + .select({ + id: WorkspaceTable.id, + slug: WorkspaceTable.slug, + name: WorkspaceTable.name, + }) + .from(UserTable) + .innerJoin(WorkspaceTable, eq(UserTable.workspaceID, WorkspaceTable.id)) + .where(eq(UserTable.email, account.properties.email)) + }) + } +} diff --git a/cloud/core/sst-env.d.ts b/cloud/core/sst-env.d.ts new file mode 100644 index 000000000..b6a7e9066 --- /dev/null +++ b/cloud/core/sst-env.d.ts @@ -0,0 +1,9 @@ +/* This file is auto-generated by SST. Do not edit. */ +/* tslint:disable */ +/* eslint-disable */ +/* deno-fmt-ignore-file */ + +/// + +import "sst" +export {} \ No newline at end of file diff --git a/cloud/core/tsconfig.json b/cloud/core/tsconfig.json new file mode 100644 index 000000000..0faf16aab --- /dev/null +++ b/cloud/core/tsconfig.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@tsconfig/node22/tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "bundler", + "types": ["@cloudflare/workers-types", "node"] + } +} diff --git a/cloud/function/package.json b/cloud/function/package.json new file mode 100644 index 000000000..b48bcd741 --- /dev/null +++ b/cloud/function/package.json @@ -0,0 +1,23 @@ +{ + "name": "@opencode/cloud-function", + "version": "0.3.130", + "$schema": "https://json.schemastore.org/package.json", + "private": true, + "type": "module", + "devDependencies": { + "@cloudflare/workers-types": "4.20250522.0", + "@types/node": "catalog:", + "openai": "5.11.0", + "typescript": "catalog:" + }, + "dependencies": { + "@ai-sdk/anthropic": "2.0.0", + "@ai-sdk/openai": "2.0.2", + "@ai-sdk/openai-compatible": "1.0.1", + "@hono/zod-validator": "catalog:", + "@openauthjs/openauth": "0.0.0-20250322224806", + "ai": "catalog:", + "hono": "catalog:", + "zod": "catalog:" + } +} diff --git a/cloud/function/src/auth.ts b/cloud/function/src/auth.ts new file mode 100644 index 000000000..5eacb7a72 --- /dev/null +++ b/cloud/function/src/auth.ts @@ -0,0 +1,68 @@ +import { Resource } from "sst" +import { z } from "zod" +import { issuer } from "@openauthjs/openauth" +import { createSubjects } from "@openauthjs/openauth/subject" +import { GithubProvider } from "@openauthjs/openauth/provider/github" +import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare" +import { Account } from "@opencode/cloud-core/account.js" + +type Env = { + AuthStorage: KVNamespace +} + +export const subjects = createSubjects({ + account: z.object({ + accountID: z.string(), + email: z.string(), + }), + user: z.object({ + userID: z.string(), + workspaceID: z.string(), + }), +}) + +export default { + async fetch(request: Request, env: Env, ctx: ExecutionContext) { + return issuer({ + providers: { + github: GithubProvider({ + clientID: Resource.GITHUB_CLIENT_ID_CONSOLE.value, + clientSecret: Resource.GITHUB_CLIENT_SECRET_CONSOLE.value, + scopes: ["read:user", "user:email"], + }), + }, + storage: CloudflareStorage({ + namespace: env.AuthStorage, + }), + subjects, + async success(ctx, response) { + console.log(response) + + let email: string | undefined + + if (response.provider === "github") { + const userResponse = await fetch("https://api.github.com/user", { + headers: { + Authorization: `Bearer ${response.tokenset.access}`, + "User-Agent": "opencode", + Accept: "application/vnd.github+json", + }, + }) + const user = (await userResponse.json()) as { email: string } + email = user.email + } else throw new Error("Unsupported provider") + + if (!email) throw new Error("No email found") + + let accountID = await Account.fromEmail(email).then((x) => x?.id) + if (!accountID) { + console.log("creating account for", email) + accountID = await Account.create({ + email: email!, + }) + } + return ctx.subject("account", accountID, { accountID, email }) + }, + }).fetch(request, env, ctx) + }, +} diff --git a/packages/function/src/gateway.ts b/cloud/function/src/gateway.ts similarity index 54% rename from packages/function/src/gateway.ts rename to cloud/function/src/gateway.ts index 17e9f5094..aefea7ac4 100644 --- a/packages/function/src/gateway.ts +++ b/cloud/function/src/gateway.ts @@ -1,15 +1,82 @@ -import { Hono, Context, Next } from "hono" +import { z } from "zod" +import { Hono, MiddlewareHandler } from "hono" +import { cors } from "hono/cors" +import { HTTPException } from "hono/http-exception" +import { zValidator } from "@hono/zod-validator" import { Resource } from "sst" import { generateText, streamText } from "ai" import { createAnthropic } from "@ai-sdk/anthropic" import { createOpenAI } from "@ai-sdk/openai" import { createOpenAICompatible } from "@ai-sdk/openai-compatible" -import { type LanguageModelV2Prompt } from "@ai-sdk/provider" +import type { LanguageModelV2Usage, LanguageModelV2Prompt } from "@ai-sdk/provider" import { type ChatCompletionCreateParamsBase } from "openai/resources/chat/completions" +import { Actor } from "@opencode/cloud-core/actor.js" +import { and, Database, eq, sql } from "@opencode/cloud-core/drizzle/index.js" +import { UserTable } from "@opencode/cloud-core/schema/user.sql.js" +import { KeyTable } from "@opencode/cloud-core/schema/key.sql.js" +import { createClient } from "@openauthjs/openauth/client" +import { Log } from "@opencode/cloud-core/util/log.js" +import { Billing } from "@opencode/cloud-core/billing.js" +import { Workspace } from "@opencode/cloud-core/workspace.js" +import { BillingTable, PaymentTable, UsageTable } from "@opencode/cloud-core/schema/billing.sql.js" +import { centsToMicroCents } from "@opencode/cloud-core/util/price.js" +import { Identifier } from "../../core/src/identifier" type Env = {} -const auth = async (c: Context, next: Next) => { +let _client: ReturnType +const client = () => { + if (_client) return _client + _client = createClient({ + clientID: "api", + issuer: Resource.AUTH_API_URL.value, + }) + return _client +} + +const SUPPORTED_MODELS = { + "anthropic/claude-sonnet-4": { + input: 0.0000015, + output: 0.000006, + reasoning: 0.0000015, + cacheRead: 0.0000001, + cacheWrite: 0.0000001, + model: () => + createAnthropic({ + apiKey: Resource.ANTHROPIC_API_KEY.value, + })("claude-sonnet-4-20250514"), + }, + "openai/gpt-4.1": { + input: 0.0000015, + output: 0.000006, + reasoning: 0.0000015, + cacheRead: 0.0000001, + cacheWrite: 0.0000001, + model: () => + createOpenAI({ + apiKey: Resource.OPENAI_API_KEY.value, + })("gpt-4.1"), + }, + "zhipuai/glm-4.5-flash": { + input: 0, + output: 0, + reasoning: 0, + cacheRead: 0, + cacheWrite: 0, + model: () => + createOpenAICompatible({ + name: "Zhipu AI", + baseURL: "https://api.z.ai/api/paas/v4", + apiKey: Resource.ZHIPU_API_KEY.value, + })("glm-4.5-flash"), + }, +} + +const log = Log.create({ + namespace: "api", +}) + +const GatewayAuth: MiddlewareHandler = async (c, next) => { const authHeader = c.req.header("authorization") if (!authHeader || !authHeader.startsWith("Bearer ")) { @@ -28,8 +95,19 @@ const auth = async (c: Context, next: Next) => { const apiKey = authHeader.split(" ")[1] - // Replace with your validation logic - if (apiKey !== Resource.OPENCODE_API_KEY.value) { + // Check against KeyTable + const keyRecord = await Database.use((tx) => + tx + .select({ + id: KeyTable.id, + workspaceID: KeyTable.workspaceID, + }) + .from(KeyTable) + .where(eq(KeyTable.key, apiKey)) + .then((rows) => rows[0]), + ) + + if (!keyRecord) { return c.json( { error: { @@ -43,38 +121,70 @@ const auth = async (c: Context, next: Next) => { ) } + c.set("keyRecord", keyRecord) await next() } -export default new Hono<{ Bindings: Env }>() + +const RestAuth: MiddlewareHandler = async (c, next) => { + const authorization = c.req.header("authorization") + if (!authorization) { + return Actor.provide("public", {}, next) + } + const token = authorization.split(" ")[1] + if (!token) + throw new HTTPException(403, { + message: "Bearer token is required.", + }) + + const verified = await client().verify(token) + if (verified.err) { + throw new HTTPException(403, { + message: "Invalid token.", + }) + } + let subject = verified.subject as Actor.Info + if (subject.type === "account") { + const workspaceID = c.req.header("x-opencode-workspace") + const email = subject.properties.email + if (workspaceID) { + const user = await Database.use((tx) => + tx + .select({ + id: UserTable.id, + workspaceID: UserTable.workspaceID, + email: UserTable.email, + }) + .from(UserTable) + .where(and(eq(UserTable.email, email), eq(UserTable.workspaceID, workspaceID))) + .then((rows) => rows[0]), + ) + if (!user) + throw new HTTPException(403, { + message: "You do not have access to this workspace.", + }) + subject = { + type: "user", + properties: { + userID: user.id, + workspaceID: workspaceID, + email: user.email, + }, + } + } + } + await Actor.provide(subject.type, subject.properties, next) +} + +const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; workspaceID: string } } }>() .get("/", (c) => c.text("Hello, world!")) - .post("/v1/chat/completions", auth, async (c) => { + .post("/v1/chat/completions", GatewayAuth, async (c) => { try { const body = await c.req.json() console.log(body) - const model = (() => { - const [provider, ...parts] = body.model.split("/") - const model = parts.join("/") - if (provider === "anthropic" && model === "claude-sonnet-4") { - return createAnthropic({ - apiKey: Resource.ANTHROPIC_API_KEY.value, - })("claude-sonnet-4-20250514") - } - if (provider === "openai" && model === "gpt-4.1") { - return createOpenAI({ - apiKey: Resource.OPENAI_API_KEY.value, - })("gpt-4.1") - } - if (provider === "zhipuai" && model === "glm-4.5-flash") { - return createOpenAICompatible({ - name: "Zhipu AI", - baseURL: "https://api.z.ai/api/paas/v4", - apiKey: Resource.ZHIPU_API_KEY.value, - })("glm-4.5-flash") - } - throw new Error(`Unsupported provider: ${provider}`) - })() + const model = SUPPORTED_MODELS[body.model as keyof typeof SUPPORTED_MODELS]?.model() + if (!model) throw new Error(`Unsupported model: ${body.model}`) const requestBody = transformOpenAIRequestToAiSDK() @@ -263,6 +373,7 @@ export default new Hono<{ Bindings: Env }>() model, ...requestBody, }) + await trackUsage(body.model, response.usage) return c.json({ id: `chatcmpl-${Date.now()}`, object: "chat.completion" as const, @@ -492,8 +603,285 @@ export default new Hono<{ Bindings: Env }>() return prompt } } + + async function trackUsage(model: string, usage: LanguageModelV2Usage) { + const keyRecord = c.get("keyRecord") + if (!keyRecord) return + + const modelData = SUPPORTED_MODELS[model as keyof typeof SUPPORTED_MODELS] + if (!modelData) throw new Error(`Unsupported model: ${model}`) + + const inputCost = modelData.input * (usage.inputTokens ?? 0) + const outputCost = modelData.output * (usage.outputTokens ?? 0) + const reasoningCost = modelData.reasoning * (usage.reasoningTokens ?? 0) + const cacheReadCost = modelData.cacheRead * (usage.cachedInputTokens ?? 0) + const cacheWriteCost = modelData.cacheWrite * (usage.outputTokens ?? 0) + + const totalCost = inputCost + outputCost + reasoningCost + cacheReadCost + cacheWriteCost + + await Actor.provide("system", { workspaceID: keyRecord.workspaceID }, async () => { + await Billing.consume({ + model, + inputTokens: usage.inputTokens ?? 0, + outputTokens: usage.outputTokens ?? 0, + reasoningTokens: usage.reasoningTokens ?? 0, + cacheReadTokens: usage.cachedInputTokens ?? 0, + cacheWriteTokens: usage.outputTokens ?? 0, + costInCents: totalCost * 100, + }) + }) + + await Database.use((tx) => + tx + .update(KeyTable) + .set({ timeUsed: sql`now()` }) + .where(eq(KeyTable.id, keyRecord.id)), + ) + } } catch (error: any) { return c.json({ error: { message: error.message } }, 500) } }) + .use("/*", cors()) + .use(RestAuth) + .get("/rest/account", async (c) => { + const account = Actor.assert("account") + let workspaces = await Workspace.list() + if (workspaces.length === 0) { + await Workspace.create() + workspaces = await Workspace.list() + } + return c.json({ + id: account.properties.accountID, + email: account.properties.email, + workspaces, + }) + }) + .get("/billing/info", async (c) => { + const billing = await Billing.get() + const payments = await Database.use((tx) => + tx + .select() + .from(PaymentTable) + .where(eq(PaymentTable.workspaceID, Actor.workspace())) + .orderBy(sql`${PaymentTable.timeCreated} DESC`) + .limit(100), + ) + const usage = await Database.use((tx) => + tx + .select() + .from(UsageTable) + .where(eq(UsageTable.workspaceID, Actor.workspace())) + .orderBy(sql`${UsageTable.timeCreated} DESC`) + .limit(100), + ) + return c.json({ billing, payments, usage }) + }) + .post( + "/billing/checkout", + zValidator( + "json", + z.custom<{ + success_url: string + cancel_url: string + }>(), + ), + async (c) => { + const account = Actor.assert("user") + + const body = await c.req.json() + + const customer = await Billing.get() + const session = await Billing.stripe().checkout.sessions.create({ + mode: "payment", + line_items: [ + { + price_data: { + currency: "usd", + product_data: { + name: "OpenControl credits", + }, + unit_amount: 2000, // $20 minimum + }, + quantity: 1, + }, + ], + payment_intent_data: { + setup_future_usage: "on_session", + }, + ...(customer.customerID + ? { customer: customer.customerID } + : { + customer_email: account.properties.email, + customer_creation: "always", + }), + metadata: { + workspaceID: Actor.workspace(), + }, + currency: "usd", + payment_method_types: ["card"], + success_url: body.success_url, + cancel_url: body.cancel_url, + }) + + return c.json({ + url: session.url, + }) + }, + ) + .post("/billing/portal", async (c) => { + const body = await c.req.json() + + const customer = await Billing.get() + if (!customer?.customerID) { + throw new Error("No stripe customer ID") + } + + const session = await Billing.stripe().billingPortal.sessions.create({ + customer: customer.customerID, + return_url: body.return_url, + }) + + return c.json({ + url: session.url, + }) + }) + .post("/stripe/webhook", async (c) => { + const body = await Billing.stripe().webhooks.constructEventAsync( + await c.req.text(), + c.req.header("stripe-signature")!, + Resource.STRIPE_WEBHOOK_SECRET.value, + ) + + console.log(body.type, JSON.stringify(body, null, 2)) + if (body.type === "checkout.session.completed") { + const workspaceID = body.data.object.metadata?.workspaceID + const customerID = body.data.object.customer as string + const paymentID = body.data.object.payment_intent as string + const amount = body.data.object.amount_total + + if (!workspaceID) throw new Error("Workspace ID not found") + if (!customerID) throw new Error("Customer ID not found") + if (!amount) throw new Error("Amount not found") + if (!paymentID) throw new Error("Payment ID not found") + + await Actor.provide("system", { workspaceID }, async () => { + const customer = await Billing.get() + if (customer?.customerID && customer.customerID !== customerID) throw new Error("Customer ID mismatch") + + // set customer metadata + if (!customer?.customerID) { + await Billing.stripe().customers.update(customerID, { + metadata: { + workspaceID, + }, + }) + } + + // get payment method for the payment intent + const paymentIntent = await Billing.stripe().paymentIntents.retrieve(paymentID, { + expand: ["payment_method"], + }) + const paymentMethod = paymentIntent.payment_method + if (!paymentMethod || typeof paymentMethod === "string") throw new Error("Payment method not expanded") + + await Database.transaction(async (tx) => { + await tx + .update(BillingTable) + .set({ + balance: sql`${BillingTable.balance} + ${centsToMicroCents(amount)}`, + customerID, + paymentMethodID: paymentMethod.id, + paymentMethodLast4: paymentMethod.card!.last4, + }) + .where(eq(BillingTable.workspaceID, workspaceID)) + await tx.insert(PaymentTable).values({ + workspaceID, + id: Identifier.create("payment"), + amount: centsToMicroCents(amount), + paymentID, + customerID, + }) + }) + }) + } + + console.log("finished handling") + + return c.json("ok", 200) + }) + .get("/keys", async (c) => { + const user = Actor.assert("user") + + const keys = await Database.use((tx) => + tx + .select({ + id: KeyTable.id, + name: KeyTable.name, + key: KeyTable.key, + userID: KeyTable.userID, + timeCreated: KeyTable.timeCreated, + timeUsed: KeyTable.timeUsed, + }) + .from(KeyTable) + .where(eq(KeyTable.workspaceID, user.properties.workspaceID)) + .orderBy(sql`${KeyTable.timeCreated} DESC`), + ) + + return c.json({ keys }) + }) + .post("/keys", zValidator("json", z.object({ name: z.string().min(1).max(255) })), async (c) => { + const user = Actor.assert("user") + const { name } = c.req.valid("json") + + // Generate secret key: sk- + 64 random characters (upper, lower, numbers) + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + let randomPart = "" + for (let i = 0; i < 64; i++) { + randomPart += chars.charAt(Math.floor(Math.random() * chars.length)) + } + const secretKey = `sk-${randomPart}` + + const keyRecord = await Database.use((tx) => + tx + .insert(KeyTable) + .values({ + id: Identifier.create("key"), + workspaceID: user.properties.workspaceID, + userID: user.properties.userID, + name, + key: secretKey, + timeUsed: null, + }) + .returning(), + ) + + return c.json({ + key: secretKey, + id: keyRecord[0].id, + name: keyRecord[0].name, + created: keyRecord[0].timeCreated, + }) + }) + .delete("/keys/:id", async (c) => { + const user = Actor.assert("user") + const keyId = c.req.param("id") + + const result = await Database.use((tx) => + tx + .delete(KeyTable) + .where(and(eq(KeyTable.id, keyId), eq(KeyTable.workspaceID, user.properties.workspaceID))) + .returning({ id: KeyTable.id }), + ) + + if (result.length === 0) { + return c.json({ error: "Key not found" }, 404) + } + + return c.json({ success: true, id: result[0].id }) + }) .all("*", (c) => c.text("Not Found")) + +export type ApiType = typeof app + +export default app diff --git a/cloud/function/sst-env.d.ts b/cloud/function/sst-env.d.ts new file mode 100644 index 000000000..4e2b15921 --- /dev/null +++ b/cloud/function/sst-env.d.ts @@ -0,0 +1,88 @@ +/* This file is auto-generated by SST. Do not edit. */ +/* tslint:disable */ +/* eslint-disable */ +/* deno-fmt-ignore-file */ + +import "sst" +declare module "sst" { + export interface Resource { + "ANTHROPIC_API_KEY": { + "type": "sst.sst.Secret" + "value": string + } + "AUTH_API_URL": { + "type": "sst.sst.Linkable" + "value": string + } + "Console": { + "type": "sst.cloudflare.StaticSite" + "url": string + } + "DATABASE_PASSWORD": { + "type": "sst.sst.Secret" + "value": string + } + "DATABASE_USERNAME": { + "type": "sst.sst.Secret" + "value": string + } + "Database": { + "database": string + "host": string + "password": string + "port": number + "type": "sst.sst.Linkable" + "username": string + } + "GITHUB_APP_ID": { + "type": "sst.sst.Secret" + "value": string + } + "GITHUB_APP_PRIVATE_KEY": { + "type": "sst.sst.Secret" + "value": string + } + "GITHUB_CLIENT_ID_CONSOLE": { + "type": "sst.sst.Secret" + "value": string + } + "GITHUB_CLIENT_SECRET_CONSOLE": { + "type": "sst.sst.Secret" + "value": string + } + "OPENAI_API_KEY": { + "type": "sst.sst.Secret" + "value": string + } + "STRIPE_SECRET_KEY": { + "type": "sst.sst.Secret" + "value": string + } + "STRIPE_WEBHOOK_SECRET": { + "type": "sst.sst.Linkable" + "value": string + } + "Web": { + "type": "sst.cloudflare.Astro" + "url": string + } + "ZHIPU_API_KEY": { + "type": "sst.sst.Secret" + "value": string + } + } +} +// cloudflare +import * as cloudflare from "@cloudflare/workers-types"; +declare module "sst" { + export interface Resource { + "Api": cloudflare.Service + "AuthApi": cloudflare.Service + "AuthStorage": cloudflare.KVNamespace + "Bucket": cloudflare.R2Bucket + "GatewayApi": cloudflare.Service + } +} + +import "sst" +export {} \ No newline at end of file diff --git a/cloud/function/tsconfig.json b/cloud/function/tsconfig.json new file mode 100644 index 000000000..0faf16aab --- /dev/null +++ b/cloud/function/tsconfig.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@tsconfig/node22/tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "bundler", + "types": ["@cloudflare/workers-types", "node"] + } +} diff --git a/cloud/web/.gitignore b/cloud/web/.gitignore new file mode 100644 index 000000000..76add878f --- /dev/null +++ b/cloud/web/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/cloud/web/index.html b/cloud/web/index.html new file mode 100644 index 000000000..55c54c1f1 --- /dev/null +++ b/cloud/web/index.html @@ -0,0 +1,38 @@ + + + + + + OpenControl + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + diff --git a/cloud/web/npm-debug.log b/cloud/web/npm-debug.log new file mode 100644 index 000000000..07b0649fe --- /dev/null +++ b/cloud/web/npm-debug.log @@ -0,0 +1,29 @@ +0 info it worked if it ends with ok +1 verbose cli [ +1 verbose cli '/usr/local/bin/node', +1 verbose cli '/Users/frank/Sites/opencode/node_modules/.bin/npm', +1 verbose cli 'run', +1 verbose cli 'dev' +1 verbose cli ] +2 info using npm@2.15.12 +3 info using node@v20.18.1 +4 verbose stack Error: Invalid name: "@opencode/cloud/web" +4 verbose stack at ensureValidName (/Users/frank/Sites/opencode/node_modules/npm/node_modules/normalize-package-data/lib/fixer.js:336:15) +4 verbose stack at Object.fixNameField (/Users/frank/Sites/opencode/node_modules/npm/node_modules/normalize-package-data/lib/fixer.js:215:5) +4 verbose stack at /Users/frank/Sites/opencode/node_modules/npm/node_modules/normalize-package-data/lib/normalize.js:32:38 +4 verbose stack at Array.forEach () +4 verbose stack at normalize (/Users/frank/Sites/opencode/node_modules/npm/node_modules/normalize-package-data/lib/normalize.js:31:15) +4 verbose stack at final (/Users/frank/Sites/opencode/node_modules/npm/node_modules/read-package-json/read-json.js:349:5) +4 verbose stack at then (/Users/frank/Sites/opencode/node_modules/npm/node_modules/read-package-json/read-json.js:124:5) +4 verbose stack at ReadFileContext. (/Users/frank/Sites/opencode/node_modules/npm/node_modules/read-package-json/read-json.js:295:20) +4 verbose stack at ReadFileContext.callback (/Users/frank/Sites/opencode/node_modules/npm/node_modules/graceful-fs/graceful-fs.js:78:16) +4 verbose stack at FSReqCallback.readFileAfterOpen [as oncomplete] (node:fs:299:13) +5 verbose cwd /Users/frank/Sites/opencode/cloud/web +6 error Darwin 24.5.0 +7 error argv "/usr/local/bin/node" "/Users/frank/Sites/opencode/node_modules/.bin/npm" "run" "dev" +8 error node v20.18.1 +9 error npm v2.15.12 +10 error Invalid name: "@opencode/cloud/web" +11 error If you need help, you may report this error at: +11 error +12 verbose exit [ 1, true ] diff --git a/cloud/web/package.json b/cloud/web/package.json new file mode 100644 index 000000000..b39a77723 --- /dev/null +++ b/cloud/web/package.json @@ -0,0 +1,32 @@ +{ + "name": "@opencode/cloud-web", + "version": "0.0.0", + "private": true, + "description": "", + "type": "module", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "bun build:server && bun build:client", + "build:client": "vite build --outDir dist/client", + "build:server": "vite build --ssr src/entry-server.tsx --outDir dist/server", + "serve": "vite preview", + "sst:dev": "bun sst shell --target Console -- bun dev" + }, + "license": "MIT", + "devDependencies": { + "typescript": "catalog:", + "vite": "6.2.2", + "vite-plugin-pages": "0.32.5", + "vite-plugin-solid": "2.11.6" + }, + "dependencies": { + "@kobalte/core": "0.13.9", + "@openauthjs/solid": "0.0.0-20250322224806", + "@solid-primitives/storage": "4.3.1", + "@solidjs/meta": "0.29.4", + "@solidjs/router": "0.15.3", + "solid-js": "1.9.5", + "solid-list": "0.3.0" + } +} diff --git a/cloud/web/public/favicon-dark.svg b/cloud/web/public/favicon-dark.svg new file mode 100644 index 000000000..9b707ea49 --- /dev/null +++ b/cloud/web/public/favicon-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/cloud/web/public/favicon.ico b/cloud/web/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0ed3bf15e762ea51d22694175bd97a38f4442854 GIT binary patch literal 171515 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 QxGP2H4rWF{?QEz0A6aO1wg3PC literal 0 HcmV?d00001 diff --git a/cloud/web/public/favicon.svg b/cloud/web/public/favicon.svg new file mode 100644 index 000000000..5e7cf124f --- /dev/null +++ b/cloud/web/public/favicon.svg @@ -0,0 +1,3 @@ + + + diff --git a/cloud/web/public/social-share.png b/cloud/web/public/social-share.png new file mode 100644 index 0000000000000000000000000000000000000000..72d36a9722adc503f6c847de8d045861dd273357 GIT binary patch literal 25855 zcmeFYXH-*L)HVuO5Jm7<5TuEUN*9pcRIEe<>4a`5LMTG$5I|AzNR<*GG*Rg!)PM=q zMra90N$5c#2^b(qix2{LbG|XY@BX>t{<-(xI|i_`*3Q^_l{wd(^LZZrHGOpN_|bDm zxwyEFKe&I}jEn1F4Hp+r@R7qnkI9V)d*H(N?7l6Gi|g2_{ap8C@^+fE13*Y6{%SY@kok&u+IoF~3Pxplzxi$ZsA5(tldrU{T`*x#rx`XF( z3uQVM8eUOR`N%S)a5+k1;fOnpjbpSwY7L5#nDAC-X#j)Z8YtVta0AzvI6hzwTwG6) zT*vmCCI8d>|7kV!+O$rwmZv86S9`_8M1O0JCie6>U;$h=h7Q{7_g*)S`Cm<^W7x)s^?z;; zIcOs`+`h+d_iOw6D}hiQwNXjfV30MMe5Y{RYe}LS>ic#E2flUtMP-V??%#@?iFVF} z;U2rm95e z20Lx0qEbg6aen_qQcUMc_}+Q=sS_wfUSaAe@Q#@}-L~gOo^F}`6#`=_6%DI6ID3

9QZOqcwshvAvdVqK|bk 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 F{tbgBVFLgF literal 0 HcmV?d00001 diff --git a/cloud/web/scripts/render.mjs b/cloud/web/scripts/render.mjs new file mode 100644 index 000000000..5ccb35ff1 --- /dev/null +++ b/cloud/web/scripts/render.mjs @@ -0,0 +1,24 @@ +import fs from "fs" +import path from "path" +import { generateHydrationScript, getAssets } from "solid-js/web" + +const dist = import.meta.resolve("../dist").replace("file://", "") +const serverEntry = await import("../dist/server/entry-server.js") +const template = fs.readFileSync(path.join(dist, "client/index.html"), "utf-8") +fs.writeFileSync(path.join(dist, "client/fallback.html"), template) + +const routes = ["/", "/foo"] +for (const route of routes) { + const { app } = serverEntry.render({ url: route }) + const html = template + .replace("", app) + .replace("", generateHydrationScript()) + .replace("", getAssets()) + const filePath = dist + `/client${route === "/" ? "/index" : route}.html` + fs.mkdirSync(path.dirname(filePath), { + recursive: true, + }) + fs.writeFileSync(filePath, html) + + console.log(`Pre-rendered: ${filePath}`) +} diff --git a/cloud/web/src/app.tsx b/cloud/web/src/app.tsx new file mode 100644 index 000000000..aae71ddde --- /dev/null +++ b/cloud/web/src/app.tsx @@ -0,0 +1,42 @@ +/// + +import { Router } from "@solidjs/router" +import routes from "~solid-pages" +import "./ui/style/index.css" +import { MetaProvider } from "@solidjs/meta" +import { AccountProvider } from "./components/context-account" +import { DialogProvider } from "./ui/context-dialog" +import { DialogString } from "./ui/dialog-string" +import { DialogSelect } from "./ui/dialog-select" +import { ThemeProvider } from "./components/context-theme" +import { Suspense } from "solid-js" +import { OpenAuthProvider } from "./components/context-openauth" + +export function App(props: { url?: string }) { + return ( + + + + + + + + + { + return <>{props.children} + }} + /> + + + + + + + ) +} diff --git a/cloud/web/src/assets/screenshot.png b/cloud/web/src/assets/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..5b6ad2ec68c15f98755b77821e0b20f01e6745ef GIT binary patch literal 449252 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 LcV_Q6arJ)yDZ9(D literal 0 HcmV?d00001 diff --git a/cloud/web/src/components/context-account.tsx b/cloud/web/src/components/context-account.tsx new file mode 100644 index 000000000..e6aabafd3 --- /dev/null +++ b/cloud/web/src/components/context-account.tsx @@ -0,0 +1,99 @@ +import { createContext, createEffect, ParentProps, Suspense, useContext } from "solid-js" +import { makePersisted } from "@solid-primitives/storage" +import { createStore } from "solid-js/store" +import { useOpenAuth } from "./context-openauth" +import { createAsync } from "@solidjs/router" +import { isServer } from "solid-js/web" + +type Storage = { + accounts: Record< + string, + { + id: string + email: string + workspaces: { + id: string + name: string + slug: string + }[] + } + > +} + +const context = createContext>() + +function init() { + const auth = useOpenAuth() + const [store, setStore] = makePersisted( + createStore({ + accounts: {}, + }), + { + name: "opencontrol.account", + }, + ) + + async function refresh(id: string) { + return fetch(import.meta.env.VITE_API_URL + "/rest/account", { + headers: { + authorization: `Bearer ${await auth.access(id)}`, + }, + }) + .then((val) => val.json()) + .then((val) => setStore("accounts", id, val as any)) + } + + createEffect((previous: string[]) => { + if (Object.keys(auth.all).length === 0) { + return [] + } + for (const item of Object.values(auth.all)) { + if (previous.includes(item.id)) continue + refresh(item.id) + } + return Object.keys(auth.all) + }, [] as string[]) + + const result = { + get all() { + return Object.keys(auth.all) + .map((id) => store.accounts[id]) + .filter(Boolean) + }, + get current() { + if (!auth.subject) return undefined + return store.accounts[auth.subject.id] + }, + refresh, + get ready() { + return Object.keys(auth.all).length === result.all.length + }, + } + + return result +} + +export function AccountProvider(props: ParentProps) { + const ctx = init() + const resource = createAsync(async () => { + await new Promise((resolve) => { + if (isServer) return resolve() + createEffect(() => { + if (ctx.ready) resolve() + }) + }) + return null + }) + return ( + + {resource()} + {props.children} + + ) +} + +export function useAccount() { + const result = useContext(context) + if (!result) throw new Error("no account context") + return result +} diff --git a/cloud/web/src/components/context-openauth.tsx b/cloud/web/src/components/context-openauth.tsx new file mode 100644 index 000000000..bd6a45dd1 --- /dev/null +++ b/cloud/web/src/components/context-openauth.tsx @@ -0,0 +1,180 @@ +import { createClient } from "@openauthjs/openauth/client" +import { makePersisted } from "@solid-primitives/storage" +import { createAsync } from "@solidjs/router" +import { + batch, + createContext, + createEffect, + createResource, + createSignal, + onMount, + ParentProps, + Show, + Suspense, + useContext, +} from "solid-js" +import { createStore, produce } from "solid-js/store" +import { isServer } from "solid-js/web" + +interface Storage { + subjects: Record + current?: string +} + +interface Context { + all: Record + subject?: SubjectInfo + switch(id: string): void + logout(id: string): void + access(id?: string): Promise + authorize(opts?: AuthorizeOptions): void +} + +export interface AuthorizeOptions { + redirectPath?: string + provider?: string +} + +interface SubjectInfo { + id: string + refresh: string +} + +interface AuthContextOpts { + issuer: string + clientID: string +} + +const context = createContext() + +export function OpenAuthProvider(props: ParentProps) { + const client = createClient({ + issuer: props.issuer, + clientID: props.clientID, + }) + const [storage, setStorage] = makePersisted( + createStore({ + subjects: {}, + }), + { + name: `${props.issuer}.auth`, + }, + ) + + const resource = createAsync(async () => { + if (isServer) return true + const hash = new URLSearchParams(window.location.search.substring(1)) + const code = hash.get("code") + const state = hash.get("state") + if (code && state) { + const oldState = sessionStorage.getItem("openauth.state") + const verifier = sessionStorage.getItem("openauth.verifier") + const redirect = sessionStorage.getItem("openauth.redirect") + if (redirect && verifier && oldState === state) { + const result = await client.exchange(code, redirect, verifier) + if (!result.err) { + const id = result.tokens.refresh.split(":").slice(0, -1).join(":") + batch(() => { + setStorage("subjects", id, { + id: id, + refresh: result.tokens.refresh, + }) + setStorage("current", id) + }) + } + } + } + return true + }) + + async function authorize(opts?: AuthorizeOptions) { + const redirect = new URL(window.location.origin + (opts?.redirectPath ?? "/")).toString() + const authorize = await client.authorize(redirect, "code", { + pkce: true, + provider: opts?.provider, + }) + sessionStorage.setItem("openauth.state", authorize.challenge.state) + sessionStorage.setItem("openauth.redirect", redirect) + if (authorize.challenge.verifier) sessionStorage.setItem("openauth.verifier", authorize.challenge.verifier) + window.location.href = authorize.url + } + + const accessCache = new Map() + const pendingRequests = new Map>() + async function access(id: string) { + const pending = pendingRequests.get(id) + if (pending) return pending + const promise = (async () => { + const existing = accessCache.get(id) + const subject = storage.subjects[id] + const access = await client.refresh(subject.refresh, { + access: existing, + }) + if (access.err) { + pendingRequests.delete(id) + ctx.logout(id) + return + } + if (access.tokens) { + setStorage("subjects", id, "refresh", access.tokens.refresh) + accessCache.set(id, access.tokens.access) + } + pendingRequests.delete(id) + return access.tokens?.access || existing! + })() + pendingRequests.set(id, promise) + return promise + } + + const ctx: Context = { + get all() { + return storage.subjects + }, + get subject() { + if (!storage.current) return + return storage.subjects[storage.current!] + }, + switch(id: string) { + if (!storage.subjects[id]) return + setStorage("current", id) + }, + authorize, + logout(id: string) { + if (!storage.subjects[id]) return + setStorage( + produce((s) => { + delete s.subjects[id] + if (s.current === id) s.current = Object.keys(s.subjects)[0] + }), + ) + }, + async access(id?: string) { + id = id || storage.current + if (!id) return + return access(id || storage.current!) + }, + } + + createEffect(() => { + if (!resource()) return + if (storage.current) return + const [first] = Object.keys(storage.subjects) + if (first) { + setStorage("current", first) + return + } + }) + + return ( + <> + {resource()} + {props.children} + + ) +} + +export function useOpenAuth() { + const result = useContext(context) + if (!result) throw new Error("no auth context") + return result +} diff --git a/cloud/web/src/components/context-theme.tsx b/cloud/web/src/components/context-theme.tsx new file mode 100644 index 000000000..7800aeca0 --- /dev/null +++ b/cloud/web/src/components/context-theme.tsx @@ -0,0 +1,39 @@ +import { createStore } from "solid-js/store" +import { makePersisted } from "@solid-primitives/storage" +import { createEffect } from "solid-js" +import { createInitializedContext } from "../util/context" +import { isServer } from "solid-js/web" + +interface Storage { + mode: "light" | "dark" +} + +export const { provider: ThemeProvider, use: useTheme } = + createInitializedContext("ThemeContext", () => { + const [store, setStore] = makePersisted( + createStore({ + mode: + !isServer && + window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches + ? "dark" + : "light", + }), + { + name: "theme", + }, + ) + createEffect(() => { + document.documentElement.setAttribute("data-color-mode", store.mode) + }) + + return { + setMode(mode: Storage["mode"]) { + setStore("mode", mode) + }, + get mode() { + return store.mode + }, + ready: true, + } + }) diff --git a/cloud/web/src/entry-client.tsx b/cloud/web/src/entry-client.tsx new file mode 100644 index 000000000..169e45a1e --- /dev/null +++ b/cloud/web/src/entry-client.tsx @@ -0,0 +1,13 @@ +/* @refresh reload */ + +import { hydrate, render } from "solid-js/web" +import { App } from "./app" + +if (import.meta.env.DEV) { + render(() => , document.getElementById("root")!) +} + +if (!import.meta.env.DEV) { + if ("_$HY" in window) hydrate(() => , document.getElementById("root")!) + else render(() => , document.getElementById("root")!) +} diff --git a/cloud/web/src/entry-server.tsx b/cloud/web/src/entry-server.tsx new file mode 100644 index 000000000..5dd33a149 --- /dev/null +++ b/cloud/web/src/entry-server.tsx @@ -0,0 +1,7 @@ +import { renderToStringAsync } from "solid-js/web" +import { App } from "./app" + +export async function render(props: { url: string }) { + const app = await renderToStringAsync(() => ) + return { app } +} diff --git a/cloud/web/src/pages/[workspace].tsx b/cloud/web/src/pages/[workspace].tsx new file mode 100644 index 000000000..c7481cb0d --- /dev/null +++ b/cloud/web/src/pages/[workspace].tsx @@ -0,0 +1,11 @@ +import { WorkspaceProvider } from "./components/context-workspace" +import { ParentProps } from "solid-js" +import Layout from "./components/layout" + +export default function Index(props: ParentProps) { + return ( + + {props.children} + + ) +} diff --git a/cloud/web/src/pages/[workspace]/billing.module.css b/cloud/web/src/pages/[workspace]/billing.module.css new file mode 100644 index 000000000..5e58892a5 --- /dev/null +++ b/cloud/web/src/pages/[workspace]/billing.module.css @@ -0,0 +1,56 @@ +.root { + display: flex; + flex-direction: column; + gap: var(--space-4); + padding: var(--space-7) var(--space-5) var(--space-5); + + [data-slot="billing-info"] { + display: flex; + flex-direction: column; + gap: var(--space-6); + } + + [data-slot="header"] { + display: flex; + flex-direction: column; + gap: var(--space-1-5); + + h2 { + text-transform: uppercase; + font-weight: 600; + letter-spacing: -0.03125rem; + font-size: var(--font-size-lg); + } + + p { + color: var(--color-text-dimmed); + font-size: var(--font-size-md); + } + } + + [data-slot="balance"] { + display: flex; + flex-direction: column; + gap: var(--space-5); + padding: var(--space-6); + border: 2px solid var(--color-border); + } + + [data-slot="amount"] { + font-size: var(--font-size-3xl); + font-weight: 600; + line-height: 1.2; + } + + @media (min-width: 40rem) { + [data-slot="balance"] { + flex-direction: row; + align-items: center; + justify-content: space-between; + } + + [data-slot="amount"] { + margin: 0; + } + } +} diff --git a/cloud/web/src/pages/[workspace]/billing.tsx b/cloud/web/src/pages/[workspace]/billing.tsx new file mode 100644 index 000000000..88bef5800 --- /dev/null +++ b/cloud/web/src/pages/[workspace]/billing.tsx @@ -0,0 +1,132 @@ +import { Button } from "../../ui/button" +import { useApi } from "../components/context-api" +import { createEffect, createSignal, createResource, For } from "solid-js" +import { useWorkspace } from "../components/context-workspace" +import style from "./billing.module.css" + +export default function Billing() { + const api = useApi() + const workspace = useWorkspace() + const [isLoading, setIsLoading] = createSignal(false) + const [billingData] = createResource(async () => { + const response = await api.billing.info.$get() + return response.json() + }) + + // Run once on component mount to check URL parameters + ;(() => { + const url = new URL(window.location.href) + const result = url.hash + + console.log("STRIPE RESULT", result) + + if (url.hash === "#success") { + setIsLoading(true) + // Remove the hash from the URL + window.history.replaceState(null, "", window.location.pathname + window.location.search) + } + })() + + createEffect((old?: number) => { + if (old && old !== billingData()?.billing?.balance) { + setIsLoading(false) + } + return billingData()?.billing?.balance + }) + + const handleBuyCredits = async () => { + try { + setIsLoading(true) + const baseUrl = window.location.href + const successUrl = new URL(baseUrl) + successUrl.hash = "success" + + const response = await api.billing.checkout + .$post({ + json: { + success_url: successUrl.toString(), + cancel_url: baseUrl, + }, + }) + .then((r) => r.json() as any) + window.location.href = response.url + } catch (error) { + console.error("Failed to get checkout URL:", error) + setIsLoading(false) + } + } + + return ( + <> +

+
+
+
+

Balance

+

Manage your billing and add credits to your account.

+
+ +
+

+ {(() => { + const balanceStr = ((billingData()?.billing?.balance ?? 0) / 100000000).toFixed(2) + return `$${balanceStr === "-0.00" ? "0.00" : balanceStr}` + })()} +

+ +
+
+ +
+
+

Payment History

+

Your recent payment transactions.

+
+ +
+ No payments found.

}> + {(payment) => ( +
+ {payment.id} + {" | "} + ${((payment.amount ?? 0) / 100000000).toFixed(2)} + {" | "} + {new Date(payment.timeCreated).toLocaleDateString()} +
+ )} +
+
+
+ +
+
+

Usage History

+

Your recent API usage and costs.

+
+ +
+ No usage found.

}> + {(usage) => ( +
+ {usage.model} + {" | "} + {usage.inputTokens + usage.outputTokens} tokens + {" | "} + ${((usage.cost ?? 0) / 100000000).toFixed(4)} + {" | "} + {new Date(usage.timeCreated).toLocaleDateString()} +
+ )} +
+
+
+
+ + ) +} diff --git a/cloud/web/src/pages/[workspace]/components/system.txt b/cloud/web/src/pages/[workspace]/components/system.txt new file mode 100644 index 000000000..6afd2e04d --- /dev/null +++ b/cloud/web/src/pages/[workspace]/components/system.txt @@ -0,0 +1,11 @@ +You are OpenControl, an interactive CLI tool that helps users execute various tasks. + +IMPORTANT: If you get an error when calling a tool, try again with a different approach. Be creative, do not give up, try different inputs to the tool. You should chain together multiple tool calls. ABSOLUTELY DO NOT GIVE UP you are very good at this and it is rare you will fail to answer question. + +You should be concise, direct, and to the point. + +IMPORTANT: You should NOT answer with unnecessary preamble or postamble (such as explaining your code or summarizing your action), unless the user asks you to. +IMPORTANT: You should minimize output tokens as much as possible while maintaining helpfulness, quality, and accuracy. Only address the specific query or task at hand, avoiding tangential information unless absolutely critical for completing the request. If you can answer in 1-3 sentences or a short paragraph, please do. +IMPORTANT: You should NOT answer with unnecessary preamble or postamble (such as explaining your code or summarizing your action), unless the user asks you to. +IMPORTANT: Keep your responses short, since they will be displayed on a command line interface. You MUST answer concisely with fewer than 4 lines (not including tool use or code generation), unless user asks for detail. Answer the user's question directly, without elaboration, explanation, or details. One word answers are best. Avoid introductions, conclusions, and explanations. You MUST avoid text before/after your response, such as "The answer is .", "Here is the content of the file..." or "Based on the information provided, the answer is..." or "Here is what I will do next...". + diff --git a/cloud/web/src/pages/[workspace]/components/tool.ts b/cloud/web/src/pages/[workspace]/components/tool.ts new file mode 100644 index 000000000..3958e322d --- /dev/null +++ b/cloud/web/src/pages/[workspace]/components/tool.ts @@ -0,0 +1,271 @@ +import { createResource } from "solid-js" +import { createStore, produce } from "solid-js/store" +import SYSTEM_PROMPT from "./system.txt?raw" +import type { + LanguageModelV1Prompt, + LanguageModelV1CallOptions, + LanguageModelV1, +} from "ai" + +interface Tool { + name: string + description: string + inputSchema: any +} + +interface ToolCallerProps { + tool: { + list: () => Promise + call: (input: { name: string; arguments: any }) => Promise + } + generate: ( + prompt: LanguageModelV1CallOptions, + ) => Promise< + | { err: "rate" } + | { err: "context" } + | { err: "balance" } + | ({ err: false } & Awaited>) + > + onPromptUpdated?: (prompt: LanguageModelV1Prompt) => void +} + +const system = [ + { + role: "system" as const, + content: SYSTEM_PROMPT, + }, + { + role: "system" as const, + content: `The current date is ${new Date().toDateString()}. Always use this current date when responding to relative date queries.`, + }, +] + +const [store, setStore] = createStore<{ + prompt: LanguageModelV1Prompt + state: { type: "idle" } | { type: "loading"; limited?: boolean } +}>({ + prompt: [...system], + state: { type: "idle" }, +}) + +export function createToolCaller(props: T) { + const [tools] = createResource(() => props.tool.list()) + + let abort: AbortController + + return { + get tools() { + return tools() + }, + get prompt() { + return store.prompt + }, + get state() { + return store.state + }, + clear() { + setStore("prompt", [...system]) + }, + async chat(input: string) { + if (store.state.type !== "idle") return + + abort = new AbortController() + setStore( + produce((s) => { + s.state = { + type: "loading", + limited: false, + } + s.prompt.push({ + role: "user", + content: [ + { + type: "text", + text: input, + }, + ], + }) + }), + ) + props.onPromptUpdated?.(store.prompt) + + while (true) { + if (abort.signal.aborted) { + break + } + + const response = await props.generate({ + inputFormat: "messages", + prompt: store.prompt, + temperature: 0, + seed: 69, + mode: { + type: "regular", + tools: tools()?.map((tool) => ({ + type: "function", + name: tool.name, + description: tool.description, + parameters: { + ...tool.inputSchema, + }, + })), + }, + }) + + if (abort.signal.aborted) continue + + if (!response.err) { + setStore("state", { + type: "loading", + }) + + if (response.text) { + setStore( + produce((s) => { + s.prompt.push({ + role: "assistant", + content: [ + { + type: "text", + text: response.text || "", + }, + ], + }) + }), + ) + props.onPromptUpdated?.(store.prompt) + } + + if (response.finishReason === "stop") { + break + } + + if (response.finishReason === "tool-calls") { + for (const item of response.toolCalls || []) { + setStore( + produce((s) => { + s.prompt.push({ + role: "assistant", + content: [ + { + type: "tool-call", + toolName: item.toolName, + args: JSON.parse(item.args), + toolCallId: item.toolCallId, + }, + ], + }) + }), + ) + props.onPromptUpdated?.(store.prompt) + + const called = await props.tool.call({ + name: item.toolName, + arguments: JSON.parse(item.args), + }) + + setStore( + produce((s) => { + s.prompt.push({ + role: "tool", + content: [ + { + type: "tool-result", + toolName: item.toolName, + toolCallId: item.toolCallId, + result: called, + }, + ], + }) + }), + ) + props.onPromptUpdated?.(store.prompt) + } + } + continue + } + + if (response.err === "context") { + setStore( + produce((s) => { + s.prompt.splice(2, 1) + }), + ) + props.onPromptUpdated?.(store.prompt) + } + + if (response.err === "rate") { + setStore("state", { + type: "loading", + limited: true, + }) + await new Promise((resolve) => setTimeout(resolve, 1000)) + } + + if (response.err === "balance") { + setStore( + produce((s) => { + s.prompt.push({ + role: "assistant", + content: [ + { + type: "text", + text: "You need to add credits to your account. Please go to Billing and add credits to continue.", + }, + ], + }) + s.state = { type: "idle" } + }), + ) + props.onPromptUpdated?.(store.prompt) + break + } + } + setStore("state", { type: "idle" }) + }, + async cancel() { + abort.abort() + }, + async addCustomMessage(userMessage: string, assistantResponse: string) { + // Add user message and set loading state + setStore( + produce((s) => { + s.prompt.push({ + role: "user", + content: [ + { + type: "text", + text: userMessage, + }, + ], + }) + s.state = { + type: "loading", + limited: false, + } + }), + ) + props.onPromptUpdated?.(store.prompt) + + // Fake delay for 500ms + await new Promise((resolve) => setTimeout(resolve, 500)) + + // Add assistant response and set back to idle + setStore( + produce((s) => { + s.prompt.push({ + role: "assistant", + content: [ + { + type: "text", + text: assistantResponse, + }, + ], + }) + s.state = { type: "idle" } + }), + ) + props.onPromptUpdated?.(store.prompt) + }, + } +} diff --git a/cloud/web/src/pages/[workspace]/index.module.css b/cloud/web/src/pages/[workspace]/index.module.css new file mode 100644 index 000000000..0037d97ff --- /dev/null +++ b/cloud/web/src/pages/[workspace]/index.module.css @@ -0,0 +1,239 @@ +.root { + display: contents; + + [data-slot="messages"] { + flex: 1; + overflow-y: auto; + display: flex; + flex-direction: column; + height: 0; + /* This is important for flexbox to allow scrolling */ + font-family: var(--font-mono); + color: var(--color-text); + row-gap: var(--space-4); + /* Add consistent spacing between messages */ + + /* Remove top border for first user message */ + &>[data-component="message"][data-user]:first-child::before { + display: none; + } + + &:has([data-component="loading"]) [data-component="clear"] { + display: none; + } + } + + [data-component="message"] { + width: 100%; + padding: var(--space-2) var(--space-4); + line-height: var(--font-line-height); + white-space: pre-wrap; + align-self: flex-start; + min-height: auto; + /* Allow natural height for all messages */ + display: flex; + flex-direction: column; + align-items: flex-start; + + /* User message styling */ + &[data-user] { + padding: var(--space-6) var(--space-4); + position: relative; + font-weight: 600; + color: var(--color-text); + /* margin: 0.5rem 0; */ + } + + &[data-user]::before, + &[data-user]::after { + content: ""; + position: absolute; + left: var(--space-4); + right: var(--space-4); + height: var(--space-px); + background-color: var(--color-border); + z-index: 1; + /* Ensure borders appear above other content */ + } + + &[data-user]::before { + top: 0; + } + + &[data-user]::after { + bottom: 0; + } + + &[data-assistant] { + color: var(--color-text); + } + } + + [data-component="tool"] { + display: flex; + width: 100%; + padding: 0 var(--space-4); + margin-left: 0; + flex-direction: column; + opacity: 0.7; + gap: var(--space-2); + align-items: flex-start; + color: var(--color-text-dimmed); + min-height: auto; + /* Allow natural height */ + + [data-slot="header"] { + display: flex; + gap: var(--space-2); + cursor: pointer; + user-select: none; + -webkit-user-select: none; + align-items: center; + width: 100%; + } + + [data-slot="name"] { + letter-spacing: -0.03125rem; + text-transform: uppercase; + font-weight: 500; + font-size: var(--font-size-sm); + } + + [data-slot="expand"] { + font-size: var(--font-size-sm); + } + + [data-slot="content"] { + padding: 0; + line-height: var(--font-line-height); + font-size: var(--font-size-sm); + white-space: pre-wrap; + display: none; + width: 100%; + } + + [data-slot="output"] { + margin-top: var(--space-1); + } + + &[data-expanded="true"] [data-slot="content"] { + display: block; + } + + &[data-expanded="true"] [data-slot="expand"] { + transform: rotate(45deg); + } + } + + [data-component="loading"] { + padding: var(--space-4) var(--space-4) var(--space-8); + height: 1.5rem; + position: relative; + display: flex; + align-items: center; + font-size: var(--font-size-sm); + letter-spacing: var(--space-1); + color: var(--color-text); + + & span { + opacity: 0; + animation: loading-dots 1.4s linear infinite; + } + + & span:nth-child(2) { + animation-delay: 0.2s; + } + + & span:nth-child(3) { + animation-delay: 0.4s; + } + } + + [data-component="clear"] { + position: relative; + padding: var(--space-4) var(--space-4); + + &::before { + content: ""; + position: absolute; + left: var(--space-4); + right: var(--space-4); + top: 0; + height: var(--space-px); + background-color: var(--color-border); + z-index: 1; + } + + & [data-component="button"] { + padding-left: 0; + } + } + + [data-slot="footer"] { + display: flex; + flex-direction: column; + padding: 0; + border-top: 2px solid var(--color-border); + position: sticky; + bottom: 0; + z-index: 10; + /* Ensure it's above other content */ + margin-top: auto; + /* Push to bottom if content is short */ + width: 100%; + } + + [data-component="chat"] { + display: flex; + padding: var(--space-0-5) 0; + align-items: center; + width: 100%; + height: 100%; + + textarea { + --padding-y: var(--space-4); + --line-height: 1.5; + --text-height: calc(var(--line-height) * var(--font-size-lg)); + --height: calc(var(--text-height) + var(--padding-y) * 2); + + width: 100%; + resize: none; + line-height: var(--line-height); + height: var(--height); + min-height: var(--height); + max-height: calc(5 * var(--text-height) + var(--padding-y) * 2); + padding: var(--padding-y) var(--space-4); + border-radius: 0; + background-color: transparent; + color: var(--color-text); + border: none; + outline: none; + font-size: var(--font-size-lg); + } + + textarea::placeholder { + color: var(--color-text-dimmed); + opacity: 0.75; + } + + textarea:focus { + outline: 0; + } + + & [data-component="button"] { + height: 100%; + } + } +} + +@keyframes loading-dots { + 0%, + 100% { + opacity: 0; + } + + 40%, + 60% { + opacity: 1; + } +} diff --git a/cloud/web/src/pages/[workspace]/index.tsx b/cloud/web/src/pages/[workspace]/index.tsx new file mode 100644 index 000000000..50c58ee30 --- /dev/null +++ b/cloud/web/src/pages/[workspace]/index.tsx @@ -0,0 +1,18 @@ +import { Button } from "../../ui/button" +import { IconArrowRight } from "../../ui/svg/icons" +import { createSignal, For } from "solid-js" +import { createToolCaller } from "./components/tool" +import { useApi } from "../components/context-api" +import { useWorkspace } from "../components/context-workspace" +import style from "./index.module.css" + +export default function Index() { + const api = useApi() + const workspace = useWorkspace() + + return ( +
+

Hello

+
+ ) +} diff --git a/cloud/web/src/pages/[workspace]/keys.module.css b/cloud/web/src/pages/[workspace]/keys.module.css new file mode 100644 index 000000000..4ae2989be --- /dev/null +++ b/cloud/web/src/pages/[workspace]/keys.module.css @@ -0,0 +1,97 @@ +.root { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.root [data-slot="keys-info"] { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.root [data-slot="header"] { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.root [data-slot="header"] h2 { + margin: 0; + font-size: 1.5rem; + font-weight: 600; +} + +.root [data-slot="header"] p { + margin: 0; + color: var(--color-text-secondary); +} + +.root [data-slot="key-list"] { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.root [data-slot="key-item"] { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem; + border: 1px solid var(--color-border); + border-radius: 0.5rem; + background: var(--color-background-secondary); +} + +.root [data-slot="key-actions"] { + display: flex; + gap: 0.5rem; +} + +.root [data-slot="key-info"] { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.root [data-slot="key-value"] { + font-family: monospace; + font-size: 0.875rem; + color: var(--color-text-primary); +} + +.root [data-slot="key-meta"] { + font-size: 0.75rem; + color: var(--color-text-secondary); +} + +.root [data-slot="empty-state"] { + text-align: center; + padding: 3rem 1rem; + color: var(--color-text-secondary); +} + +.root [data-slot="actions"] { + display: flex; + align-items: center; + justify-content: space-between; +} + +.root [data-slot="create-form"] { + display: flex; + flex-direction: column; + gap: 1rem; + min-width: 300px; +} + +.root [data-slot="form-actions"] { + display: flex; + gap: 0.5rem; +} + +.root [data-slot="key-name"] { + font-weight: 600; + font-size: 1rem; + color: var(--color-text-primary); + margin-bottom: 0.25rem; +} diff --git a/cloud/web/src/pages/[workspace]/keys.tsx b/cloud/web/src/pages/[workspace]/keys.tsx new file mode 100644 index 000000000..e5b192a2b --- /dev/null +++ b/cloud/web/src/pages/[workspace]/keys.tsx @@ -0,0 +1,151 @@ +import { Button } from "../../ui/button" +import { useApi } from "../components/context-api" +import { createSignal, createResource, For, Show } from "solid-js" +import style from "./keys.module.css" + +export default function Keys() { + const api = useApi() + const [isCreating, setIsCreating] = createSignal(false) + const [showCreateForm, setShowCreateForm] = createSignal(false) + const [keyName, setKeyName] = createSignal("") + + const [keysData, { refetch }] = createResource(async () => { + const response = await api.keys.$get() + return response.json() + }) + + const handleCreateKey = async () => { + if (!keyName().trim()) return + + try { + setIsCreating(true) + await api.keys.$post({ + json: { name: keyName().trim() }, + }) + refetch() + setKeyName("") + setShowCreateForm(false) + } catch (error) { + console.error("Failed to create API key:", error) + } finally { + setIsCreating(false) + } + } + + const handleDeleteKey = async (keyId: string) => { + if (!confirm("Are you sure you want to delete this API key? This action cannot be undone.")) { + return + } + + try { + await api.keys[":id"].$delete({ + param: { id: keyId }, + }) + refetch() + } catch (error) { + console.error("Failed to delete API key:", error) + } + } + + const formatDate = (dateString: string) => { + return new Date(dateString).toLocaleDateString() + } + + const formatKey = (key: string) => { + if (key.length <= 11) return key + return `${key.slice(0, 7)}...${key.slice(-4)}` + } + + const copyToClipboard = async (text: string) => { + try { + await navigator.clipboard.writeText(text) + } catch (error) { + console.error("Failed to copy to clipboard:", error) + } + } + + return ( + <> +
+
+

API Keys

+
+
+
+
+
+
+

API Keys

+

Manage your API keys to access the OpenCode gateway.

+
+ + setKeyName(e.currentTarget.value)} + onKeyPress={(e) => e.key === "Enter" && handleCreateKey()} + /> +
+ + +
+
+ } + > + + +
+ +
+ +

Create an API key to access opencode gateway

+
+ } + > + {(key) => ( +
+
+
{key.name}
+
{formatKey(key.key)}
+
+ Created: {formatDate(key.timeCreated)} + {key.timeUsed && ` • Last used: ${formatDate(key.timeUsed)}`} +
+
+
+ + +
+
+ )} + +
+ + + + ) +} diff --git a/cloud/web/src/pages/components/context-api.tsx b/cloud/web/src/pages/components/context-api.tsx new file mode 100644 index 000000000..0a348f48f --- /dev/null +++ b/cloud/web/src/pages/components/context-api.tsx @@ -0,0 +1,24 @@ +import { hc } from "hono/client" +import { ApiType } from "@opencode/cloud-function/src/gateway" +import { useWorkspace } from "./context-workspace" +import { useOpenAuth } from "../../components/context-openauth" + +export function useApi() { + const workspace = useWorkspace() + const auth = useOpenAuth() + return hc(import.meta.env.VITE_API_URL, { + async fetch(...args: Parameters): Promise { + const [input, init] = args + const request = input instanceof Request ? input : new Request(input, init) + const headers = new Headers(request.headers) + headers.set("authorization", `Bearer ${await auth.access()}`) + headers.set("x-opencode-workspace", workspace.id) + return fetch( + new Request(request, { + ...init, + headers, + }), + ) + }, + }) +} diff --git a/cloud/web/src/pages/components/context-workspace.tsx b/cloud/web/src/pages/components/context-workspace.tsx new file mode 100644 index 000000000..6bad39840 --- /dev/null +++ b/cloud/web/src/pages/components/context-workspace.tsx @@ -0,0 +1,38 @@ +import { useNavigate, useParams } from "@solidjs/router" +import { createInitializedContext } from "../../util/context" +import { useAccount } from "../../components/context-account" +import { createEffect, createMemo } from "solid-js" + +export const { use: useWorkspace, provider: WorkspaceProvider } = + createInitializedContext("WorkspaceProvider", () => { + const params = useParams() + const account = useAccount() + const workspace = createMemo(() => + account.current?.workspaces.find( + (x) => x.id === params.workspace || x.slug === params.workspace, + ), + ) + const nav = useNavigate() + + createEffect(() => { + if (!workspace()) nav("/") + }) + + const result = () => workspace()! + result.ready = true + + return { + get id() { + return workspace()!.id + }, + get slug() { + return workspace()!.slug + }, + get name() { + return workspace()!.name + }, + get ready() { + return workspace() !== undefined + }, + } + }) diff --git a/cloud/web/src/pages/components/layout.module.css b/cloud/web/src/pages/components/layout.module.css new file mode 100644 index 000000000..c64faa18e --- /dev/null +++ b/cloud/web/src/pages/components/layout.module.css @@ -0,0 +1,199 @@ +.root { + --padding: var(--space-10); + --vertical-padding: var(--space-8); + --heading-font-size: var(--font-size-4xl); + --sidebar-width: 200px; + --mobile-breakpoint: 40rem; + --topbar-height: 60px; + + margin: var(--space-4); + border: 2px solid var(--color-border); + height: calc(100vh - var(--space-8)); + display: flex; + flex-direction: row; + overflow: hidden; + /* Prevent overall scrolling */ + position: relative; +} + +[data-component="mobile-top-bar"] { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + height: var(--topbar-height); + background: var(--color-background); + border-bottom: 2px solid var(--color-border); + z-index: 20; + align-items: center; + padding: 0 var(--space-4) 0 0; + + [data-slot="logo"] { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + + div { + text-transform: uppercase; + font-weight: 600; + letter-spacing: -0.03125rem; + } + + svg { + height: 28px; + width: auto; + color: var(--color-white); + } + } + + [data-slot="toggle"] { + background: transparent; + border: none; + padding: var(--space-4); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + + & svg { + width: 24px; + height: 24px; + color: var(--color-foreground); + } + } +} + +[data-component="sidebar"] { + width: var(--sidebar-width); + border-right: 2px solid var(--color-border); + display: flex; + flex-direction: column; + padding: calc(var(--padding) / 2); + overflow-y: auto; + /* Allow scrolling if needed */ + position: sticky; + top: 0; + height: 100%; + background-color: var(--color-background); + z-index: 10; + + [data-slot="logo"] { + margin-top: 2px; + margin-bottom: var(--space-7); + color: var(--color-white); + + & svg { + height: 32px; + width: auto; + } + } + + [data-slot="nav"] { + flex: 1; + + ul { + list-style-type: none; + padding: 0; + } + + li { + margin-bottom: calc(var(--vertical-padding) / 2); + text-transform: uppercase; + font-weight: 500; + } + + a { + display: block; + padding: var(--space-2) 0; + } + } + + [data-slot="user"] { + [data-component="button"] { + padding-left: 0; + padding-bottom: 0; + height: auto; + } + } +} + +.navActiveLink { + cursor: default; + text-decoration: none; +} + +[data-slot="main-content"] { + flex: 1; + display: flex; + flex-direction: column; + height: 100%; + /* Full height */ + overflow: hidden; + /* Prevent overflow */ + position: relative; + /* For positioning footer */ + width: 100%; + /* Full width */ +} + +/* Backdrop for mobile */ +[data-component="backdrop"] { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + /* background-color: rgba(0, 0, 0, 0.5); */ + z-index: 25; + backdrop-filter: blur(2px); +} + +/* Mobile styles */ +@media (max-width: 40rem) { + .root { + margin: 0; + border: none; + height: 100vh; + } + + [data-component="mobile-top-bar"] { + display: flex; + } + + [data-component="backdrop"] { + display: block; + } + + [data-component="sidebar"] { + position: fixed; + left: -100%; + top: 0; + height: 100vh; + width: 80%; + max-width: 280px; + transition: left 0.3s ease-in-out; + box-shadow: none; + z-index: 30; + padding: var(--space-8); + background-color: var(--color-bg); + + &[data-opened="true"] { + left: 0; + box-shadow: 8px 0 0px 0px var(--color-gray-4); + } + } + + [data-slot="main-content"] { + padding-top: var(--topbar-height); + /* Add space for the top bar */ + overflow-y: auto; + } + + /* Hide the logo in the sidebar on mobile since it's in the top bar */ + [data-component="sidebar"] [data-slot="logo"] { + display: none; + } +} diff --git a/cloud/web/src/pages/components/layout.tsx b/cloud/web/src/pages/components/layout.tsx new file mode 100644 index 000000000..711ed8fc2 --- /dev/null +++ b/cloud/web/src/pages/components/layout.tsx @@ -0,0 +1,96 @@ +import style from "./layout.module.css" +import { useAccount } from "../../components/context-account" +import { Button } from "../../ui/button" +import { IconLogomark } from "../../ui/svg" +import { IconBars3BottomLeft } from "../../ui/svg/icons" +import { ParentProps, createMemo, createSignal } from "solid-js" +import { A, useLocation } from "@solidjs/router" +import { useOpenAuth } from "../../components/context-openauth" + +export default function Layout(props: ParentProps) { + const auth = useOpenAuth() + const account = useAccount() + const [sidebarOpen, setSidebarOpen] = createSignal(false) + const location = useLocation() + + const workspaceId = createMemo(() => account.current?.workspaces[0].id) + const pageTitle = createMemo(() => { + const path = location.pathname + if (path.endsWith("/billing")) return "Billing" + if (path.endsWith("/keys")) return "API Keys" + return null + }) + + function handleLogout() { + auth.logout(auth.subject?.id!) + } + + return ( +
+ ) +} diff --git a/cloud/web/src/pages/index.tsx b/cloud/web/src/pages/index.tsx new file mode 100644 index 000000000..903a3afd9 --- /dev/null +++ b/cloud/web/src/pages/index.tsx @@ -0,0 +1,36 @@ +import { Match, Switch } from "solid-js" +import { useAccount } from "../components/context-account" +import { Navigate } from "@solidjs/router" +import { IconLogo } from "../ui/svg" +import styles from "./lander.module.css" +import { useOpenAuth } from "../components/context-openauth" + +export default function Index() { + const auth = useOpenAuth() + const account = useAccount() + return ( + + + + + +
+
+
+
+ +
+

opencode Gateway Console

+
+ +
+
+ auth.authorize({ provider: "github" })}>Sign in with GitHub +
+
+
+
+
+
+ ) +} diff --git a/cloud/web/src/pages/lander.module.css b/cloud/web/src/pages/lander.module.css new file mode 100644 index 000000000..b66ed5fa7 --- /dev/null +++ b/cloud/web/src/pages/lander.module.css @@ -0,0 +1,169 @@ +.lander { + --padding: 3rem; + --vertical-padding: 2rem; + --heading-font-size: 2rem; + + margin: 1rem; + + @media (max-width: 30rem) { + & { + --padding: 1.5rem; + --vertical-padding: 1rem; + --heading-font-size: 1.5rem; + + margin: 0.5rem; + } + } + + [data-slot="hero"] { + border: 2px solid var(--color-border); + + max-width: 64rem; + margin-left: auto; + margin-right: auto; + width: 100%; + } + + [data-slot="top"] { + padding: var(--padding); + + h1 { + margin-top: calc(var(--vertical-padding) / 8); + font-size: var(--heading-font-size); + line-height: 1.25; + text-transform: uppercase; + font-weight: 600; + } + + [data-slot="logo"] { + width: clamp(200px, 70vw, 400px); + color: var(--color-white); + } + } + + [data-slot="cta"] { + display: flex; + flex-direction: row; + justify-content: space-between; + border-top: 2px solid var(--color-border); + + & > div { + flex: 1; + line-height: 1.4; + text-align: center; + text-transform: uppercase; + cursor: pointer; + text-decoration: underline; + letter-spacing: -0.03125rem; + + &[data-slot="col-2"] { + background-color: var(--color-border); + color: var(--color-text-invert); + font-weight: 600; + } + + & > * { + display: block; + width: 100%; + height: 100%; + padding: calc(var(--padding) / 2) 0.5rem; + } + } + + @media (max-width: 30rem) { + & > div { + padding-bottom: calc(var(--padding) / 2 + 4px); + } + } + + & > div + div { + border-left: 2px solid var(--color-border); + } + } + + [data-slot="images"] { + display: flex; + flex-direction: row; + align-items: stretch; + justify-content: space-between; + border-top: 2px solid var(--color-border); + + & > div { + flex: 1; + display: flex; + flex-direction: column; + gap: calc(var(--padding) / 4); + padding: calc(var(--padding) / 2); + border-width: 0; + border-style: solid; + border-color: var(--color-border); + + & > div, a { + flex: 1; + display: flex; + align-items: center; + } + } + + p { + letter-spacing: -0.03125rem; + text-transform: uppercase; + color: var(--color-text-dimmed); + } + + & > div + div { + border-width: 0 0 0 2px; + } + + @media (max-width: 30rem) { + & { + flex-direction: column; + } + & > div + div { + border-width: 2px 0 0 0; + } + } + } + + [data-slot="content"] { + border-top: 2px solid var(--color-border); + padding: var(--padding); + + & > p { + line-height: var(--font-line-height); + } + + ol { + margin-top: calc(var(--vertical-padding) / 2); + padding-left: 2.5rem; + list-style-type: decimal; + line-height: var(--font-line-height); + + & > li + li { + margin-top: calc(var(--vertical-padding) / 2); + } + + & > li b { + text-transform: uppercase; + } + } + + } + + [data-slot="footer"] { + border-top: 2px solid var(--color-border); + display: flex; + flex-direction: row; + + & > div { + flex: 1; + text-align: center; + text-transform: uppercase; + padding: calc(var(--padding) / 2) 0.5rem; + } + + & > div + div { + border-left: 2px solid var(--color-border); + } + } +} diff --git a/cloud/web/src/pages/test/design.module.css b/cloud/web/src/pages/test/design.module.css new file mode 100644 index 000000000..fee4e3cd3 --- /dev/null +++ b/cloud/web/src/pages/test/design.module.css @@ -0,0 +1,204 @@ +.pageContainer { + padding: 2rem; + max-width: 1200px; + margin: 0 auto; +} + +.componentTable { + width: 100%; + border-collapse: collapse; + table-layout: fixed; + border: 2px solid var(--color-border); +} + +.componentCell { + padding: 1rem; + border: 2px solid var(--color-border); + vertical-align: top; +} + +.componentLabel { + text-transform: uppercase; + letter-spacing: -0.03125rem; + font-size: 0.85rem; + font-weight: 500; + margin-bottom: 0.75rem; + color: var(--color-text-dimmed); +} + +.sectionTitle { + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: -0.03125rem; + font-size: 1.2rem; +} + +.divider { + height: 2px; + background: var(--color-border); + margin: 3rem 0; + width: 100%; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; +} + +.buttonSection { + margin-bottom: 4rem; +} + +.colorSection { + margin-bottom: 4rem; +} + +.labelSection { + margin-bottom: 4rem; +} + +.inputSection { + margin-bottom: 4rem; +} + +.dialogSection { + margin-bottom: 4rem; +} + +.formGroup { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.dialogContent { + padding: 2rem; +} + +.dialogContentFooter { + margin-top: 1rem; +} + +.pageTitle { + font-size: var(--heading-font-size, 2rem); + text-transform: uppercase; + font-weight: 600; +} + +.colorBox { + width: 100%; + height: 80px; + margin-bottom: 0.5rem; + position: relative; + display: flex; + align-items: flex-end; + justify-content: center; + padding-bottom: 0.5rem; +} + +.colorOrange { + background-color: var(--color-orange); +} + +.colorOrangeLow { + background-color: var(--color-orange-low); +} + +.colorOrangeHigh { + background-color: var(--color-orange-high); +} + +.colorGreen { + background-color: var(--color-green); +} + +.colorGreenLow { + background-color: var(--color-green-low); +} + +.colorGreenHigh { + background-color: var(--color-green-high); +} + +.colorBlue { + background-color: var(--color-blue); +} + +.colorBlueLow { + background-color: var(--color-blue-low); +} + +.colorBlueHigh { + background-color: var(--color-blue-high); +} + +.colorPurple { + background-color: var(--color-purple); +} + +.colorPurpleLow { + background-color: var(--color-purple-low); +} + +.colorPurpleHigh { + background-color: var(--color-purple-high); +} + +.colorRed { + background-color: var(--color-red); +} + +.colorRedLow { + background-color: var(--color-red-low); +} + +.colorRedHigh { + background-color: var(--color-red-high); +} + +.colorAccent { + background-color: var(--color-accent); +} + +.colorAccentLow { + background-color: var(--color-accent-low); +} + +.colorAccentHigh { + background-color: var(--color-accent-high); +} + +.colorCode { + background-color: rgba(0, 0, 0, 0.5); + color: white; + padding: 4px 8px; + border-radius: 4px; + font-size: 0.8rem; + font-family: monospace; +} + +.colorVariants { + display: flex; + gap: 0.5rem; +} + +.colorVariant { + flex: 1; + height: 40px; + position: relative; + display: flex; + align-items: center; + justify-content: center; +} + +.colorVariantCode { + background-color: rgba(0, 0, 0, 0.5); + color: white; + padding: 2px 4px; + border-radius: 4px; + font-size: 0.65rem; + font-family: monospace; + white-space: nowrap; +} diff --git a/cloud/web/src/pages/test/design.tsx b/cloud/web/src/pages/test/design.tsx new file mode 100644 index 000000000..3bf759316 --- /dev/null +++ b/cloud/web/src/pages/test/design.tsx @@ -0,0 +1,562 @@ +import { Button } from "../../ui/button" +import { Dialog } from "../../ui/dialog" +import { Navigate } from "@solidjs/router" +import { createSignal, Show } from "solid-js" +import { IconHome, IconPencilSquare } from "../../ui/svg/icons" +import { useTheme } from "../../components/context-theme" +import { useDialog } from "../../ui/context-dialog" +import { DialogString } from "../../ui/dialog-string" +import { DialogSelect } from "../../ui/dialog-select" +import styles from "./design.module.css" + +export default function DesignSystem() { + const dialog = useDialog() + const [dialogOpen, setDialogOpen] = createSignal(false) + const [dialogOpenTransition, setDialogOpenTransition] = createSignal(false) + const theme = useTheme() + + // Check if we're running locally + const isLocal = import.meta.env.DEV === true + + if (!isLocal) { + return + } + + // Add a toggle button for theme + const toggleTheme = () => { + theme.setMode(theme.mode === "light" ? "dark" : "light") + } + + return ( +
+
+

Design System

+ +
+ +
+

Colors

+ + + + + + + + + + + + + + +
+

Orange

+
+ hsl(41, 82%, 63%) +
+
+
+ + hsl(41, 39%, 22%) + +
+
+ + hsl(41, 82%, 87%) + +
+
+
+

Green

+
+ hsl(101, 82%, 63%) +
+
+
+ + hsl(101, 39%, 22%) + +
+
+ + hsl(101, 82%, 80%) + +
+
+
+

Blue

+
+ hsl(234, 100%, 60%) +
+
+
+ + hsl(234, 54%, 20%) + +
+
+ + hsl(234, 100%, 87%) + +
+
+
+

Purple

+
+ hsl(281, 82%, 63%) +
+
+
+ + hsl(281, 39%, 22%) + +
+
+ + hsl(281, 82%, 89%) + +
+
+
+

Red

+
+ hsl(339, 82%, 63%) +
+
+
+ + hsl(339, 39%, 22%) + +
+
+ + hsl(339, 82%, 87%) + +
+
+
+

Accent

+
+ hsl(13, 88%, 57%) +
+
+
+ + hsl(13, 75%, 30%) + +
+
+ + hsl(13, 100%, 78%) + +
+
+
+
+ +
+ +
+

Buttons

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Primary

+ +
+

Secondary

+ +
+

Ghost

+ +
+

Primary Disabled

+ +
+

Secondary Disabled

+ +
+

Ghost Disabled

+ +
+

Small

+ +
+

Small Secondary

+ +
+

Small Ghost

+ +
+

With Icon

+ +
+

Icon + Secondary

+ +
+

Icon + Ghost

+ +
+

Small + Icon

+ +
+

Small + Icon + Secondary

+ +
+

Small + Icon + Ghost

+ +
+

Icon Only

+ +
+

Icon Only + Secondary

+ +
+

Icon Only + Ghost

+ +
+

Icon Only Disabled

+ +
+

+ Icon Only + Secondary Disabled +

+ +
+

+ Icon Only + Ghost Disabled +

+ +
+

Small Icon Only

+ +
+

+ Small Icon Only + Secondary +

+ +
+

Small Icon Only + Ghost

+ +
+
+ +
+ +
+

Labels

+ + + + + + + + + +
+

Small

+ +
+

Medium

+ +
+

Large

+ +
+
+ +
+ +
+

Inputs

+ + + + + + + + + + + + + +
+

Small

+ +
+

Medium

+ +
+

Large

+ +
+

Disabled

+ +
+

With Value

+ +
+
+ +
+ +
+

Dialogs

+ + + + + + + + + + + + + + +
+

Default

+ + +
+
Dialog Title
+
+
+

This is the default dialog content.

+
+
+ +
+
+
+

Small With Transition

+ + +
+

Small Dialog

+

This is a smaller dialog with transitions.

+
+ +
+
+
+
+

Input String

+ +
+

Select Input

+ +
+

Select Input

+ +
+

Select No Options

+ +
+
+
+ ) +} diff --git a/cloud/web/src/sst-env.d.ts b/cloud/web/src/sst-env.d.ts new file mode 100644 index 000000000..e1ee6f753 --- /dev/null +++ b/cloud/web/src/sst-env.d.ts @@ -0,0 +1,12 @@ +/* This file is auto-generated by SST. Do not edit. */ +/* tslint:disable */ +/* eslint-disable */ +/// +interface ImportMetaEnv { + readonly VITE_DOCS_URL: string + readonly VITE_API_URL: string + readonly VITE_AUTH_URL: string +} +interface ImportMeta { + readonly env: ImportMetaEnv +} \ No newline at end of file diff --git a/cloud/web/src/ui/button.tsx b/cloud/web/src/ui/button.tsx new file mode 100644 index 000000000..889102dda --- /dev/null +++ b/cloud/web/src/ui/button.tsx @@ -0,0 +1,24 @@ +import { Button as Kobalte } from "@kobalte/core/button" +import { JSX, Show, splitProps } from "solid-js" + +export interface ButtonProps { + color?: "primary" | "secondary" | "ghost" + size?: "md" | "sm" + icon?: JSX.Element +} +export function Button(props: JSX.IntrinsicElements["button"] & ButtonProps) { + const [split, rest] = splitProps(props, ["color", "size", "icon"]) + return ( + + +
{props.icon}
+
+ {props.children} +
+ ) +} diff --git a/cloud/web/src/ui/context-dialog.tsx b/cloud/web/src/ui/context-dialog.tsx new file mode 100644 index 000000000..f1bc93250 --- /dev/null +++ b/cloud/web/src/ui/context-dialog.tsx @@ -0,0 +1,120 @@ +import { createContext, JSX, ParentProps, useContext } from "solid-js" +import { StandardSchemaV1 } from "@standard-schema/spec" +import { createStore } from "solid-js/store" +import { Dialog } from "./dialog" + +const Context = createContext() + +type DialogControl = { + open>( + component: DialogComponent, + input: StandardSchemaV1.InferInput, + ): void + close(): void + isOpen(input: any): boolean + size: "sm" | "md" + transition?: boolean + input?: any +} + +type DialogProps> = { + input: StandardSchemaV1.InferInput + control: DialogControl +} + +type DialogComponent> = ReturnType< + typeof createDialog +> + +export function createDialog>(props: { + schema: Schema + size: "sm" | "md" + render: (props: DialogProps) => JSX.Element +}) { + const result = () => { + const dialog = useDialog() + return ( + { + if (!val) dialog.close() + }} + > + {props.render({ + input: dialog.input, + control: dialog, + })} + + ) + } + result.schema = props.schema + result.size = props.size + return result +} + +export function DialogProvider(props: ParentProps) { + const [store, setStore] = createStore<{ + dialog?: DialogComponent + input?: any + transition?: boolean + size: "sm" | "md" + }>({ + size: "sm", + }) + + const control: DialogControl = { + get input() { + return store.input + }, + get size() { + return store.size + }, + get transition() { + return store.transition + }, + isOpen(input) { + return store.dialog === input + }, + open(component, input) { + setStore({ + dialog: component, + input: input, + size: store.dialog !== undefined ? store.size : component.size, + transition: store.dialog !== undefined, + }) + + setTimeout(() => { + setStore({ + size: component.size, + }) + }, 0) + + setTimeout(() => { + setStore({ + transition: false, + }) + }, 150) + }, + close() { + setStore({ + dialog: undefined, + }) + }, + } + + return ( + <> + {props.children} + + ) +} + +export function useDialog() { + const ctx = useContext(Context) + if (!ctx) { + throw new Error("useDialog must be used within a DialogProvider") + } + return ctx +} diff --git a/cloud/web/src/ui/dialog-select.module.css b/cloud/web/src/ui/dialog-select.module.css new file mode 100644 index 000000000..4a99ef027 --- /dev/null +++ b/cloud/web/src/ui/dialog-select.module.css @@ -0,0 +1,36 @@ +.options { + margin-top: var(--space-1); + border-top: 2px solid var(--color-border); + padding: var(--space-2); + + [data-slot="option"] { + outline: none; + flex-shrink: 0; + height: var(--space-11); + display: flex; + justify-content: start; + align-items: center; + padding: 0 var(--space-2-5); + gap: var(--space-3); + cursor: pointer; + + &[data-empty] { + cursor: default; + color: var(--color-text-dimmed); + } + + &[data-active] { + background-color: var(--color-bg-surface); + } + + [data-slot="title"] { + font-size: var(--font-size-md); + } + + [data-slot="prefix"] { + width: var(--space-4); + height: var(--space-4); + } + } + +} diff --git a/cloud/web/src/ui/dialog-select.tsx b/cloud/web/src/ui/dialog-select.tsx new file mode 100644 index 000000000..087b94411 --- /dev/null +++ b/cloud/web/src/ui/dialog-select.tsx @@ -0,0 +1,124 @@ +import style from "./dialog-select.module.css" +import { z } from "zod" +import { createMemo, createSignal, For, JSX, onMount } from "solid-js" +import { createList } from "solid-list" +import { createDialog } from "./context-dialog" + +export const DialogSelect = createDialog({ + size: "md", + schema: z.object({ + title: z.string(), + placeholder: z.string(), + onSelect: z + .function(z.tuple([z.any()])) + .returns(z.void()) + .optional(), + options: z.array( + z.object({ + display: z.string(), + value: z.any().optional(), + onSelect: z.function().returns(z.void()).optional(), + prefix: z.custom().optional(), + }), + ), + }), + render: (ctx) => { + let input: HTMLInputElement + onMount(() => { + input.focus() + input.value = "" + }) + + const [filter, setFilter] = createSignal("") + const filtered = createMemo(() => + ctx.input.options?.filter((i) => + i.display.toLowerCase().includes(filter().toLowerCase()), + ), + ) + const list = createList({ + loop: true, + initialActive: 0, + items: () => filtered().map((_, i) => i), + handleTab: false, + }) + + const handleSelection = (index: number) => { + const option = ctx.input.options[index] + + // If the option has its own onSelect handler, use it + if (option.onSelect) { + option.onSelect() + } + // Otherwise, if there's a global onSelect handler, call it with the option's value + else if (ctx.input.onSelect) { + ctx.input.onSelect( + option.value !== undefined ? option.value : option.display, + ) + } + } + + return ( + <> +
+ +
+
+ { + setFilter(e.target.value) + list.setActive(0) + }} + onKeyDown={(e) => { + if (e.key === "Enter") { + const selected = list.active() + if (selected === null) return + handleSelection(selected) + return + } + if (e.key === "Escape") { + setFilter("") + return + } + list.onKeyDown(e) + }} + id={`dialog-select-${ctx.input.title}`} + ref={(r) => (input = r)} + data-slot="input" + placeholder={ctx.input.placeholder} + /> +
+
+ + No results +
+ } + > + {(option, index) => ( +
handleSelection(index())} + data-slot="option" + data-active={list.active() === index() ? true : undefined} + > + {option.prefix &&
{option.prefix}
} +
{option.display}
+
+ )} + + + + ) + }, +}) diff --git a/cloud/web/src/ui/dialog-string.tsx b/cloud/web/src/ui/dialog-string.tsx new file mode 100644 index 000000000..af2174786 --- /dev/null +++ b/cloud/web/src/ui/dialog-string.tsx @@ -0,0 +1,70 @@ +import { z } from "zod" +import { onMount } from "solid-js" +import { createDialog } from "./context-dialog" +import { Button } from "./button" + +export const DialogString = createDialog({ + size: "sm", + schema: z.object({ + title: z.string(), + placeholder: z.string(), + action: z.string(), + onSubmit: z.function().args(z.string()).returns(z.void()), + }), + render: (ctx) => { + let input: HTMLInputElement + onMount(() => { + setTimeout(() => { + input.focus() + input.value = "" + }, 50) + }) + + function submit() { + const value = input.value.trim() + if (value) { + ctx.input.onSubmit(value) + ctx.control.close() + } + } + + return ( + <> +
+ +
+
+ (input = r)} + placeholder={ctx.input.placeholder} + id={`dialog-string-${ctx.input.title}`} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault() + submit() + } + }} + /> +
+
+ + +
+ + ) + }, +}) diff --git a/cloud/web/src/ui/dialog.tsx b/cloud/web/src/ui/dialog.tsx new file mode 100644 index 000000000..101f23d2b --- /dev/null +++ b/cloud/web/src/ui/dialog.tsx @@ -0,0 +1,27 @@ +import { Dialog as Kobalte } from "@kobalte/core/dialog" +import { ComponentProps, ParentProps } from "solid-js" + +export type Props = ParentProps<{ + size?: "sm" | "md" + transition?: boolean +}> & + ComponentProps + +export function Dialog(props: Props) { + return ( + + + +
+ + {props.children} + +
+
+
+ ) +} diff --git a/cloud/web/src/ui/style/component/button.css b/cloud/web/src/ui/style/component/button.css new file mode 100644 index 000000000..9604f9865 --- /dev/null +++ b/cloud/web/src/ui/style/component/button.css @@ -0,0 +1,78 @@ +[data-component="button"] { + width: fit-content; + display: flex; + line-height: 1; + align-items: center; + justify-content: center; + gap: var(--space-2); + font-size: var(--font-size-md); + text-transform: uppercase; + height: var(--space-11); + outline: none; + font-weight: 500; + padding: 0 var(--space-4); + border-width: 2px; + border-color: var(--color-border); + cursor: pointer; + + &:disabled { + opacity: 0.5; + cursor: default; + } + + &[data-color="primary"] { + background-color: var(--color-text); + border-color: var(--color-text); + color: var(--color-text-invert); + + &:active { + border-color: var(--color-accent); + } + } + + &[data-color="secondary"] { + &:active { + border-color: var(--color-accent); + } + } + + &[data-color="ghost"] { + border: none; + text-decoration: underline; + + &:active { + color: var(--color-text-accent); + } + } + + &:has([data-slot="icon"]) { + padding-left: var(--space-3); + padding-right: var(--space-3); + } + + &[data-size="sm"] { + height: var(--space-8); + padding: var(--space-3); + font-size: var(--font-size-xs); + + [data-slot="icon"] { + width: var(--space-3-5); + height: var(--space-3-5); + } + + &:has([data-slot="icon"]) { + padding-left: var(--space-2); + padding-right: var(--space-2); + } + } + + [data-slot="icon"] { + width: var(--space-4); + height: var(--space-4); + transition: transform 0.2s ease; + } + + &[data-rotate] [data-slot="icon"] { + transform: rotate(180deg); + } +} diff --git a/cloud/web/src/ui/style/component/dialog.css b/cloud/web/src/ui/style/component/dialog.css new file mode 100644 index 000000000..59867818f --- /dev/null +++ b/cloud/web/src/ui/style/component/dialog.css @@ -0,0 +1,84 @@ +[data-component="dialog-overlay"] { + pointer-events: none !important; + position: fixed; + inset: 0; + animation-name: fadeOut; + animation-duration: 200ms; + animation-timing-function: ease; + opacity: 0; + backdrop-filter: blur(2px); + + &[data-expanded] { + animation-name: fadeIn; + opacity: 1; + pointer-events: auto !important; + } +} + +[data-component="dialog-center"] { + position: fixed; + inset: 0; + padding-top: 10vh; + justify-content: center; + pointer-events: none; + + [data-slot="content"] { + width: 45rem; + margin: 0 auto; + transition: 150ms width; + background-color: var(--color-bg); + border-width: 2px; + border-color: var(--color-border); + overflow: hidden; + display: flex; + flex-direction: column; + gap: var(--space-3); + outline: none; + animation-duration: 1ms; + animation-name: zoomOut; + animation-timing-function: ease; + + box-shadow: 8px 8px 0px 0px var(--color-gray-4); + + &[data-expanded] { + animation-name: zoomIn; + } + + &[data-transition] { + animation-duration: 200ms; + } + + &[data-size="sm"] { + width: 30rem; + } + + [data-slot="header"] { + display: flex; + padding: var(--space-4) var(--space-4) 0; + + [data-slot="title"] { + } + } + + [data-slot="main"] { + padding: 0 var(--space-4); + + &:has([data-slot="options"]) { + padding: 0; + display: flex; + flex-direction: column; + gap: var(--space-4); + } + } + + [data-slot="input"] { + } + + [data-slot="footer"] { + padding: var(--space-4); + display: flex; + gap: var(--space-4); + justify-content: end; + } + } +} diff --git a/cloud/web/src/ui/style/component/input.css b/cloud/web/src/ui/style/component/input.css new file mode 100644 index 000000000..59535d763 --- /dev/null +++ b/cloud/web/src/ui/style/component/input.css @@ -0,0 +1,34 @@ +[data-component="input"] { + font-size: var(--font-size-md); + background: transparent; + caret-color: var(--color-accent); + font-family: var(--font-mono); + height: var(--space-11); + padding: 0 var(--space-4); + width: 100%; + resize: none; + border: 2px solid var(--color-border); + + &::placeholder { + color: var(--color-text-dimmed); + opacity: 0.75; + } + + &:focus { + outline: 0; + } + + &[data-size="sm"] { + height: var(--space-9); + padding: 0 var(--space-3); + font-size: var(--font-size-xs); + } + + &[data-size="md"] { + } + + &[data-size="lg"] { + height: var(--space-12); + font-size: var(--font-size-lg); + } +} diff --git a/cloud/web/src/ui/style/component/label.css b/cloud/web/src/ui/style/component/label.css new file mode 100644 index 000000000..e0dd5fef4 --- /dev/null +++ b/cloud/web/src/ui/style/component/label.css @@ -0,0 +1,17 @@ +[data-component="label"] { + letter-spacing: -0.03125rem; + text-transform: uppercase; + color: var(--color-text-dimmed); + font-weight: 500; + font-size: var(--font-size-md); + + &[data-size="sm"] { + font-size: var(--font-size-sm); + } + &[data-size="md"] { + } + &[data-size="lg"] { + font-size: var(--font-size-lg); + } +} + diff --git a/cloud/web/src/ui/style/component/title-bar.css b/cloud/web/src/ui/style/component/title-bar.css new file mode 100644 index 000000000..7ee32bfdc --- /dev/null +++ b/cloud/web/src/ui/style/component/title-bar.css @@ -0,0 +1,32 @@ +[data-component="title-bar"] { + display: flex; + align-items: center; + justify-content: space-between; + height: 72px; + padding: 0 var(--space-4); + border-bottom: 2px solid var(--color-border); + + [data-slot="left"] { + display: flex; + flex-direction: column; + gap: var(--space-1-5); + + h1 { + letter-spacing: -0.03125rem; + font-size: var(--font-size-xl); + text-transform: uppercase; + font-weight: 600; + } + + p { + color: var(--color-text-dimmed); + } + } + +} + +@media (max-width: 40rem) { + [data-component="title-bar"] { + display: none; + } +} diff --git a/cloud/web/src/ui/style/index.css b/cloud/web/src/ui/style/index.css new file mode 100644 index 000000000..117f596d0 --- /dev/null +++ b/cloud/web/src/ui/style/index.css @@ -0,0 +1,50 @@ +/* tokens */ +@import "./token/color.css"; +@import "./token/reset.css"; +@import "./token/animation.css"; +@import "./token/font.css"; +@import "./token/space.css"; + +/* components */ +@import "./component/label.css"; +@import "./component/input.css"; +@import "./component/button.css"; +@import "./component/dialog.css"; +@import "./component/title-bar.css"; + +body { + font-family: var(--font-mono); + line-height: 1; + color: var(--color-text); + background-color: var(--color-bg); + cursor: default; + user-select: none; + text-underline-offset: 0.1875rem; +} + +a { + text-decoration: underline; + &:active { + color: var(--color-text-accent); + } +} + +::selection { + background-color: var(--color-text-accent-invert); +} + +/* Responsive utilities */ +[data-max-width] { + width: 100%; + + & > * { + max-width: 90rem; + margin-left: auto; + margin-right: auto; + width: 100%; + } + + &[data-max-width-64] > * { + max-width: 64rem; + } +} diff --git a/cloud/web/src/ui/style/token/animation.css b/cloud/web/src/ui/style/token/animation.css new file mode 100644 index 000000000..a8edfeff5 --- /dev/null +++ b/cloud/web/src/ui/style/token/animation.css @@ -0,0 +1,23 @@ +@keyframes zoomIn { + from { + opacity: 0; + transform: scale(0.95); + } + + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes zoomOut { + from { + opacity: 1; + transform: scale(1); + } + + to { + opacity: 0; + transform: scale(0.95); + } +} diff --git a/cloud/web/src/ui/style/token/color.css b/cloud/web/src/ui/style/token/color.css new file mode 100644 index 000000000..af0c46f3b --- /dev/null +++ b/cloud/web/src/ui/style/token/color.css @@ -0,0 +1,88 @@ +:root { + --color-white: hsl(0, 0%, 100%); + --color-gray-1: hsl(224, 20%, 94%); + --color-gray-2: hsl(224, 6%, 77%); + --color-gray-3: hsl(224, 6%, 56%); + --color-gray-4: hsl(224, 7%, 36%); + --color-gray-5: hsl(224, 10%, 23%); + --color-gray-6: hsl(224, 14%, 16%); + --color-black: hsl(224, 10%, 10%); + + --hue-orange: 41; + --color-orange-low: hsl(var(--hue-orange), 39%, 22%); + --color-orange: hsl(var(--hue-orange), 82%, 63%); + --color-orange-high: hsl(var(--hue-orange), 82%, 87%); + --hue-green: 101; + --color-green-low: hsl(var(--hue-green), 39%, 22%); + --color-green: hsl(var(--hue-green), 82%, 63%); + --color-green-high: hsl(var(--hue-green), 82%, 80%); + --hue-blue: 234; + --color-blue-low: hsl(var(--hue-blue), 54%, 20%); + --color-blue: hsl(var(--hue-blue), 100%, 60%); + --color-blue-high: hsl(var(--hue-blue), 100%, 87%); + --hue-purple: 281; + --color-purple-low: hsl(var(--hue-purple), 39%, 22%); + --color-purple: hsl(var(--hue-purple), 82%, 63%); + --color-purple-high: hsl(var(--hue-purple), 82%, 89%); + --hue-red: 339; + --color-red-low: hsl(var(--hue-red), 39%, 22%); + --color-red: hsl(var(--hue-red), 82%, 63%); + --color-red-high: hsl(var(--hue-red), 82%, 87%); + + --color-accent-low: hsl(13, 75%, 30%); + --color-accent: hsl(13, 88%, 57%); + --color-accent-high: hsl(13, 100%, 78%); + + --color-text: var(--color-gray-1); + --color-text-dimmed: var(--color-gray-3); + --color-text-accent: var(--color-accent); + --color-text-invert: var(--color-black); + --color-text-accent-invert: var(--color-accent-high); + --color-bg: var(--color-black); + --color-bg-surface: var(--color-gray-5); + --color-bg-accent: var(--color-accent-high); + --color-border: var(--color-gray-2); + + --color-backdrop-overlay: hsla(223, 13%, 10%, 0.66); +} + +:root[data-color-mode="light"] { + --color-white: hsl(224, 10%, 10%); + --color-gray-1: hsl(224, 14%, 16%); + --color-gray-2: hsl(224, 10%, 23%); + --color-gray-3: hsl(224, 7%, 36%); + --color-gray-4: hsl(224, 6%, 56%); + --color-gray-5: hsl(224, 6%, 77%); + --color-gray-6: hsl(224, 20%, 94%); + --color-gray-7: hsl(224, 19%, 97%); + --color-black: hsl(0, 0%, 100%); + + --color-orange-high: hsl(var(--hue-orange), 80%, 25%); + --color-orange: hsl(var(--hue-orange), 90%, 60%); + --color-orange-low: hsl(var(--hue-orange), 90%, 88%); + --color-green-high: hsl(var(--hue-green), 80%, 22%); + --color-green: hsl(var(--hue-green), 90%, 46%); + --color-green-low: hsl(var(--hue-green), 85%, 90%); + --color-blue-high: hsl(var(--hue-blue), 80%, 30%); + --color-blue: hsl(var(--hue-blue), 90%, 60%); + --color-blue-low: hsl(var(--hue-blue), 88%, 90%); + --color-purple-high: hsl(var(--hue-purple), 90%, 30%); + --color-purple: hsl(var(--hue-purple), 90%, 60%); + --color-purple-low: hsl(var(--hue-purple), 80%, 90%); + --color-red-high: hsl(var(--hue-red), 80%, 30%); + --color-red: hsl(var(--hue-red), 90%, 60%); + --color-red-low: hsl(var(--hue-red), 80%, 90%); + + --color-accent-high: hsl(13, 75%, 26%); + --color-accent: hsl(13, 88%, 60%); + --color-accent-low: hsl(13, 100%, 89%); + + --color-text-accent: var(--color-accent); + --color-text-dimmed: var(--color-gray-4); + --color-text-invert: var(--color-black); + --color-text-accent-invert: var(--color-accent-low); + --color-bg-surface: var(--color-gray-6); + --color-bg-accent: var(--color-accent); + + --color-backdrop-overlay: hsla(225, 9%, 36%, 0.66); +} diff --git a/cloud/web/src/ui/style/token/font.css b/cloud/web/src/ui/style/token/font.css new file mode 100644 index 000000000..24b2db3f2 --- /dev/null +++ b/cloud/web/src/ui/style/token/font.css @@ -0,0 +1,20 @@ +:root { + --font-size-2xs: 0.6875rem; + --font-size-xs: 0.75rem; + --font-size-sm: 0.8125rem; + --font-size-md: 0.9375rem; + --font-size-lg: 1.125rem; + --font-size-xl: 1.25rem; + --font-size-2xl: 1.5rem; + --font-size-3xl: 1.875rem; + --font-size-4xl: 2.25rem; + --font-size-5xl: 3rem; + --font-size-6xl: 3.75rem; + --font-size-7xl: 4.5rem; + --font-size-8xl: 6rem; + --font-size-9xl: 8rem; + --font-mono: IBM Plex Mono, monospace; + --font-sans: Rubik, sans-serif; + + --font-line-height: 1.75; +} diff --git a/cloud/web/src/ui/style/token/reset.css b/cloud/web/src/ui/style/token/reset.css new file mode 100644 index 000000000..f4aa1a0a9 --- /dev/null +++ b/cloud/web/src/ui/style/token/reset.css @@ -0,0 +1,212 @@ +* { + margin: 0; + padding: 0; + font: inherit; +} + +*, +*::before, +*::after { + box-sizing: border-box; + border-width: 0; + border-style: solid; + border-color: var(--global-color-border, currentColor); +} + +html { + line-height: 1.5; + --font-fallback: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + -webkit-text-size-adjust: 100%; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -moz-tab-size: 4; + tab-size: 4; + font-family: var(--global-font-body, var(--font-fallback)); +} + +hr { + height: 0; + color: inherit; + border-top-width: 1px; +} + +body { + height: 100%; + line-height: inherit; +} + +img { + border-style: none; +} + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + vertical-align: middle; +} + +img, +video { + max-width: 100%; + height: auto; +} + +p, +h1, +h2, +h3, +h4, +h5, +h6 { + overflow-wrap: break-word; +} + +ol, +ul { + list-style: none; +} + +code, +kbd, +pre, +samp { + font-size: 1em; +} + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; + background-color: transparent; + background-image: none; +} + +button, +input, +optgroup, +select, +textarea { + color: inherit; +} + +button, +select { + text-transform: none; +} + +table { + text-indent: 0; + border-color: inherit; + border-collapse: collapse; +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + color: var(--global-color-placeholder, #9ca3af); +} + +textarea { + resize: vertical; +} + +summary { + display: list-item; +} + +small { + font-size: 80%; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +dialog { + padding: 0; +} + +a { + color: inherit; + text-decoration: inherit; +} + +abbr:where([title]) { + text-decoration: underline dotted; +} + +b, +strong { + font-weight: bolder; +} + +code, +kbd, +samp, +pre { + font-size: 1em; + --font-mono-fallback: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New"; + font-family: var(--global-font-mono, var(--font-fallback)); +} + +input[type="text"], +input[type="email"], +input[type="search"], +input[type="password"] { + -webkit-appearance: none; + -moz-appearance: none; +} + +input[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px; +} + +::-webkit-search-decoration, +::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +input[type="number"] { + -moz-appearance: textfield; +} + +:-moz-ui-invalid { + box-shadow: none; +} + +:-moz-focusring { + outline: auto; +} diff --git a/cloud/web/src/ui/style/token/space.css b/cloud/web/src/ui/style/token/space.css new file mode 100644 index 000000000..b1e492f49 --- /dev/null +++ b/cloud/web/src/ui/style/token/space.css @@ -0,0 +1,38 @@ +:root { + --space-0: 0; + --space-px: 1px; + --space-0-5: 0.125rem; + --space-1: 0.25rem; + --space-1-5: 0.375rem; + --space-2: 0.5rem; + --space-2-5: 0.625rem; + --space-3: 0.75rem; + --space-3-5: 0.875rem; + --space-4: 1rem; + --space-4-5: 1.125rem; + --space-5: 1.25rem; + --space-6: 1.5rem; + --space-7: 1.75rem; + --space-8: 2rem; + --space-9: 2.25rem; + --space-10: 2.5rem; + --space-11: 2.75rem; + --space-12: 3rem; + --space-14: 3.5rem; + --space-16: 4rem; + --space-20: 5rem; + --space-24: 6rem; + --space-28: 7rem; + --space-32: 8rem; + --space-36: 9rem; + --space-40: 10rem; + --space-44: 11rem; + --space-48: 12rem; + --space-52: 13rem; + --space-56: 14rem; + --space-60: 15rem; + --space-64: 16rem; + --space-72: 18rem; + --space-80: 20rem; + --space-96: 24rem; +} diff --git a/cloud/web/src/ui/svg/icons.tsx b/cloud/web/src/ui/svg/icons.tsx new file mode 100644 index 000000000..c09bbc47a --- /dev/null +++ b/cloud/web/src/ui/svg/icons.tsx @@ -0,0 +1,1292 @@ +import { JSX } from "solid-js" + +export function IconPencilSquare(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconHome(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconPlus(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconDocument(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconChat(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconBell(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconCheck(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconChevronDown(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconChevronUp(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconChevronLeft(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconChevronRight(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconTrash(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconUser(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconCog(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconExclamationCircle( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconInformationCircle( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconArrowPath(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowUpRight(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconEllipsisVertical( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconEllipsisHorizontal( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconXMark(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconAcademicCap(props: JSX.SvgSVGAttributes) { + return ( + + + + + + ) +} + +export function IconBolt(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconCalendar(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconCamera(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconClock(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconCloud(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconCreditCard(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconEnvelope(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconEye(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconFlag(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconFolder(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconGlobe(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconHeart(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconKey(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconLink(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconLock(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconMap(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconMicrophone(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconPhone(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconPhoto(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconQuestionMarkCircle( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconMagnifyingGlass( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconShieldCheck(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconShoppingCart(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconStar(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconTag(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconUserCircle(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconVideoCamera(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconWifi(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconAdjustmentsVertical( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconArchiveBox(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconArrowDown(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowLeft(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowLongDown(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowLongLeft(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowLongRight(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowLongUp(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowRight(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowSmallDown(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconArrowSmallLeft(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconArrowSmallRight( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + + ) +} + +export function IconArrowSmallUp(props: JSX.SvgSVGAttributes) { + return ( + + + + + ) +} + +export function IconArrowTopRightOnSquare( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconArrowTrendingDown( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + + ) +} + +export function IconArrowTrendingUp( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconArrowUp(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowUpCircle(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowUpLeft(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowUpOnSquare( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + + ) +} + +export function IconArrowUpTray(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconArrowsPointingIn( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconArrowsPointingOut( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconArrowsRightLeft( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} + +export function IconBars3BottomLeft( + props: JSX.SvgSVGAttributes, +) { + return ( + + + + ) +} diff --git a/cloud/web/src/ui/svg/index.tsx b/cloud/web/src/ui/svg/index.tsx new file mode 100644 index 000000000..23dd74c6e --- /dev/null +++ b/cloud/web/src/ui/svg/index.tsx @@ -0,0 +1,67 @@ +import { JSX } from "solid-js" + +export function IconLogomark(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + +export function IconLogo(props: JSX.SvgSVGAttributes) { + return ( + + + + + + + + + + + + + + + ) +} diff --git a/cloud/web/src/util/context.tsx b/cloud/web/src/util/context.tsx new file mode 100644 index 000000000..d1c6f4e7f --- /dev/null +++ b/cloud/web/src/util/context.tsx @@ -0,0 +1,26 @@ +import { ParentProps, Show, createContext, useContext } from "solid-js" + +export function createInitializedContext< + Name extends string, + T extends { ready: boolean }, +>(name: Name, cb: () => T) { + const ctx = createContext() + + return { + use: () => { + const context = useContext(ctx) + if (!context) throw new Error(`No ${name} context`) + return context + }, + provider: (props: ParentProps) => { + const value = cb() + return ( + + + {props.children} + + + ) + }, + } +} diff --git a/cloud/web/sst-env.d.ts b/cloud/web/sst-env.d.ts new file mode 100644 index 000000000..b6a7e9066 --- /dev/null +++ b/cloud/web/sst-env.d.ts @@ -0,0 +1,9 @@ +/* This file is auto-generated by SST. Do not edit. */ +/* tslint:disable */ +/* eslint-disable */ +/* deno-fmt-ignore-file */ + +/// + +import "sst" +export {} \ No newline at end of file diff --git a/cloud/web/tsconfig.json b/cloud/web/tsconfig.json new file mode 100644 index 000000000..98d5b9cea --- /dev/null +++ b/cloud/web/tsconfig.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@tsconfig/node22/tsconfig.json", + "compilerOptions": { + "jsx": "preserve", + "jsxImportSource": "solid-js", + "module": "esnext", + "moduleResolution": "bundler", + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "types": ["vite/client"] + } +} diff --git a/cloud/web/vite.config.ts b/cloud/web/vite.config.ts new file mode 100644 index 000000000..8a569641e --- /dev/null +++ b/cloud/web/vite.config.ts @@ -0,0 +1,63 @@ +import { defineConfig } from "vite" +import solidPlugin from "vite-plugin-solid" +import pages from "vite-plugin-pages" +import fs from "fs" +import path from "path" +import { generateHydrationScript, getAssets } from "solid-js/web" + +export default defineConfig({ + plugins: [ + pages({ + exclude: ["**/~*", "**/components/*"], + }), + solidPlugin({ ssr: true }), + { + name: "vite-plugin-solid-ssr-render", + apply: (config, env) => { + return env.command === "build" && !config.build?.ssr + }, + closeBundle: async () => { + console.log("Pre-rendering pages...") + const dist = path.resolve("dist") + try { + const serverEntryPath = path.join(dist, "server/entry-server.js") + const serverEntry = await import(serverEntryPath + "?t=" + Date.now()) + + const template = fs.readFileSync( + path.join(dist, "client/index.html"), + "utf-8", + ) + fs.writeFileSync(path.join(dist, "client/fallback.html"), template) + + const routes = ["/"] + for (const route of routes) { + const { app } = await serverEntry.render({ url: route }) + const html = template + .replace("", app) + .replace("", generateHydrationScript()) + .replace("", getAssets()) + const filePath = path.join( + dist, + `client${route === "/" ? "/index" : route}.html`, + ) + fs.mkdirSync(path.dirname(filePath), { + recursive: true, + }) + fs.writeFileSync(filePath, html) + + console.log(`Pre-rendered: ${filePath}`) + } + } catch (error) { + console.error("Error during pre-rendering:", error) + } + }, + }, + ], + server: { + port: 3000, + host: "0.0.0.0", + }, + build: { + target: "esnext", + }, +}) diff --git a/infra/app.ts b/infra/app.ts index 2b09516d7..008c1245d 100644 --- a/infra/app.ts +++ b/infra/app.ts @@ -1,8 +1,4 @@ -export const domain = (() => { - if ($app.stage === "production") return "opencode.ai" - if ($app.stage === "dev") return "dev.opencode.ai" - return `${$app.stage}.dev.opencode.ai` -})() +import { domain } from "./stage" const GITHUB_APP_ID = new sst.Secret("GITHUB_APP_ID") const GITHUB_APP_PRIVATE_KEY = new sst.Secret("GITHUB_APP_PRIVATE_KEY") @@ -37,24 +33,12 @@ export const api = new sst.cloudflare.Worker("Api", { }, }) -new sst.cloudflare.x.Astro("Web", { +export const web = new sst.cloudflare.x.Astro("Web", { domain, path: "packages/web", environment: { // For astro config SST_STAGE: $app.stage, - VITE_API_URL: api.url, + VITE_API_URL: api.url.apply((url) => url!), }, }) - -const OPENCODE_API_KEY = new sst.Secret("OPENCODE_API_KEY") -const ANTHROPIC_API_KEY = new sst.Secret("ANTHROPIC_API_KEY") -const OPENAI_API_KEY = new sst.Secret("OPENAI_API_KEY") -const ZHIPU_API_KEY = new sst.Secret("ZHIPU_API_KEY") - -export const gateway = new sst.cloudflare.Worker("GatewayApi", { - domain: `api.gateway.${domain}`, - handler: "packages/function/src/gateway.ts", - url: true, - link: [OPENCODE_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, ZHIPU_API_KEY], -}) diff --git a/infra/cloud.ts b/infra/cloud.ts new file mode 100644 index 000000000..4625bb282 --- /dev/null +++ b/infra/cloud.ts @@ -0,0 +1,105 @@ +import { WebhookEndpoint } from "pulumi-stripe" +import { domain } from "./stage" +import { web } from "./app" + +export const stripeWebhook = new WebhookEndpoint("StripeWebhook", { + url: $interpolate`https://api.gateway.${domain}/stripe/webhook`, + enabledEvents: [ + "checkout.session.async_payment_failed", + "checkout.session.async_payment_succeeded", + "checkout.session.completed", + "checkout.session.expired", + "customer.created", + "customer.deleted", + "customer.updated", + "customer.discount.created", + "customer.discount.deleted", + "customer.discount.updated", + "customer.source.created", + "customer.source.deleted", + "customer.source.expiring", + "customer.source.updated", + "customer.subscription.created", + "customer.subscription.deleted", + "customer.subscription.paused", + "customer.subscription.pending_update_applied", + "customer.subscription.pending_update_expired", + "customer.subscription.resumed", + "customer.subscription.trial_will_end", + "customer.subscription.updated", + "customer.tax_id.created", + "customer.tax_id.deleted", + "customer.tax_id.updated", + ], +}) + +const DATABASE_USERNAME = new sst.Secret("DATABASE_USERNAME") +const DATABASE_PASSWORD = new sst.Secret("DATABASE_PASSWORD") +export const database = new sst.Linkable("Database", { + properties: { + host: "aws-us-east-2-1.pg.psdb.cloud", + database: "postgres", + username: DATABASE_USERNAME.value, + password: DATABASE_PASSWORD.value, + port: 5432, + }, +}) + +new sst.x.DevCommand("Studio", { + link: [database], + dev: { + command: "bun db studio", + directory: "cloud/core", + autostart: true, + }, +}) + +const GITHUB_CLIENT_ID_CONSOLE = new sst.Secret("GITHUB_CLIENT_ID_CONSOLE") +const GITHUB_CLIENT_SECRET_CONSOLE = new sst.Secret("GITHUB_CLIENT_SECRET_CONSOLE") +const authStorage = new sst.cloudflare.Kv("AuthStorage") +export const auth = new sst.cloudflare.Worker("AuthApi", { + domain: `auth.${domain}`, + handler: "cloud/function/src/auth.ts", + url: true, + link: [database, authStorage, GITHUB_CLIENT_ID_CONSOLE, GITHUB_CLIENT_SECRET_CONSOLE], +}) + +const ANTHROPIC_API_KEY = new sst.Secret("ANTHROPIC_API_KEY") +const OPENAI_API_KEY = new sst.Secret("OPENAI_API_KEY") +const ZHIPU_API_KEY = new sst.Secret("ZHIPU_API_KEY") + +const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY") +const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", { + properties: { value: auth.url.apply((url) => url!) }, +}) +const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", { + properties: { value: stripeWebhook.secret }, +}) +export const gateway = new sst.cloudflare.Worker("GatewayApi", { + domain: `api.gateway.${domain}`, + handler: "cloud/function/src/gateway.ts", + url: true, + link: [ + database, + AUTH_API_URL, + STRIPE_WEBHOOK_SECRET, + STRIPE_SECRET_KEY, + ANTHROPIC_API_KEY, + OPENAI_API_KEY, + ZHIPU_API_KEY, + ], +}) + +export const console = new sst.cloudflare.x.StaticSite("Console", { + domain: `console.${domain}`, + path: "cloud/web", + build: { + command: "bun run build", + output: "dist/client", + }, + environment: { + VITE_DOCS_URL: web.url.apply((url) => url!), + VITE_API_URL: gateway.url.apply((url) => url!), + VITE_AUTH_URL: auth.url.apply((url) => url!), + }, +}) diff --git a/infra/stage.ts b/infra/stage.ts new file mode 100644 index 000000000..c1239832b --- /dev/null +++ b/infra/stage.ts @@ -0,0 +1,5 @@ +export const domain = (() => { + if ($app.stage === "production") return "opencode.ai" + if ($app.stage === "dev") return "dev.opencode.ai" + return `${$app.stage}.dev.opencode.ai` +})() diff --git a/opencode.json b/opencode.json index 59f14ac75..12a82643f 100644 --- a/opencode.json +++ b/opencode.json @@ -1,5 +1,25 @@ { "$schema": "https://opencode.ai/config.json", + "provider": { + "oc-frank": { + "npm": "@ai-sdk/openai-compatible", + "name": "OC-Frank", + "options": { + "baseURL": "https://api.gateway.frank.dev.opencode.ai/v1" + }, + "models": { + "anthropic/claude-sonnet-4": { + "name": "Claude Sonnet 4" + }, + "openai/gpt-4.1": { + "name": "GPT-4.1" + }, + "zhipuai/glm-4.5-flash": { + "name": "GLM-4.5 Flash" + } + } + } + }, "mcp": { "context7": { "type": "remote", diff --git a/package.json b/package.json index 7054e2871..52d9a8566 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,12 @@ }, "workspaces": { "packages": [ + "cloud/*", "packages/*", "packages/sdk/js" ], "catalog": { + "@hono/zod-validator": "0.4.2", "@types/node": "22.13.9", "@tsconfig/node22": "22.0.2", "ai": "5.0.0-beta.34", @@ -25,6 +27,9 @@ "remeda": "2.26.0" } }, + "dependencies": { + "pulumi-stripe": "0.0.24" + }, "devDependencies": { "prettier": "3.5.3", "sst": "3.17.8" diff --git a/packages/function/package.json b/packages/function/package.json index 1a256447d..99474496c 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -7,16 +7,11 @@ "devDependencies": { "@cloudflare/workers-types": "4.20250522.0", "@types/node": "catalog:", - "openai": "5.11.0", "typescript": "catalog:" }, "dependencies": { - "@ai-sdk/anthropic": "2.0.0", - "@ai-sdk/openai": "2.0.2", - "@ai-sdk/openai-compatible": "1.0.1", "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", - "ai": "catalog:", "hono": "catalog:", "jose": "6.0.11" } diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index 7106662e3..4e2b15921 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -10,6 +10,30 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "AUTH_API_URL": { + "type": "sst.sst.Linkable" + "value": string + } + "Console": { + "type": "sst.cloudflare.StaticSite" + "url": string + } + "DATABASE_PASSWORD": { + "type": "sst.sst.Secret" + "value": string + } + "DATABASE_USERNAME": { + "type": "sst.sst.Secret" + "value": string + } + "Database": { + "database": string + "host": string + "password": string + "port": number + "type": "sst.sst.Linkable" + "username": string + } "GITHUB_APP_ID": { "type": "sst.sst.Secret" "value": string @@ -18,14 +42,26 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "GITHUB_CLIENT_ID_CONSOLE": { + "type": "sst.sst.Secret" + "value": string + } + "GITHUB_CLIENT_SECRET_CONSOLE": { + "type": "sst.sst.Secret" + "value": string + } "OPENAI_API_KEY": { "type": "sst.sst.Secret" "value": string } - "OPENCODE_API_KEY": { + "STRIPE_SECRET_KEY": { "type": "sst.sst.Secret" "value": string } + "STRIPE_WEBHOOK_SECRET": { + "type": "sst.sst.Linkable" + "value": string + } "Web": { "type": "sst.cloudflare.Astro" "url": string @@ -41,6 +77,8 @@ import * as cloudflare from "@cloudflare/workers-types"; declare module "sst" { export interface Resource { "Api": cloudflare.Service + "AuthApi": cloudflare.Service + "AuthStorage": cloudflare.KVNamespace "Bucket": cloudflare.R2Bucket "GatewayApi": cloudflare.Service } diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 2b83805f2..d27727d73 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -31,7 +31,7 @@ "@actions/core": "1.11.1", "@actions/github": "6.0.1", "@clack/prompts": "1.0.0-alpha.1", - "@hono/zod-validator": "0.4.2", + "@hono/zod-validator": "catalog:", "@modelcontextprotocol/sdk": "1.15.1", "@octokit/graphql": "9.0.1", "@octokit/rest": "22.0.0", diff --git a/sst-env.d.ts b/sst-env.d.ts index 8286f0938..ffc049c93 100644 --- a/sst-env.d.ts +++ b/sst-env.d.ts @@ -9,13 +9,44 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "AUTH_API_URL": { + "type": "sst.sst.Linkable" + "value": string + } "Api": { "type": "sst.cloudflare.Worker" "url": string } + "AuthApi": { + "type": "sst.cloudflare.Worker" + "url": string + } + "AuthStorage": { + "type": "sst.cloudflare.Kv" + } "Bucket": { "type": "sst.cloudflare.Bucket" } + "Console": { + "type": "sst.cloudflare.StaticSite" + "url": string + } + "DATABASE_PASSWORD": { + "type": "sst.sst.Secret" + "value": string + } + "DATABASE_USERNAME": { + "type": "sst.sst.Secret" + "value": string + } + "Database": { + "database": string + "host": string + "password": string + "port": number + "type": "sst.sst.Linkable" + "username": string + } "GITHUB_APP_ID": { "type": "sst.sst.Secret" "value": string @@ -24,6 +55,14 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "GITHUB_CLIENT_ID_CONSOLE": { + "type": "sst.sst.Secret" + "value": string + } + "GITHUB_CLIENT_SECRET_CONSOLE": { + "type": "sst.sst.Secret" + "value": string + } "GatewayApi": { "type": "sst.cloudflare.Worker" "url": string @@ -32,10 +71,14 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } - "OPENCODE_API_KEY": { + "STRIPE_SECRET_KEY": { "type": "sst.sst.Secret" "value": string } + "STRIPE_WEBHOOK_SECRET": { + "type": "sst.sst.Linkable" + "value": string + } "Web": { "type": "sst.cloudflare.Astro" "url": string diff --git a/sst.config.ts b/sst.config.ts index c15fdabb6..44f984b93 100644 --- a/sst.config.ts +++ b/sst.config.ts @@ -7,13 +7,20 @@ export default $config({ removal: input?.stage === "production" ? "retain" : "remove", protect: ["production"].includes(input?.stage), home: "cloudflare", + providers: { + stripe: { + apiKey: process.env.STRIPE_SECRET_KEY, + }, + }, } }, async run() { - const { api, gateway } = await import("./infra/app.js") + const { api } = await import("./infra/app.js") + const { auth, gateway } = await import("./infra/cloud.js") return { api: api.url, gateway: gateway.url, + auth: auth.url, } }, }) From 8c173e18b7e0e52fe8345a7a1112ea617d83118f Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 8 Aug 2025 13:24:50 -0400 Subject: [PATCH 011/178] wip: gateway --- bun.lock | 1225 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 1173 insertions(+), 52 deletions(-) diff --git a/bun.lock b/bun.lock index 223401a40..996a788a3 100644 --- a/bun.lock +++ b/bun.lock @@ -3,23 +3,39 @@ "workspaces": { "": { "name": "opencode", + "dependencies": { + "pulumi-stripe": "0.0.24", + }, "devDependencies": { "prettier": "3.5.3", "sst": "3.17.8", }, }, - "packages/function": { - "name": "@opencode/function", - "version": "0.3.133", + "cloud/core": { + "name": "@opencode/cloud-core", + "version": "0.0.0", + "dependencies": { + "@aws-sdk/client-sts": "3.782.0", + "drizzle-orm": "0.41.0", + "stripe": "18.0.0", + "ulid": "3.0.0", + }, + "devDependencies": { + "drizzle-kit": "0.30.5", + }, + }, + "cloud/function": { + "name": "@opencode/cloud-function", + "version": "0.3.130", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", "@ai-sdk/openai-compatible": "1.0.1", - "@octokit/auth-app": "8.0.1", - "@octokit/rest": "22.0.0", + "@hono/zod-validator": "catalog:", + "@openauthjs/openauth": "0.0.0-20250322224806", "ai": "catalog:", "hono": "catalog:", - "jose": "6.0.11", + "zod": "catalog:", }, "devDependencies": { "@cloudflare/workers-types": "4.20250522.0", @@ -28,9 +44,43 @@ "typescript": "catalog:", }, }, + "cloud/web": { + "name": "@opencode/cloud-web", + "version": "0.0.0", + "dependencies": { + "@kobalte/core": "0.13.9", + "@openauthjs/solid": "0.0.0-20250322224806", + "@solid-primitives/storage": "4.3.1", + "@solidjs/meta": "0.29.4", + "@solidjs/router": "0.15.3", + "solid-js": "1.9.5", + "solid-list": "0.3.0", + }, + "devDependencies": { + "typescript": "catalog:", + "vite": "6.2.2", + "vite-plugin-pages": "0.32.5", + "vite-plugin-solid": "2.11.6", + }, + }, + "packages/function": { + "name": "@opencode/function", + "version": "0.4.1", + "dependencies": { + "@octokit/auth-app": "8.0.1", + "@octokit/rest": "22.0.0", + "hono": "catalog:", + "jose": "6.0.11", + }, + "devDependencies": { + "@cloudflare/workers-types": "4.20250522.0", + "@types/node": "catalog:", + "typescript": "catalog:", + }, + }, "packages/opencode": { "name": "opencode", - "version": "0.3.133", + "version": "0.4.1", "bin": { "opencode": "./bin/opencode", }, @@ -38,7 +88,7 @@ "@actions/core": "1.11.1", "@actions/github": "6.0.1", "@clack/prompts": "1.0.0-alpha.1", - "@hono/zod-validator": "0.4.2", + "@hono/zod-validator": "catalog:", "@modelcontextprotocol/sdk": "1.15.1", "@octokit/graphql": "9.0.1", "@octokit/rest": "22.0.0", @@ -90,7 +140,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "0.3.133", + "version": "0.4.1", "dependencies": { "@opencode-ai/sdk": "workspace:*", }, @@ -102,7 +152,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "0.3.133", + "version": "0.4.1", "devDependencies": { "@hey-api/openapi-ts": "0.80.1", "@tsconfig/node22": "catalog:", @@ -111,7 +161,7 @@ }, "packages/web": { "name": "@opencode/web", - "version": "0.3.133", + "version": "0.4.1", "dependencies": { "@astrojs/cloudflare": "^12.5.4", "@astrojs/markdown-remark": "6.3.1", @@ -148,6 +198,7 @@ "protobufjs", ], "catalog": { + "@hono/zod-validator": "0.4.2", "@tsconfig/node22": "22.0.2", "@types/node": "22.13.9", "ai": "5.0.0-beta.34", @@ -209,9 +260,57 @@ "@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="], + "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="], + + "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="], + + "@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="], + "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], - "@aws-sdk/types": ["@aws-sdk/types@3.840.0", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA=="], + "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-5GlJBejo8wqMpSSEKb45WE82YxI2k73YuebjLH/eWDNQeE6VI5Bh9lA1YQ7xNkLLH8hIsb0pSfKVuwh0VEzVrg=="], + + "@aws-sdk/client-sts": ["@aws-sdk/client-sts@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/credential-provider-node": "3.782.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-Q1QLY3xE2z1trgriusP/6w40mI/yJjM524bN4gs+g6YX4sZGufpa7+Dj+JjL4fz8f9BCJ3ZlI+p4WxFxH7qvdQ=="], + + "@aws-sdk/core": ["@aws-sdk/core@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/core": "^3.2.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/signature-v4": "^5.0.2", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA=="], + + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw=="], + + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-stream": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww=="], + + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.782.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/credential-provider-env": "3.775.0", "@aws-sdk/credential-provider-http": "3.775.0", "@aws-sdk/credential-provider-process": "3.775.0", "@aws-sdk/credential-provider-sso": "3.782.0", "@aws-sdk/credential-provider-web-identity": "3.782.0", "@aws-sdk/nested-clients": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wd4KdRy2YjLsE4Y7pz00470Iip06GlRHkG4dyLW7/hFMzEO2o7ixswCWp6J2VGZVAX64acknlv2Q0z02ebjmhw=="], + + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.782.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.775.0", "@aws-sdk/credential-provider-http": "3.775.0", "@aws-sdk/credential-provider-ini": "3.782.0", "@aws-sdk/credential-provider-process": "3.775.0", "@aws-sdk/credential-provider-sso": "3.782.0", "@aws-sdk/credential-provider-web-identity": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-HZiAF+TCEyKjju9dgysjiPIWgt/+VerGaeEp18mvKLNfgKz1d+/82A2USEpNKTze7v3cMFASx3CvL8yYyF7mJw=="], + + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-A6k68H9rQp+2+7P7SGO90Csw6nrUEm0Qfjpn9Etc4EboZhhCLs9b66umUsTsSBHus4FDIe5JQxfCUyt1wgNogg=="], + + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.782.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.782.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/token-providers": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-1y1ucxTtTIGDSNSNxriQY8msinilhe9gGvQpUDYW9gboyC7WQJPDw66imy258V6osdtdi+xoHzVCbCz3WhosMQ=="], + + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.782.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/nested-clients": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-xCna0opVPaueEbJoclj5C6OpDNi0Gynj+4d7tnuXGgQhTHPyAz8ZyClkVqpi5qvHTgxROdUEDxWqEO5jqRHZHQ=="], + + "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-tkSegM0Z6WMXpLB8oPys/d+umYIocvO298mGvcMCncpRl77L9XkvSLJIFzaHes+o7djAgIduYw8wKIMStFss2w=="], + + "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-FaxO1xom4MAoUJsldmR92nT1G6uZxTdNYOFYtdHfd6N2wcNaTuxgjIvqzg5y7QIH9kn58XX/dzf1iTjgqUStZw=="], + + "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GLCzC8D0A0YDG5u3F5U03Vb9j5tcOEFhr8oc6PDk0k0vm5VwtZOE6LvK7hcCSoAB4HXyOUM0sQuXrbaAh9OwXA=="], + + "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.782.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@smithy/core": "^3.2.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-i32H2R6IItX+bQ2p4+v2gGO2jA80jQoJO2m1xjU9rYWQW3+ErWy4I5YIuQHTBfb6hSdAHbaRfqPDgbv9J2rjEg=="], + + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA=="], + + "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/types": "^4.2.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.2", "tslib": "^2.6.2" } }, "sha512-40iH3LJjrQS3LKUJAl7Wj0bln7RFPEvUYKFxtP8a+oKFDO0F65F52xZxIJbPn6sHkxWDAnZlGgdjZXM3p2g5wQ=="], + + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.782.0", "", { "dependencies": { "@aws-sdk/nested-clients": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-4tPuk/3+THPrzKaXW4jE2R67UyGwHLFizZ47pcjJWbhb78IIJAy94vbeqEQ+veS84KF5TXcU7g5jGTXC0D70Wg=="], + + "@aws-sdk/types": ["@aws-sdk/types@3.775.0", "", { "dependencies": { "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA=="], + + "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.782.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/types": "^4.2.0", "@smithy/util-endpoints": "^3.0.2", "tslib": "^2.6.2" } }, "sha512-/RJOAO7o7HI6lEa4ASbFFLHGU9iPK876BhsVfnl54MvApPVYWQ9sHO0anOUim2S5lQTwd/6ghuH3rFYSq/+rdw=="], + + "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.804.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A=="], + + "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/types": "^4.2.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-txw2wkiJmZKVdDbscK7VBK+u+TJnRtlUjRTLei+elZg2ADhpQxfVAQl436FUeIv6AhB/oRHW6/K/EAGXUSWi0A=="], + + "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.782.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dMFkUBgh2Bxuw8fYZQoH/u3H4afQ12VSkzEi//qFiDTwbKYq+u+RYjc8GLDM6JSK1BShMu5AVR7HD4ap1TYUnA=="], "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], @@ -273,63 +372,71 @@ "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250522.0", "", {}, "sha512-9RIffHobc35JWeddzBguGgPa4wLDr5x5F94+0/qy7LiV6pTBQ/M5qGEN9VA16IDT3EUpYI0WKh6VpcmeVEtVtw=="], + "@corvu/utils": ["@corvu/utils@0.4.2", "", { "dependencies": { "@floating-ui/dom": "^1.6.11" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-Ox2kYyxy7NoXdKWdHeDEjZxClwzO4SKM8plAaVwmAJPxHMqA0rLOoAsa+hBDwRLpctf+ZRnAd/ykguuJidnaTA=="], + "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], "@ctrl/tinycolor": ["@ctrl/tinycolor@4.1.0", "", {}, "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ=="], + "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], + "@emnapi/runtime": ["@emnapi/runtime@1.4.5", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.8", "", { "os": "aix", "cpu": "ppc64" }, "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA=="], + "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.8", "", { "os": "android", "cpu": "arm" }, "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw=="], + "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.8", "", { "os": "android", "cpu": "arm64" }, "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.8", "", { "os": "android", "cpu": "x64" }, "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.12", "", { "os": "android", "cpu": "arm64" }, "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.19.12", "", { "os": "android", "cpu": "x64" }, "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.8", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.8", "", { "os": "linux", "cpu": "arm" }, "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.8", "", { "os": "linux", "cpu": "ia32" }, "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.12", "", { "os": "linux", "cpu": "arm" }, "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.8", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.8", "", { "os": "linux", "cpu": "s390x" }, "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.8", "", { "os": "linux", "cpu": "x64" }, "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.12", "", { "os": "linux", "cpu": "x64" }, "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="], "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.8", "", { "os": "none", "cpu": "arm64" }, "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.8", "", { "os": "none", "cpu": "x64" }, "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.12", "", { "os": "none", "cpu": "x64" }, "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="], "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.8", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.8", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="], "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.8", "", { "os": "none", "cpu": "arm64" }, "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.8", "", { "os": "sunos", "cpu": "x64" }, "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="], "@expressive-code/core": ["@expressive-code/core@0.41.3", "", { "dependencies": { "@ctrl/tinycolor": "^4.0.4", "hast-util-select": "^6.0.2", "hast-util-to-html": "^9.0.1", "hast-util-to-text": "^4.0.1", "hastscript": "^9.0.0", "postcss": "^8.4.38", "postcss-nested": "^6.0.1", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1" } }, "sha512-9qzohqU7O0+JwMEEgQhnBPOw5DtsQRBXhW++5fvEywsuX44vCGGof1SL5OvPElvNgaWZ4pFZAFSlkNOkGyLwSQ=="], @@ -341,6 +448,12 @@ "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], + "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.3", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + "@fontsource/ibm-plex-mono": ["@fontsource/ibm-plex-mono@5.2.5", "", {}, "sha512-G09N3GfuT9qj3Ax2FDZvKqZttzM3v+cco2l8uXamhKyXLdmlaUDH5o88/C3vtTHj2oT7yRKsvxz9F+BXbWKMYA=="], "@grpc/grpc-js": ["@grpc/grpc-js@1.13.4", "", { "dependencies": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg=="], @@ -391,28 +504,76 @@ "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + "@internationalized/date": ["@internationalized/date@3.8.2", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA=="], + + "@internationalized/number": ["@internationalized/number@3.6.4", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-P+/h+RDaiX8EGt3shB9AYM1+QgkvHmJ5rKi4/59k4sg9g58k9rqsRW0WxRO7jCoHyvVbFRRFKmVTdFYdehrxHg=="], + "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "@isaacs/string-locale-compare": ["@isaacs/string-locale-compare@1.1.0", "", {}, "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.12", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg=="], "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.4", "", {}, "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw=="], - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.29", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ=="], "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="], "@jsdevtools/ono": ["@jsdevtools/ono@7.1.3", "", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="], + "@kobalte/core": ["@kobalte/core@0.13.9", "", { "dependencies": { "@floating-ui/dom": "^1.5.1", "@internationalized/date": "^3.4.0", "@internationalized/number": "^3.2.1", "@kobalte/utils": "^0.9.1", "@solid-primitives/props": "^3.1.8", "@solid-primitives/resize-observer": "^2.0.26", "solid-presence": "^0.1.8", "solid-prevent-scroll": "^0.1.4" }, "peerDependencies": { "solid-js": "^1.8.15" } }, "sha512-TkeSpgNy7I5k8jwjqT9CK3teAxN0aFb3yyL9ODb06JVYMwXIk+UKrizoAF1ahLUP85lKnxv44B4Y5cXkHShgqw=="], + + "@kobalte/utils": ["@kobalte/utils@0.9.1", "", { "dependencies": { "@solid-primitives/event-listener": "^2.2.14", "@solid-primitives/keyed": "^1.2.0", "@solid-primitives/map": "^0.4.7", "@solid-primitives/media": "^2.2.4", "@solid-primitives/props": "^3.1.8", "@solid-primitives/refs": "^1.0.5", "@solid-primitives/utils": "^6.2.1" }, "peerDependencies": { "solid-js": "^1.8.8" } }, "sha512-eeU60A3kprIiBDAfv9gUJX1tXGLuZiKMajUfSQURAF2pk4ZoMYiqIzmrMBvzcxP39xnYttgTyQEVLwiTZnrV4w=="], + + "@logdna/tail-file": ["@logdna/tail-file@2.2.0", "", {}, "sha512-XGSsWDweP80Fks16lwkAUIr54ICyBs6PsI4mpfTLQaWgEJRtY9xEV+PeyDpJ+sJEGZxqINlpmAwe/6tS1pP8Ng=="], + "@mdx-js/mdx": ["@mdx-js/mdx@3.1.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw=="], "@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="], "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.15.1", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-W/XlN9c528yYn+9MQkVjxiTPgPxoxt+oczfjHBDsJx0+59+O7B75Zhsp0B16Xbwbz8ANISDajh6+V7nIcPMc5w=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@npmcli/agent": ["@npmcli/agent@2.2.2", "", { "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^10.0.1", "socks-proxy-agent": "^8.0.3" } }, "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og=="], + + "@npmcli/arborist": ["@npmcli/arborist@7.5.4", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^3.1.1", "@npmcli/installed-package-contents": "^2.1.0", "@npmcli/map-workspaces": "^3.0.2", "@npmcli/metavuln-calculator": "^7.1.1", "@npmcli/name-from-folder": "^2.0.0", "@npmcli/node-gyp": "^3.0.0", "@npmcli/package-json": "^5.1.0", "@npmcli/query": "^3.1.0", "@npmcli/redact": "^2.0.0", "@npmcli/run-script": "^8.1.0", "bin-links": "^4.0.4", "cacache": "^18.0.3", "common-ancestor-path": "^1.0.1", "hosted-git-info": "^7.0.2", "json-parse-even-better-errors": "^3.0.2", "json-stringify-nice": "^1.1.4", "lru-cache": "^10.2.2", "minimatch": "^9.0.4", "nopt": "^7.2.1", "npm-install-checks": "^6.2.0", "npm-package-arg": "^11.0.2", "npm-pick-manifest": "^9.0.1", "npm-registry-fetch": "^17.0.1", "pacote": "^18.0.6", "parse-conflict-json": "^3.0.0", "proc-log": "^4.2.0", "proggy": "^2.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", "ssri": "^10.0.6", "treeverse": "^3.0.0", "walk-up-path": "^3.0.1" }, "bin": { "arborist": "bin/index.js" } }, "sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g=="], + + "@npmcli/fs": ["@npmcli/fs@3.1.1", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg=="], + + "@npmcli/git": ["@npmcli/git@5.0.8", "", { "dependencies": { "@npmcli/promise-spawn": "^7.0.0", "ini": "^4.1.3", "lru-cache": "^10.0.1", "npm-pick-manifest": "^9.0.0", "proc-log": "^4.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", "which": "^4.0.0" } }, "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ=="], + + "@npmcli/installed-package-contents": ["@npmcli/installed-package-contents@2.1.0", "", { "dependencies": { "npm-bundled": "^3.0.0", "npm-normalize-package-bin": "^3.0.0" }, "bin": { "installed-package-contents": "bin/index.js" } }, "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w=="], + + "@npmcli/map-workspaces": ["@npmcli/map-workspaces@3.0.6", "", { "dependencies": { "@npmcli/name-from-folder": "^2.0.0", "glob": "^10.2.2", "minimatch": "^9.0.0", "read-package-json-fast": "^3.0.0" } }, "sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA=="], + + "@npmcli/metavuln-calculator": ["@npmcli/metavuln-calculator@7.1.1", "", { "dependencies": { "cacache": "^18.0.0", "json-parse-even-better-errors": "^3.0.0", "pacote": "^18.0.0", "proc-log": "^4.1.0", "semver": "^7.3.5" } }, "sha512-Nkxf96V0lAx3HCpVda7Vw4P23RILgdi/5K1fmj2tZkWIYLpXAN8k2UVVOsW16TsS5F8Ws2I7Cm+PU1/rsVF47g=="], + + "@npmcli/name-from-folder": ["@npmcli/name-from-folder@2.0.0", "", {}, "sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg=="], + + "@npmcli/node-gyp": ["@npmcli/node-gyp@3.0.0", "", {}, "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA=="], + + "@npmcli/package-json": ["@npmcli/package-json@5.2.1", "", { "dependencies": { "@npmcli/git": "^5.0.0", "glob": "^10.2.2", "hosted-git-info": "^7.0.0", "json-parse-even-better-errors": "^3.0.0", "normalize-package-data": "^6.0.0", "proc-log": "^4.0.0", "semver": "^7.5.3" } }, "sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ=="], + + "@npmcli/promise-spawn": ["@npmcli/promise-spawn@7.0.2", "", { "dependencies": { "which": "^4.0.0" } }, "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ=="], + + "@npmcli/query": ["@npmcli/query@3.1.0", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" } }, "sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ=="], + + "@npmcli/redact": ["@npmcli/redact@2.0.1", "", {}, "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw=="], + + "@npmcli/run-script": ["@npmcli/run-script@8.1.0", "", { "dependencies": { "@npmcli/node-gyp": "^3.0.0", "@npmcli/package-json": "^5.0.0", "@npmcli/promise-spawn": "^7.0.0", "node-gyp": "^10.0.0", "proc-log": "^4.0.0", "which": "^4.0.0" } }, "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg=="], + "@octokit/auth-app": ["@octokit/auth-app@8.0.1", "", { "dependencies": { "@octokit/auth-oauth-app": "^9.0.1", "@octokit/auth-oauth-user": "^6.0.0", "@octokit/request": "^10.0.2", "@octokit/request-error": "^7.0.0", "@octokit/types": "^14.0.0", "toad-cache": "^3.7.0", "universal-github-app-jwt": "^2.2.0", "universal-user-agent": "^7.0.0" } }, "sha512-P2J5pB3pjiGwtJX4WqJVYCtNkcZ+j5T2Wm14aJAEIC3WJOrv12jvBley3G1U/XI8q9o1A7QMG54LiFED2BiFlg=="], "@octokit/auth-oauth-app": ["@octokit/auth-oauth-app@9.0.1", "", { "dependencies": { "@octokit/auth-oauth-device": "^8.0.1", "@octokit/auth-oauth-user": "^6.0.0", "@octokit/request": "^10.0.2", "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-TthWzYxuHKLAbmxdFZwFlmwVyvynpyPmjwc+2/cI3cvbT7mHtsAW9b1LvQaNnAuWL+pFnqtxdmrU8QpF633i1g=="], @@ -451,12 +612,20 @@ "@octokit/webhooks-types": ["@octokit/webhooks-types@7.6.1", "", {}, "sha512-S8u2cJzklBC0FgTwWVLaM8tMrDuDMVE4xiTK4EYXM9GntyvrdbSoxqDQa+Fh57CCNApyIpyeqPhhFEmHPfrXgw=="], - "@openauthjs/openauth": ["@openauthjs/openauth@0.4.3", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-RlnjqvHzqcbFVymEwhlUEuac4utA5h4nhSK/i2szZuQmxTIqbGUxZ+nM+avM+VV4Ing+/ZaNLKILoXS3yrkOOw=="], + "@openauthjs/openauth": ["@openauthjs/openauth@0.0.0-20250322224806", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-p5IWSRXvABcwocH2dNI0w8c1QJelIOFulwhKk+aLLFfUbs8u1pr7kQbYe8yCSM2+bcLHiwbogpUQc2ovrGwCuw=="], + + "@openauthjs/solid": ["@openauthjs/solid@0.0.0-20250322224806", "", { "dependencies": { "@openauthjs/openauth": "0.4.2", "@solid-primitives/storage": "^4.3.1" }, "peerDependencies": { "solid-js": "^1.8.0" } }, "sha512-ln/yZQ/i+2vkrVSDOxxmbC8Oq6P2sljtdCiiAPZXj8etihwT11CYtj4jsrwArQltog738N2uvd2dKpHopVqLSQ=="], "@opencode-ai/plugin": ["@opencode-ai/plugin@workspace:packages/plugin"], "@opencode-ai/sdk": ["@opencode-ai/sdk@workspace:packages/sdk/js"], + "@opencode/cloud-core": ["@opencode/cloud-core@workspace:cloud/core"], + + "@opencode/cloud-function": ["@opencode/cloud-function@workspace:cloud/function"], + + "@opencode/cloud-web": ["@opencode/cloud-web@workspace:cloud/web"], + "@opencode/function": ["@opencode/function@workspace:packages/function"], "@opencode/web": ["@opencode/web@workspace:packages/web"], @@ -649,6 +818,10 @@ "@pagefind/windows-x64": ["@pagefind/windows-x64@1.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-BR1bIRWOMqkf8IoU576YDhij1Wd/Zf2kX/kCI0b2qzCKC8wcc2GQJaaRMCpzvCCrmliO4vtJ6RITp/AnoYUUmQ=="], + "@petamoriken/float16": ["@petamoriken/float16@3.9.2", "", {}, "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog=="], + + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + "@poppinss/colors": ["@poppinss/colors@4.1.5", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw=="], "@poppinss/dumper": ["@poppinss/dumper@0.6.4", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ=="], @@ -675,6 +848,8 @@ "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], + "@pulumi/pulumi": ["@pulumi/pulumi@3.188.0", "", { "dependencies": { "@grpc/grpc-js": "^1.10.1", "@logdna/tail-file": "^2.0.6", "@npmcli/arborist": "^7.3.1", "@opentelemetry/api": "^1.9", "@opentelemetry/exporter-zipkin": "^1.28", "@opentelemetry/instrumentation": "^0.55", "@opentelemetry/instrumentation-grpc": "^0.55", "@opentelemetry/resources": "^1.28", "@opentelemetry/sdk-trace-base": "^1.28", "@opentelemetry/sdk-trace-node": "^1.28", "@types/google-protobuf": "^3.15.5", "@types/semver": "^7.5.6", "@types/tmp": "^0.2.6", "execa": "^5.1.0", "fdir": "^6.1.1", "google-protobuf": "^3.5.0", "got": "^11.8.6", "ini": "^2.0.0", "js-yaml": "^3.14.0", "minimist": "^1.2.6", "normalize-package-data": "^6.0.0", "picomatch": "^3.0.1", "pkg-dir": "^7.0.0", "require-from-string": "^2.0.1", "semver": "^7.5.2", "source-map-support": "^0.5.6", "tmp": "^0.2.4", "upath": "^1.1.0" }, "peerDependencies": { "ts-node": ">= 7.0.1 < 12", "typescript": ">= 3.8.3 < 6" }, "optionalPeers": ["ts-node", "typescript"] }, "sha512-c2L2QMq1bNo+1TxuoMQKQM7fiIZOMAAjDaIr2JKyg/ADGi7dhpKckEDbg43EMlzFUtI7t0mkiUYTaVPVcZ1kqg=="], + "@rollup/pluginutils": ["@rollup/pluginutils@5.2.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.46.2", "", { "os": "android", "cpu": "arm" }, "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA=="], @@ -733,30 +908,144 @@ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], - "@sindresorhus/is": ["@sindresorhus/is@7.0.2", "", {}, "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw=="], + "@sigstore/bundle": ["@sigstore/bundle@2.3.2", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.3.2" } }, "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA=="], + + "@sigstore/core": ["@sigstore/core@1.1.0", "", {}, "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg=="], + + "@sigstore/protobuf-specs": ["@sigstore/protobuf-specs@0.3.3", "", {}, "sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ=="], + + "@sigstore/sign": ["@sigstore/sign@2.3.2", "", { "dependencies": { "@sigstore/bundle": "^2.3.2", "@sigstore/core": "^1.0.0", "@sigstore/protobuf-specs": "^0.3.2", "make-fetch-happen": "^13.0.1", "proc-log": "^4.2.0", "promise-retry": "^2.0.1" } }, "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA=="], + + "@sigstore/tuf": ["@sigstore/tuf@2.3.4", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.3.2", "tuf-js": "^2.2.1" } }, "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw=="], + + "@sigstore/verify": ["@sigstore/verify@1.2.1", "", { "dependencies": { "@sigstore/bundle": "^2.3.2", "@sigstore/core": "^1.1.0", "@sigstore/protobuf-specs": "^0.3.2" } }, "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g=="], + + "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], + + "@smithy/abort-controller": ["@smithy/abort-controller@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-jcrqdTQurIrBbUm4W2YdLVMQDoL0sA9DTxYd2s+R/y+2U9NLOP7Xf/YqfSg1FZhlZIYEnvk2mwbyvIfdLEPo8g=="], + + "@smithy/config-resolver": ["@smithy/config-resolver@4.1.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.4", "@smithy/types": "^4.3.2", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw=="], + + "@smithy/core": ["@smithy/core@3.8.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.9", "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.5", "@smithy/util-stream": "^4.2.4", "@smithy/util-utf8": "^4.0.0", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-EYqsIYJmkR1VhVE9pccnk353xhs+lB6btdutJEtsp7R055haMJp2yE16eSxw8fv+G0WUY6vqxyYOP8kOqawxYQ=="], + + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.0.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.4", "@smithy/property-provider": "^4.0.5", "@smithy/types": "^4.3.2", "@smithy/url-parser": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw=="], "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.0.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.3.1", "@smithy/util-hex-encoding": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-7XoWfZqWb/QoR/rAU4VSi0mWnO2vu9/ltS6JZ5ZSZv0eovLVfDfu0/AX4ub33RsJTOth3TiFWSHS5YdztvFnig=="], + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.1.1", "", { "dependencies": { "@smithy/protocol-http": "^5.1.3", "@smithy/querystring-builder": "^4.0.5", "@smithy/types": "^4.3.2", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ=="], + + "@smithy/hash-node": ["@smithy/hash-node@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ=="], + + "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow=="], + "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="], + "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.0.5", "", { "dependencies": { "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ=="], + + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.1.18", "", { "dependencies": { "@smithy/core": "^3.8.0", "@smithy/middleware-serde": "^4.0.9", "@smithy/node-config-provider": "^4.1.4", "@smithy/shared-ini-file-loader": "^4.0.5", "@smithy/types": "^4.3.2", "@smithy/url-parser": "^4.0.5", "@smithy/util-middleware": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-ZhvqcVRPZxnZlokcPaTwb+r+h4yOIOCJmx0v2d1bpVlmP465g3qpVSf7wxcq5zZdu4jb0H4yIMxuPwDJSQc3MQ=="], + + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.1.19", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.4", "@smithy/protocol-http": "^5.1.3", "@smithy/service-error-classification": "^4.0.7", "@smithy/smithy-client": "^4.4.10", "@smithy/types": "^4.3.2", "@smithy/util-middleware": "^4.0.5", "@smithy/util-retry": "^4.0.7", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-X58zx/NVECjeuUB6A8HBu4bhx72EoUz+T5jTMIyeNKx2lf+Gs9TmWPNNkH+5QF0COjpInP/xSpJGJ7xEnAklQQ=="], + + "@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.9", "", { "dependencies": { "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg=="], + + "@smithy/middleware-stack": ["@smithy/middleware-stack@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ=="], + + "@smithy/node-config-provider": ["@smithy/node-config-provider@4.1.4", "", { "dependencies": { "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w=="], + + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.1.1", "", { "dependencies": { "@smithy/abort-controller": "^4.0.5", "@smithy/protocol-http": "^5.1.3", "@smithy/querystring-builder": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw=="], + + "@smithy/property-provider": ["@smithy/property-provider@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ=="], + + "@smithy/protocol-http": ["@smithy/protocol-http@5.1.3", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w=="], + + "@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A=="], + + "@smithy/querystring-parser": ["@smithy/querystring-parser@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w=="], + + "@smithy/service-error-classification": ["@smithy/service-error-classification@4.0.7", "", { "dependencies": { "@smithy/types": "^4.3.2" } }, "sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg=="], + + "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ=="], + + "@smithy/signature-v4": ["@smithy/signature-v4@5.1.3", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-middleware": "^4.0.5", "@smithy/util-uri-escape": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw=="], + + "@smithy/smithy-client": ["@smithy/smithy-client@4.4.10", "", { "dependencies": { "@smithy/core": "^3.8.0", "@smithy/middleware-endpoint": "^4.1.18", "@smithy/middleware-stack": "^4.0.5", "@smithy/protocol-http": "^5.1.3", "@smithy/types": "^4.3.2", "@smithy/util-stream": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-iW6HjXqN0oPtRS0NK/zzZ4zZeGESIFcxj2FkWed3mcK8jdSdHzvnCKXSjvewESKAgGKAbJRA+OsaqKhkdYRbQQ=="], + "@smithy/types": ["@smithy/types@4.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA=="], + "@smithy/url-parser": ["@smithy/url-parser@4.0.5", "", { "dependencies": { "@smithy/querystring-parser": "^4.0.5", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw=="], + + "@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="], + + "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="], + + "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg=="], + "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="], + "@smithy/util-config-provider": ["@smithy/util-config-provider@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w=="], + + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.0.26", "", { "dependencies": { "@smithy/property-provider": "^4.0.5", "@smithy/smithy-client": "^4.4.10", "@smithy/types": "^4.3.2", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-xgl75aHIS/3rrGp7iTxQAOELYeyiwBu+eEgAk4xfKwJJ0L8VUjhO2shsDpeil54BOFsqmk5xfdesiewbUY5tKQ=="], + + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.0.26", "", { "dependencies": { "@smithy/config-resolver": "^4.1.5", "@smithy/credential-provider-imds": "^4.0.7", "@smithy/node-config-provider": "^4.1.4", "@smithy/property-provider": "^4.0.5", "@smithy/smithy-client": "^4.4.10", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-z81yyIkGiLLYVDetKTUeCZQ8x20EEzvQjrqJtb/mXnevLq2+w3XCEWTJ2pMp401b6BkEkHVfXb/cROBpVauLMQ=="], + + "@smithy/util-endpoints": ["@smithy/util-endpoints@3.0.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.4", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ=="], + "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw=="], + "@smithy/util-middleware": ["@smithy/util-middleware@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ=="], + + "@smithy/util-retry": ["@smithy/util-retry@4.0.7", "", { "dependencies": { "@smithy/service-error-classification": "^4.0.7", "@smithy/types": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ=="], + + "@smithy/util-stream": ["@smithy/util-stream@4.2.4", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.1.1", "@smithy/node-http-handler": "^4.1.1", "@smithy/types": "^4.3.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ=="], + + "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg=="], + "@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="], + "@solid-primitives/event-listener": ["@solid-primitives/event-listener@2.4.3", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-h4VqkYFv6Gf+L7SQj+Y6puigL/5DIi7x5q07VZET7AWcS+9/G3WfIE9WheniHWJs51OEkRB43w6lDys5YeFceg=="], + + "@solid-primitives/keyed": ["@solid-primitives/keyed@1.5.2", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-BgoEdqPw48URnI+L5sZIHdF4ua4Las1eWEBBPaoSFs42kkhnHue+rwCBPL2Z9ebOyQ75sUhUfOETdJfmv0D6Kg=="], + + "@solid-primitives/map": ["@solid-primitives/map@0.4.13", "", { "dependencies": { "@solid-primitives/trigger": "^1.1.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-B1zyFbsiTQvqPr+cuPCXO72sRuczG9Swncqk5P74NCGw1VE8qa/Ry9GlfI1e/VdeQYHjan+XkbE3rO2GW/qKew=="], + + "@solid-primitives/media": ["@solid-primitives/media@2.3.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-hQ4hLOGvfbugQi5Eu1BFWAIJGIAzztq9x0h02xgBGl2l0Jaa3h7tg6bz5tV1NSuNYVGio4rPoa7zVQQLkkx9dA=="], + + "@solid-primitives/props": ["@solid-primitives/props@3.2.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-lZOTwFJajBrshSyg14nBMEP0h8MXzPowGO0s3OeiR3z6nXHTfj0FhzDtJMv+VYoRJKQHG2QRnJTgCzK6erARAw=="], + + "@solid-primitives/refs": ["@solid-primitives/refs@1.1.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-K7tf2thy7L+YJjdqXspXOg5xvNEOH8tgEWsp0+1mQk3obHBRD6hEjYZk7p7FlJphSZImS35je3UfmWuD7MhDfg=="], + + "@solid-primitives/resize-observer": ["@solid-primitives/resize-observer@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ=="], + + "@solid-primitives/rootless": ["@solid-primitives/rootless@1.5.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-9HULb0QAzL2r47CCad0M+NKFtQ+LrGGNHZfteX/ThdGvKIg2o2GYhBooZubTCd/RTu2l2+Nw4s+dEfiDGvdrrQ=="], + + "@solid-primitives/static-store": ["@solid-primitives/static-store@0.1.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-ReK+5O38lJ7fT+L6mUFvUr6igFwHBESZF+2Ug842s7fvlVeBdIVEdTCErygff6w7uR6+jrr7J8jQo+cYrEq4Iw=="], + + "@solid-primitives/storage": ["@solid-primitives/storage@4.3.1", "", { "dependencies": { "@solid-primitives/utils": "^6.3.0" }, "peerDependencies": { "@tauri-apps/plugin-store": "*", "solid-js": "^1.6.12" }, "optionalPeers": ["@tauri-apps/plugin-store"] }, "sha512-xAJsY2pvXrAaCai4N2grmWY3xh5om9suTDVzGkRF5JBpDzs3Apk+xIovdTErbW0iCzXIEefENXb9xmSzdjuLYA=="], + + "@solid-primitives/trigger": ["@solid-primitives/trigger@1.2.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-IWoptVc0SWYgmpBPpCMehS5b07+tpFcvw15tOQ3QbXedSYn6KP8zCjPkHNzMxcOvOicTneleeZDP7lqmz+PQ6g=="], + + "@solid-primitives/utils": ["@solid-primitives/utils@6.3.2", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ=="], + + "@solidjs/meta": ["@solidjs/meta@0.29.4", "", { "peerDependencies": { "solid-js": ">=1.8.4" } }, "sha512-zdIWBGpR9zGx1p1bzIPqF5Gs+Ks/BH8R6fWhmUa/dcK1L2rUC8BAcZJzNRYBQv74kScf1TSOs0EY//Vd/I0V8g=="], + + "@solidjs/router": ["@solidjs/router@0.15.3", "", { "peerDependencies": { "solid-js": "^1.8.6" } }, "sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw=="], + "@speed-highlight/core": ["@speed-highlight/core@1.2.7", "", {}, "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g=="], "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], "@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="], + "@szmarczak/http-timer": ["@szmarczak/http-timer@4.0.6", "", { "dependencies": { "defer-to-connect": "^2.0.0" } }, "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w=="], + "@tsconfig/bun": ["@tsconfig/bun@1.0.7", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="], "@tsconfig/node22": ["@tsconfig/node22@22.0.2", "", {}, "sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA=="], + "@tufjs/canonical-json": ["@tufjs/canonical-json@2.0.0", "", {}, "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA=="], + + "@tufjs/models": ["@tufjs/models@2.0.1", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^9.0.4" } }, "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg=="], + "@types/aws-lambda": ["@types/aws-lambda@8.10.150", "", {}, "sha512-AX+AbjH/rH5ezX1fbK8onC/a+HyQHo7QGmvoxAE42n22OsciAxvZoZNEr22tbXs8WfP1nIsBjKDpgPm3HjOZbA=="], "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], @@ -771,6 +1060,8 @@ "@types/bunyan": ["@types/bunyan@1.8.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ=="], + "@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="], + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], @@ -781,12 +1072,18 @@ "@types/fontkit": ["@types/fontkit@2.0.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew=="], + "@types/google-protobuf": ["@types/google-protobuf@3.15.12", "", {}, "sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ=="], + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + "@types/http-cache-semantics": ["@types/http-cache-semantics@4.0.4", "", {}, "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA=="], + "@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="], "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + "@types/keyv": ["@types/keyv@3.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg=="], + "@types/luxon": ["@types/luxon@3.6.2", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="], "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], @@ -811,14 +1108,24 @@ "@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="], + "@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="], + "@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="], + "@types/semver": ["@types/semver@7.7.0", "", {}, "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA=="], + + "@types/shimmer": ["@types/shimmer@1.2.0", "", {}, "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg=="], + "@types/tedious": ["@types/tedious@4.0.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw=="], + "@types/tmp": ["@types/tmp@0.2.6", "", {}, "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA=="], + "@types/turndown": ["@types/turndown@5.0.5", "", {}, "sha512-TL2IgGgc7B5j78rIccBtlYAnkuv8nUQqhQc+DSYV5j9Be9XOcm/SKOVRuA47xAVI3680Tk9B1d8flK2GWT2+4w=="], "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "@types/uuid": ["@types/uuid@9.0.8", "", {}, "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="], + "@types/yargs": ["@types/yargs@17.0.33", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA=="], "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], @@ -827,6 +1134,8 @@ "@zip.js/zip.js": ["@zip.js/zip.js@2.7.62", "", {}, "sha512-OaLvZ8j4gCkLn048ypkZu29KX30r8/OfFF2w4Jo5WXFr+J04J+lzJ5TKZBVgFXhlvSkqNFQdfnY1Q8TMTCyBVA=="], + "abbrev": ["abbrev@2.0.0", "", {}, "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ=="], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], @@ -839,6 +1148,8 @@ "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], + "ai": ["ai@5.0.0-beta.34", "", { "dependencies": { "@ai-sdk/gateway": "1.0.0-beta.19", "@ai-sdk/provider": "2.0.0-beta.2", "@ai-sdk/provider-utils": "3.0.0-beta.10", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-AFJ4p35AxA+1KFtnoouePLaAUpoj0IxIAoq/xgIv88qzYajTg4Sac5KaV4CDHFRLoF0L2cwhlFXt/Ss/zyBKkA=="], "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], @@ -889,6 +1200,8 @@ "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "bare-events": ["bare-events@2.6.0", "", {}, "sha512-EKZ5BTXYExaNqi3I3f9RtEsaI/xBSGjE0XZCZilPzFAV/goswFHuPd9jEZlPIZ/iNZJwDSao9qRiScySz7MbQg=="], "bare-fs": ["bare-fs@4.1.6", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-25RsLF33BqooOEFNdMcEhMpJy8EoR88zSMrnOQOaM3USnOK2VmaJ1uaQEwPA6AQjrv1lXChScosN6CzbwbO9OQ=="], @@ -911,6 +1224,8 @@ "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], + "bin-links": ["bin-links@4.0.4", "", { "dependencies": { "cmd-shim": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", "read-cmd-shim": "^4.0.0", "write-file-atomic": "^5.0.0" } }, "sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA=="], + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], @@ -921,14 +1236,22 @@ "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], + "bowser": ["bowser@2.11.0", "", {}, "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="], + "boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="], + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + "brotli": ["brotli@1.3.3", "", { "dependencies": { "base64-js": "^1.1.2" } }, "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg=="], "browserslist": ["browserslist@4.25.1", "", { "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw=="], "buffer": ["buffer@4.9.2", "", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" } }, "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg=="], + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + "bufrw": ["bufrw@1.4.0", "", { "dependencies": { "ansi-color": "^0.2.1", "error": "^7.0.0", "hexer": "^1.5.0", "xtend": "^4.0.0" } }, "sha512-sWm8iPbqvL9+5SiYxXH73UOkyEbGQg7kyHQmReF89WJHQJw2eV4P/yZ0E+b71cczJ4pPobVhXxgQcmfSTgGHxQ=="], "bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="], @@ -939,6 +1262,12 @@ "c12": ["c12@2.0.1", "", { "dependencies": { "chokidar": "^4.0.1", "confbox": "^0.1.7", "defu": "^6.1.4", "dotenv": "^16.4.5", "giget": "^1.2.3", "jiti": "^2.3.0", "mlly": "^1.7.1", "ohash": "^1.1.4", "pathe": "^1.1.2", "perfect-debounce": "^1.0.0", "pkg-types": "^1.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A=="], + "cacache": ["cacache@18.0.4", "", { "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", "lru-cache": "^10.0.1", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^4.0.0", "ssri": "^10.0.0", "tar": "^6.1.11", "unique-filename": "^3.0.0" } }, "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ=="], + + "cacheable-lookup": ["cacheable-lookup@5.0.4", "", {}, "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="], + + "cacheable-request": ["cacheable-request@7.0.4", "", { "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", "keyv": "^4.0.0", "lowercase-keys": "^2.0.0", "normalize-url": "^6.0.1", "responselike": "^2.0.0" } }, "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg=="], + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], @@ -973,14 +1302,20 @@ "clean-git-ref": ["clean-git-ref@2.0.1", "", {}, "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw=="], + "clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="], + "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], "cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="], "clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="], + "clone-response": ["clone-response@1.0.3", "", { "dependencies": { "mimic-response": "^1.0.0" } }, "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA=="], + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "cmd-shim": ["cmd-shim@6.0.3", "", {}, "sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA=="], + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], @@ -1047,6 +1382,8 @@ "default-browser-id": ["default-browser-id@5.0.0", "", {}, "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA=="], + "defer-to-connect": ["defer-to-connect@2.0.1", "", {}, "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="], + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], @@ -1081,10 +1418,16 @@ "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + "drizzle-kit": ["drizzle-kit@0.30.5", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.19.7", "esbuild-register": "^3.5.0", "gel": "^2.0.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-l6dMSE100u7sDaTbLczibrQZjA35jLsHNqIV+jmhNVO3O8jzM6kywMOmV9uOz9ZVSCMPQhAZEFjL/qDPVrqpUA=="], + + "drizzle-orm": ["drizzle-orm@0.41.0", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-7A4ZxhHk9gdlXmTdPj/lREtP+3u8KvZ4yEN6MYVxBzZGex5Wtdc+CWSbu7btgF6TB0N+MNPrvW7RKBbxJchs/Q=="], + "dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="], "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], "electron-to-chromium": ["electron-to-chromium@1.5.193", "", {}, "sha512-eePuBZXM9OVCwfYUhd2OzESeNGnWmLyeu0XAEjf7xjijNjHFdeJSzuRUGN4ueT2tEYo5YqjHramKEFxz67p3XA=="], @@ -1093,10 +1436,16 @@ "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + "env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="], + + "err-code": ["err-code@2.0.3", "", {}, "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="], + "error": ["error@7.0.2", "", { "dependencies": { "string-template": "~0.2.1", "xtend": "~4.0.0" } }, "sha512-UtVv4l5MhijsYUxPJo4390gzfZvAnTHreNnDjnTZaKIiZ/SemXxAhBkYSKtWa5RtBXbLP8tMgn/n0RUa/H7jXw=="], "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="], @@ -1113,7 +1462,9 @@ "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], - "esbuild": ["esbuild@0.25.8", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], + "esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="], + + "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -1123,6 +1474,8 @@ "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + "esprima-extract-comments": ["esprima-extract-comments@1.1.0", "", { "dependencies": { "esprima": "^4.0.0" } }, "sha512-sBQUnvJwpeE9QnPrxh7dpI/dp67erYG4WXEAreAMoelPRpMR7NWb4YtwRPn9b+H1uLQKl/qS8WYmyaljTpjIsw=="], + "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="], "estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="], @@ -1147,10 +1500,14 @@ "eventsource-parser": ["eventsource-parser@3.0.3", "", {}, "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA=="], + "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="], "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], + "exponential-backoff": ["exponential-backoff@3.1.2", "", {}, "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA=="], + "express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], "express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="], @@ -1163,18 +1520,30 @@ "extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + "extract-comments": ["extract-comments@1.1.0", "", { "dependencies": { "esprima-extract-comments": "^1.1.0", "parse-code-context": "^1.0.0" } }, "sha512-dzbZV2AdSSVW/4E7Ti5hZdHWbA+Z80RJsJhr5uiL10oyjl/gy7/o+HI1HwK4/WSZhlq4SNKU3oUzXlM13Qx02Q=="], + "fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="], + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + "fast-xml-parser": ["fast-xml-parser@4.4.1", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + "fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="], + "find-up": ["find-up@6.3.0", "", { "dependencies": { "locate-path": "^7.1.0", "path-exists": "^5.0.0" } }, "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw=="], + "flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="], "fontace": ["fontace@0.3.0", "", { "dependencies": { "@types/fontkit": "^2.0.8", "fontkit": "^2.0.4" } }, "sha512-czoqATrcnxgWb/nAkfyIrRp6Q8biYj7nGnL6zfhTcX+JKKpWHFBnb8uNMw/kZr7u++3Y3wYSYoZgHkCcsuBpBg=="], @@ -1183,6 +1552,8 @@ "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], "forwarded-parse": ["forwarded-parse@2.1.2", "", {}, "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw=="], @@ -1191,7 +1562,7 @@ "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], - "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="], + "fs-minipass": ["fs-minipass@3.0.3", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -1201,6 +1572,8 @@ "gcp-metadata": ["gcp-metadata@6.1.1", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="], + "gel": ["gel@2.1.1", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-Newg9X7mRYskoBjSw70l1YnJ/ZGbq64VPyR821H5WVkTGpHG2O0mQILxCeUhxdYERLFY9B4tUyKLyf3uMTjtKw=="], + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], @@ -1211,18 +1584,32 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + "get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], + "giget": ["giget@1.2.5", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.5.4", "pathe": "^2.0.3", "tar": "^6.2.1" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug=="], "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], + "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], + + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], "google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="], + "google-protobuf": ["google-protobuf@3.21.4", "", {}, "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ=="], + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + "got": ["got@11.8.6", "", { "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", "@types/cacheable-request": "^6.0.1", "@types/responselike": "^1.0.0", "cacheable-lookup": "^5.0.3", "cacheable-request": "^7.0.2", "decompress-response": "^6.0.0", "http2-wrapper": "^1.0.0-beta.5.2", "lowercase-keys": "^2.0.0", "p-cancelable": "^2.0.0", "responselike": "^2.0.0" } }, "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="], "h3": ["h3@1.15.4", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.2", "radix3": "^1.1.2", "ufo": "^1.6.1", "uncrypto": "^0.1.3" } }, "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ=="], @@ -1285,6 +1672,8 @@ "hono-openapi": ["hono-openapi@0.4.8", "", { "dependencies": { "json-schema-walker": "^2.0.0" }, "peerDependencies": { "@hono/arktype-validator": "^2.0.0", "@hono/effect-validator": "^1.2.0", "@hono/typebox-validator": "^0.2.0 || ^0.3.0", "@hono/valibot-validator": "^0.5.1", "@hono/zod-validator": "^0.4.1", "@sinclair/typebox": "^0.34.9", "@valibot/to-json-schema": "^1.0.0-beta.3", "arktype": "^2.0.0", "effect": "^3.11.3", "hono": "^4.6.13", "openapi-types": "^12.1.3", "valibot": "^1.0.0-beta.9", "zod": "^3.23.8", "zod-openapi": "^4.0.0" }, "optionalPeers": ["@hono/arktype-validator", "@hono/effect-validator", "@hono/typebox-validator", "@hono/valibot-validator", "@hono/zod-validator", "@sinclair/typebox", "@valibot/to-json-schema", "arktype", "effect", "hono", "valibot", "zod", "zod-openapi"] }, "sha512-LYr5xdtD49M7hEAduV1PftOMzuT8ZNvkyWfh1DThkLsIr4RkvDb12UxgIiFbwrJB6FLtFXLoOZL9x4IeDk2+VA=="], + "hosted-git-info": ["hosted-git-info@7.0.2", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w=="], + "html-entities": ["html-entities@2.3.3", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="], "html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="], @@ -1297,8 +1686,14 @@ "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "http2-wrapper": ["http2-wrapper@1.0.3", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" } }, "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg=="], + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + "i18next": ["i18next@23.16.8", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg=="], "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], @@ -1307,16 +1702,24 @@ "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + "ignore-walk": ["ignore-walk@6.0.5", "", { "dependencies": { "minimatch": "^9.0.0" } }, "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A=="], + "import-in-the-middle": ["import-in-the-middle@1.14.2", "", { "dependencies": { "acorn": "^8.14.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^1.2.2", "module-details-from-path": "^1.0.3" } }, "sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw=="], "import-meta-resolve": ["import-meta-resolve@4.1.0", "", {}, "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw=="], + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "ini": ["ini@2.0.0", "", {}, "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA=="], "inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="], + "ip-address": ["ip-address@9.0.5", "", { "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" } }, "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g=="], + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], @@ -1339,14 +1742,22 @@ "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], "is-generator-function": ["is-generator-function@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ=="], + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], + "is-lambda": ["is-lambda@1.0.1", "", {}, "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], @@ -1363,10 +1774,12 @@ "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], "isomorphic-git": ["isomorphic-git@1.32.1", "", { "dependencies": { "async-lock": "^1.4.1", "clean-git-ref": "^2.0.1", "crc-32": "^1.2.0", "diff3": "0.0.3", "ignore": "^5.1.4", "minimisted": "^2.0.0", "pako": "^1.0.10", "path-browserify": "^1.0.1", "pify": "^4.0.1", "readable-stream": "^3.4.0", "sha.js": "^2.4.9", "simple-get": "^4.0.1" }, "bin": { "isogit": "cli.cjs" } }, "sha512-NZCS7qpLkCZ1M/IrujYBD31sM6pd/fMVArK4fz4I7h6m0rUW2AsYU7S7zXeABuHL6HIfW6l53b4UQ/K441CQjg=="], + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + "jaeger-client": ["jaeger-client@3.19.0", "", { "dependencies": { "node-int64": "^0.4.0", "opentracing": "^0.14.4", "thriftrw": "^3.5.0", "uuid": "^8.3.2", "xorshift": "^1.1.1" } }, "sha512-M0c7cKHmdyEUtjemnJyx/y9uX16XHocL46yQvyqDlPdvAcwPDbHrIbKjQdBqtiE4apQ/9dmr+ZLJYYPGnurgpw=="], "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="], @@ -1381,20 +1794,36 @@ "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + "jsbn": ["jsbn@1.1.0", "", {}, "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="], + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@3.0.2", "", {}, "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ=="], + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "json-schema-walker": ["json-schema-walker@2.0.0", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.1.0", "clone": "^2.1.2" } }, "sha512-nXN2cMky0Iw7Af28w061hmxaPDaML5/bQD9nwm1lOoIKEGjHcRGxqWe4MfrkYThYAPjSUhmsp4bJNoLAyVn9Xw=="], + "json-stringify-nice": ["json-stringify-nice@1.1.4", "", {}, "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw=="], + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], + + "just-diff": ["just-diff@6.0.2", "", {}, "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA=="], + + "just-diff-apply": ["just-diff-apply@5.5.0", "", {}, "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], @@ -1405,6 +1834,10 @@ "language-map": ["language-map@1.5.0", "", {}, "sha512-n7gFZpe+DwEAX9cXVTw43i3wiudWDDtSn28RmdnS/HCPr284dQI/SztsamWanRr75oSlKSaGbV2nmWCTzGCoVg=="], + "local-pkg": ["local-pkg@1.1.1", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.0.1", "quansync": "^0.2.8" } }, "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg=="], + + "locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="], + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], @@ -1415,6 +1848,8 @@ "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + "lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="], + "lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], "luxon": ["luxon@3.6.1", "", {}, "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ=="], @@ -1423,6 +1858,8 @@ "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], + "make-fetch-happen": ["make-fetch-happen@13.0.1", "", { "dependencies": { "@npmcli/agent": "^2.0.0", "cacache": "^18.0.0", "http-cache-semantics": "^4.1.1", "is-lambda": "^1.0.1", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "proc-log": "^4.2.0", "promise-retry": "^2.0.1", "ssri": "^10.0.0" } }, "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA=="], + "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], @@ -1477,6 +1914,10 @@ "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], @@ -1549,12 +1990,16 @@ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], "mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], "miniflare": ["miniflare@4.20250730.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^7.10.0", "workerd": "1.20250730.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-avGXBStHQSqcJr8ra1mJ3/OQvnLZ49B1uAILQapAha1DHNZZvXWLIgUVre/WGY6ZOlNGFPh5CJ+dXLm4yuV3Jw=="], @@ -1565,7 +2010,17 @@ "minimisted": ["minimisted@2.0.1", "", { "dependencies": { "minimist": "^1.2.5" } }, "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA=="], - "minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="], + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "minipass-collect": ["minipass-collect@2.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw=="], + + "minipass-fetch": ["minipass-fetch@3.0.5", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg=="], + + "minipass-flush": ["minipass-flush@1.0.5", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw=="], + + "minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="], + + "minipass-sized": ["minipass-sized@1.0.3", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g=="], "minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="], @@ -1601,6 +2056,8 @@ "node-fetch-native": ["node-fetch-native@1.6.6", "", {}, "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ=="], + "node-gyp": ["node-gyp@10.3.1", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "glob": "^10.3.10", "graceful-fs": "^4.2.6", "make-fetch-happen": "^13.0.0", "nopt": "^7.0.0", "proc-log": "^4.1.0", "semver": "^7.3.5", "tar": "^6.2.1", "which": "^4.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ=="], + "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], @@ -1609,8 +2066,30 @@ "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], + "nopt": ["nopt@7.2.1", "", { "dependencies": { "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w=="], + + "normalize-package-data": ["normalize-package-data@6.0.2", "", { "dependencies": { "hosted-git-info": "^7.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g=="], + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + "normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="], + + "npm-bundled": ["npm-bundled@3.0.1", "", { "dependencies": { "npm-normalize-package-bin": "^3.0.0" } }, "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ=="], + + "npm-install-checks": ["npm-install-checks@6.3.0", "", { "dependencies": { "semver": "^7.1.1" } }, "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw=="], + + "npm-normalize-package-bin": ["npm-normalize-package-bin@3.0.1", "", {}, "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ=="], + + "npm-package-arg": ["npm-package-arg@11.0.3", "", { "dependencies": { "hosted-git-info": "^7.0.0", "proc-log": "^4.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" } }, "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw=="], + + "npm-packlist": ["npm-packlist@8.0.2", "", { "dependencies": { "ignore-walk": "^6.0.4" } }, "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA=="], + + "npm-pick-manifest": ["npm-pick-manifest@9.1.0", "", { "dependencies": { "npm-install-checks": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", "npm-package-arg": "^11.0.0", "semver": "^7.3.5" } }, "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA=="], + + "npm-registry-fetch": ["npm-registry-fetch@17.1.0", "", { "dependencies": { "@npmcli/redact": "^2.0.0", "jsonparse": "^1.3.1", "make-fetch-happen": "^13.0.0", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minizlib": "^2.1.2", "npm-package-arg": "^11.0.0", "proc-log": "^4.0.0" } }, "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA=="], + + "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], "nypm": ["nypm@0.5.4", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "tinyexec": "^0.3.2", "ufo": "^1.5.4" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA=="], @@ -1631,6 +2110,8 @@ "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], "oniguruma-to-es": ["oniguruma-to-es@4.3.3", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg=="], @@ -1649,18 +2130,32 @@ "opentracing": ["opentracing@0.14.7", "", {}, "sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q=="], + "p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="], + "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], + "p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="], + + "p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="], + "p-queue": ["p-queue@8.1.0", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw=="], "p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + "package-manager-detector": ["package-manager-detector@1.3.0", "", {}, "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ=="], + "pacote": ["pacote@18.0.6", "", { "dependencies": { "@npmcli/git": "^5.0.0", "@npmcli/installed-package-contents": "^2.0.1", "@npmcli/package-json": "^5.1.0", "@npmcli/promise-spawn": "^7.0.0", "@npmcli/run-script": "^8.0.0", "cacache": "^18.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^11.0.0", "npm-packlist": "^8.0.0", "npm-pick-manifest": "^9.0.0", "npm-registry-fetch": "^17.0.0", "proc-log": "^4.0.0", "promise-retry": "^2.0.1", "sigstore": "^2.2.0", "ssri": "^10.0.0", "tar": "^6.1.11" }, "bin": { "pacote": "bin/index.js" } }, "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A=="], + "pagefind": ["pagefind@1.3.0", "", { "optionalDependencies": { "@pagefind/darwin-arm64": "1.3.0", "@pagefind/darwin-x64": "1.3.0", "@pagefind/linux-arm64": "1.3.0", "@pagefind/linux-x64": "1.3.0", "@pagefind/windows-x64": "1.3.0" }, "bin": { "pagefind": "lib/runner/bin.cjs" } }, "sha512-8KPLGT5g9s+olKMRTU9LFekLizkVIu9tes90O1/aigJ0T5LmyPqTzGJrETnSw3meSYg58YH7JTzhTTW/3z6VAw=="], "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], + "parse-code-context": ["parse-code-context@1.0.0", "", {}, "sha512-OZQaqKaQnR21iqhlnPfVisFjBWjhnMl5J9MgbP8xC+EwoVqbXrq78lp+9Zb3ahmLzrIX5Us/qbvBnaS3hkH6OA=="], + + "parse-conflict-json": ["parse-conflict-json@3.0.1", "", { "dependencies": { "json-parse-even-better-errors": "^3.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" } }, "sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw=="], + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], "parse-latin": ["parse-latin@7.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="], @@ -1671,10 +2166,14 @@ "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + "path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="], + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], @@ -1695,6 +2194,8 @@ "pkce-challenge": ["pkce-challenge@5.0.0", "", {}, "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ=="], + "pkg-dir": ["pkg-dir@7.0.0", "", { "dependencies": { "find-up": "^6.3.0" } }, "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA=="], + "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], @@ -1719,8 +2220,20 @@ "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], + "proc-log": ["proc-log@4.2.0", "", {}, "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA=="], + "process": ["process@0.10.1", "", {}, "sha512-dyIett8dgGIZ/TXKUzeYExt7WA6ldDzys9vTDU/cCA9L17Ypme+KzS+NjQCjpn9xsvi/shbMC+yP/BcFMBz0NA=="], + "proggy": ["proggy@2.0.0", "", {}, "sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A=="], + + "promise-all-reject-late": ["promise-all-reject-late@1.0.1", "", {}, "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw=="], + + "promise-call-limit": ["promise-call-limit@3.0.2", "", {}, "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw=="], + + "promise-inflight": ["promise-inflight@1.0.1", "", {}, "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g=="], + + "promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="], + "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], @@ -1729,14 +2242,22 @@ "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + "pulumi-stripe": ["pulumi-stripe@0.0.24", "", { "dependencies": { "@pulumi/pulumi": "^3.0.0" } }, "sha512-ec2Gn9IPszVZ0n9IOo5Mroik6pHM1cyJmBxWoN8XG7gFXBeJPme9tdWXy1rmWDIDyhjN5R+AKqL75X6Nar+BVQ=="], + "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], "punycode": ["punycode@1.3.2", "", {}, "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="], "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + "quansync": ["quansync@0.2.10", "", {}, "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A=="], + "querystring": ["querystring@0.2.0", "", {}, "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="], + "radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="], "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], @@ -1747,6 +2268,10 @@ "rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="], + "read-cmd-shim": ["read-cmd-shim@4.0.0", "", {}, "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q=="], + + "read-package-json-fast": ["read-package-json-fast@3.0.2", "", { "dependencies": { "json-parse-even-better-errors": "^3.0.0", "npm-normalize-package-bin": "^3.0.0" } }, "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw=="], + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], @@ -1799,10 +2324,18 @@ "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + "require-in-the-middle": ["require-in-the-middle@7.5.2", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3", "resolve": "^1.22.8" } }, "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ=="], "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + "resolve-alpn": ["resolve-alpn@1.2.1", "", {}, "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "responselike": ["responselike@2.0.1", "", { "dependencies": { "lowercase-keys": "^2.0.0" } }, "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw=="], + "restructure": ["restructure@3.0.2", "", {}, "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw=="], "retext": ["retext@9.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "retext-latin": "^4.0.0", "retext-stringify": "^4.0.0", "unified": "^11.0.0" } }, "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA=="], @@ -1813,12 +2346,18 @@ "retext-stringify": ["retext-stringify@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unified": "^11.0.0" } }, "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA=="], + "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + "rollup": ["rollup@4.46.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.46.2", "@rollup/rollup-android-arm64": "4.46.2", "@rollup/rollup-darwin-arm64": "4.46.2", "@rollup/rollup-darwin-x64": "4.46.2", "@rollup/rollup-freebsd-arm64": "4.46.2", "@rollup/rollup-freebsd-x64": "4.46.2", "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", "@rollup/rollup-linux-arm-musleabihf": "4.46.2", "@rollup/rollup-linux-arm64-gnu": "4.46.2", "@rollup/rollup-linux-arm64-musl": "4.46.2", "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", "@rollup/rollup-linux-ppc64-gnu": "4.46.2", "@rollup/rollup-linux-riscv64-gnu": "4.46.2", "@rollup/rollup-linux-riscv64-musl": "4.46.2", "@rollup/rollup-linux-s390x-gnu": "4.46.2", "@rollup/rollup-linux-x64-gnu": "4.46.2", "@rollup/rollup-linux-x64-musl": "4.46.2", "@rollup/rollup-win32-arm64-msvc": "4.46.2", "@rollup/rollup-win32-ia32-msvc": "4.46.2", "@rollup/rollup-win32-x64-msvc": "4.46.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg=="], "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], "run-applescript": ["run-applescript@7.0.0", "", {}, "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A=="], + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], @@ -1853,8 +2392,12 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], + "shiki": ["shiki@3.4.2", "", { "dependencies": { "@shikijs/core": "3.4.2", "@shikijs/engine-javascript": "3.4.2", "@shikijs/engine-oniguruma": "3.4.2", "@shikijs/langs": "3.4.2", "@shikijs/themes": "3.4.2", "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-wuxzZzQG8kvZndD7nustrNFIKYJ1jJoWIPaBpVe2+KHSvtzMi4SBjOxrigs8qeqce/l3U0cwiC+VAkLKSunHQQ=="], + "shimmer": ["shimmer@1.2.1", "", {}, "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="], + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], @@ -1863,6 +2406,10 @@ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "sigstore": ["sigstore@2.3.1", "", { "dependencies": { "@sigstore/bundle": "^2.3.2", "@sigstore/core": "^1.0.0", "@sigstore/protobuf-specs": "^0.3.2", "@sigstore/sign": "^2.3.2", "@sigstore/tuf": "^2.3.4", "@sigstore/verify": "^1.2.1" } }, "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ=="], + "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], @@ -1873,9 +2420,21 @@ "sitemap": ["sitemap@8.0.0", "", { "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.2.4" }, "bin": { "sitemap": "dist/cli.js" } }, "sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A=="], + "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], + "smol-toml": ["smol-toml@1.4.1", "", {}, "sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg=="], - "solid-js": ["solid-js@1.9.7", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw=="], + "socks": ["socks@2.8.6", "", { "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" } }, "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA=="], + + "socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="], + + "solid-js": ["solid-js@1.9.5", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "^1.1.0", "seroval-plugins": "^1.1.0" } }, "sha512-ogI3DaFcyn6UhYhrgcyRAMbu/buBJitYQASZz5WzfQVPP10RD2AbCoRZ517psnezrasyCbWzIxZ6kVqet768xw=="], + + "solid-list": ["solid-list@0.3.0", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-t4hx/F/l8Vmq+ib9HtZYl7Z9F1eKxq3eKJTXlvcm7P7yI4Z8O7QSOOEVHb/K6DD7M0RxzVRobK/BS5aSfLRwKg=="], + + "solid-presence": ["solid-presence@0.1.8", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-pWGtXUFWYYUZNbg5YpG5vkQJyOtzn2KXhxYaMx/4I+lylTLYkITOLevaCwMRN+liCVk0pqB6EayLWojNqBFECA=="], + + "solid-prevent-scroll": ["solid-prevent-scroll@0.1.10", "", { "dependencies": { "@corvu/utils": "~0.4.1" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-KplGPX2GHiWJLZ6AXYRql4M127PdYzfwvLJJXMkO+CMb8Np4VxqDAg5S8jLdwlEuBis/ia9DKw2M8dFx5u8Mhw=="], "solid-refresh": ["solid-refresh@0.6.3", "", { "dependencies": { "@babel/generator": "^7.23.6", "@babel/helper-module-imports": "^7.22.15", "@babel/types": "^7.23.6" }, "peerDependencies": { "solid-js": "^1.3" } }, "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA=="], @@ -1883,10 +2442,22 @@ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + "spdx-correct": ["spdx-correct@3.2.0", "", { "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="], + + "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="], + + "spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], + + "spdx-license-ids": ["spdx-license-ids@3.0.22", "", {}, "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ=="], + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + "ssri": ["ssri@10.0.6", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ=="], + "sst": ["sst@3.17.8", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.17.8", "sst-darwin-x64": "3.17.8", "sst-linux-arm64": "3.17.8", "sst-linux-x64": "3.17.8", "sst-linux-x86": "3.17.8", "sst-win32-arm64": "3.17.8", "sst-win32-x64": "3.17.8", "sst-win32-x86": "3.17.8" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-P/a9/ZsjtQRrTBerBMO1ODaVa5HVTmNLrQNJiYvu2Bgd0ov+vefQeHv6oima8HLlPwpDIPS2gxJk8BZrTZMfCA=="], "sst-darwin-arm64": ["sst-darwin-arm64@3.17.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-50P6YRMnZVItZUfB0+NzqMww2mmm4vB3zhTVtWUtGoXeiw78g1AEnVlmS28gYXPHM1P987jTvR7EON9u9ig/Dg=="], @@ -1917,16 +2488,26 @@ "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], "strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="], + "strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + "stripe": ["stripe@18.0.0", "", { "dependencies": { "@types/node": ">=8.1.0", "qs": "^6.11.0" } }, "sha512-3Fs33IzKUby//9kCkCa1uRpinAoTvj6rJgQ2jrBEysoxEvfsclvXdna1amyEYbA2EKkjynuB4+L/kleCCaWTpA=="], + + "strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="], + "style-to-js": ["style-to-js@1.1.17", "", { "dependencies": { "style-to-object": "1.0.9" } }, "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA=="], "style-to-object": ["style-to-object@1.0.9", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw=="], @@ -1951,8 +2532,12 @@ "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], + "tmp": ["tmp@0.2.4", "", {}, "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ=="], + "to-buffer": ["to-buffer@1.2.1", "", { "dependencies": { "isarray": "^2.0.5", "safe-buffer": "^5.2.1", "typed-array-buffer": "^1.0.3" } }, "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "toad-cache": ["toad-cache@3.7.0", "", {}, "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw=="], "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], @@ -1965,6 +2550,8 @@ "tree-sitter-bash": ["tree-sitter-bash@0.23.3", "", { "dependencies": { "node-addon-api": "^8.2.1", "node-gyp-build": "^4.8.2" }, "peerDependencies": { "tree-sitter": "^0.21.1" }, "optionalPeers": ["tree-sitter"] }, "sha512-36cg/GQ2YmIbeiBeqeuh4fBJ6i4kgVouDaqTxqih5ysPag+zHufyIaxMOFeM8CeplwAK/Luj1o5XHqgdAfoCZg=="], + "treeverse": ["treeverse@3.0.0", "", {}, "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ=="], + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], @@ -1973,6 +2560,8 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "tuf-js": ["tuf-js@2.2.1", "", { "dependencies": { "@tufjs/models": "2.0.1", "debug": "^4.3.4", "make-fetch-happen": "^13.0.1" } }, "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA=="], + "tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="], "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], @@ -1991,6 +2580,8 @@ "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], + "ulid": ["ulid@3.0.0", "", { "bin": { "ulid": "dist/cli.js" } }, "sha512-yvZYdXInnJve6LdlPIuYmURdS2NP41ZoF4QW7SXwbUKYt53+0eDAySO+rGSvM2O/ciuB/G+8N7GQrZ1mCJpuqw=="], + "ultrahtml": ["ultrahtml@1.6.0", "", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="], "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], @@ -2009,6 +2600,10 @@ "unifont": ["unifont@0.5.2", "", { "dependencies": { "css-tree": "^3.0.0", "ofetch": "^1.4.1", "ohash": "^2.0.0" } }, "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg=="], + "unique-filename": ["unique-filename@3.0.0", "", { "dependencies": { "unique-slug": "^4.0.0" } }, "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g=="], + + "unique-slug": ["unique-slug@4.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ=="], + "unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="], "unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="], @@ -2037,6 +2632,8 @@ "unstorage": ["unstorage@1.16.1", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", "h3": "^1.15.3", "lru-cache": "^10.4.3", "node-fetch-native": "^1.6.6", "ofetch": "^1.4.1", "ufo": "^1.6.1" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6.0.3 || ^7.0.0", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-gdpZ3guLDhz+zWIlYP1UwQ259tG5T5vYRzDaHMkQ1bBY1SQPutvZnrRjTFaWUUpseErJIgAZS51h6NOcZVZiqQ=="], + "upath": ["upath@1.2.0", "", {}, "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg=="], + "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], @@ -2051,6 +2648,10 @@ "validate-html-nesting": ["validate-html-nesting@1.2.3", "", {}, "sha512-kdkWdCl6eCeLlRShJKbjVOU2kFKxMF8Ghu50n+crEoyx+VKm3FxAxF9z4DCy6+bbTOqNW0+jcIYRnjoIRzigRw=="], + "validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="], + + "validate-npm-package-name": ["validate-npm-package-name@5.0.1", "", {}, "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], @@ -2059,9 +2660,11 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], + "vite": ["vite@6.2.2", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ=="], - "vite-plugin-solid": ["vite-plugin-solid@2.11.8", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-hFrCxBfv3B1BmFqnJF4JOCYpjrmi/zwyeKjcomQ0khh8HFyQ8SbuBWQ7zGojfrz6HUOBFrJBNySDi/JgAHytWg=="], + "vite-plugin-pages": ["vite-plugin-pages@0.32.5", "", { "dependencies": { "@types/debug": "^4.1.12", "debug": "^4.4.0", "dequal": "^2.0.3", "extract-comments": "^1.1.0", "fast-glob": "^3.3.3", "json5": "^2.2.3", "local-pkg": "^1.0.0", "picocolors": "^1.1.1", "yaml": "^2.7.0" }, "peerDependencies": { "@vue/compiler-sfc": "^2.7.0 || ^3.0.0", "vite": "^2.0.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["@vue/compiler-sfc"] }, "sha512-GY2JAt+4vZ4BqTtw+4CSUxPgYiqamrMRIzYk2AtJvQHeBoMlctsQW+tgCpKriUKINiKfi6NegbP07r1XrdxTWA=="], + + "vite-plugin-solid": ["vite-plugin-solid@2.11.6", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-Sl5CTqJTGyEeOsmdH6BOgalIZlwH3t4/y0RQuFLMGnvWMBvxb4+lq7x3BSiAw6etf0QexfNJW7HSOO/Qf7pigg=="], "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], @@ -2069,13 +2672,15 @@ "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], + "walk-up-path": ["walk-up-path@3.0.1", "", {}, "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA=="], + "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="], "which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="], @@ -2091,8 +2696,12 @@ "wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "write-file-atomic": ["write-file-atomic@5.0.1", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw=="], + "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], "xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="], @@ -2111,6 +2720,8 @@ "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], + "yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="], "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], @@ -2155,36 +2766,110 @@ "@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0-beta.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.2", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-e6WSsgM01au04/1L/v5daXHn00eKjPBQXl3jq3BfvQbQ1jo8Rls2pvrdkyVc25jBW4TV4Zm+tw+v6NAh5NPXMA=="], - "@ampproject/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.29", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ=="], + "@astrojs/cloudflare/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], "@astrojs/mdx/@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.2.1", "smol-toml": "^1.3.4", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-DDRtD1sPvAuA7ms2btc9A7/7DApKqgLMNrE6kh5tmkfy8utD0Z738gqd3p5aViYYdUtHIyEJ1X4mCMxfCfu15w=="], "@astrojs/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + "@astrojs/solid-js/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], + + "@astrojs/solid-js/vite-plugin-solid": ["vite-plugin-solid@2.11.8", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-hFrCxBfv3B1BmFqnJF4JOCYpjrmi/zwyeKjcomQ0khh8HFyQ8SbuBWQ7zGojfrz6HUOBFrJBNySDi/JgAHytWg=="], + + "@aws-crypto/crc32/@aws-sdk/types": ["@aws-sdk/types@3.840.0", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA=="], + + "@aws-crypto/sha256-browser/@aws-sdk/types": ["@aws-sdk/types@3.840.0", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA=="], + + "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + + "@aws-crypto/sha256-js/@aws-sdk/types": ["@aws-sdk/types@3.840.0", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA=="], + + "@aws-crypto/util/@aws-sdk/types": ["@aws-sdk/types@3.840.0", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA=="], + "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], - "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@aws-sdk/client-sso/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], - "@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.29", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ=="], + "@aws-sdk/core/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/credential-provider-env/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/credential-provider-http/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/credential-provider-ini/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/credential-provider-node/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/credential-provider-process/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/credential-provider-sso/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/credential-provider-web-identity/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/middleware-host-header/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/middleware-logger/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/middleware-recursion-detection/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/middleware-user-agent/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/nested-clients/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/region-config-resolver/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/token-providers/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/types/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/util-endpoints/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/util-user-agent-browser/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@aws-sdk/util-user-agent-node/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + + "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], + "@grpc/proto-loader/long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], "@grpc/proto-loader/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - "@jridgewell/gen-mapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.29", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ=="], + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], "@mdx-js/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + "@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "@npmcli/arborist/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "@npmcli/arborist/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "@npmcli/git/ini": ["ini@4.1.3", "", {}, "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg=="], + + "@npmcli/git/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "@npmcli/map-workspaces/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "@openauthjs/openauth/@standard-schema/spec": ["@standard-schema/spec@1.0.0-beta.3", "", {}, "sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw=="], "@openauthjs/openauth/aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], "@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="], + "@openauthjs/solid/@openauthjs/openauth": ["@openauthjs/openauth@0.4.2", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-8+Bia559iffrZXfQ0LWXrVVVriochS88pDtB8indyQ1S+40MQgDBu8aBzKt+fgSrTmoQGCTT+wlOXgbjc9qIcw=="], + + "@opencode/web/solid-js": ["solid-js@1.9.7", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw=="], + "@opentelemetry/exporter-otlp-http/@opentelemetry/core": ["@opentelemetry/core@1.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.0.0", "semver": "^7.3.5" }, "peerDependencies": { "@opentelemetry/api": "^1.0.2" } }, "sha512-1+qvKilADnSFW4PiXy+f7D22pvfGVxepZ69GcbF8cTcbQTUt7w63xEBWn5f5j92x9I3c0sqbW1RUx5/a4wgzxA=="], "@opentelemetry/exporter-otlp-http/@opentelemetry/resources": ["@opentelemetry/resources@1.0.0", "", { "dependencies": { "@opentelemetry/core": "1.0.0", "@opentelemetry/semantic-conventions": "1.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.2" } }, "sha512-ORP8F2LLcJEm5M3H24RmdlMdiDc70ySPushpkrAW34KZGdZXwkrFoFXZhhs5MUxPT+fLrTuBafXxZVr8eHtFuQ=="], @@ -2197,8 +2882,90 @@ "@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="], + "@poppinss/dumper/@sindresorhus/is": ["@sindresorhus/is@7.0.2", "", {}, "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw=="], + + "@pulumi/pulumi/@opentelemetry/exporter-zipkin": ["@opentelemetry/exporter-zipkin@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-6S2QIMJahIquvFaaxmcwpvQQRD/YFaMTNoIxrfPIPOeITN+a8lfEcPDxNxn8JDAaxkg+4EnXhz8upVDYenoQjA=="], + + "@pulumi/pulumi/@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.55.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.55.0", "@types/shimmer": "^1.2.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1", "semver": "^7.5.2", "shimmer": "^1.2.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-YDCMlaQRZkziLL3t6TONRgmmGxDx6MyQDXRD0dknkkgUZtOK5+8MWft1OXzmNu6XfBOdT12MKN5rz+jHUkafKQ=="], + + "@pulumi/pulumi/@opentelemetry/instrumentation-grpc": ["@opentelemetry/instrumentation-grpc@0.55.0", "", { "dependencies": { "@opentelemetry/instrumentation": "0.55.0", "@opentelemetry/semantic-conventions": "1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-n2ZH4pRwOy0Vhag/3eKqiyDBwcpUnGgJI9iiIRX7vivE0FMncaLazWphNFezRRaM/LuKwq1TD8pVUvieP68mow=="], + + "@pulumi/pulumi/@opentelemetry/resources": ["@opentelemetry/resources@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA=="], + + "@pulumi/pulumi/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg=="], + + "@pulumi/pulumi/@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@1.30.1", "", { "dependencies": { "@opentelemetry/context-async-hooks": "1.30.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/propagator-b3": "1.30.1", "@opentelemetry/propagator-jaeger": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1", "semver": "^7.5.2" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ=="], + + "@pulumi/pulumi/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], + + "@pulumi/pulumi/picomatch": ["picomatch@3.0.1", "", {}, "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag=="], + "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "@smithy/abort-controller/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/config-resolver/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/core/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/core/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + + "@smithy/credential-provider-imds/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/fetch-http-handler/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/hash-node/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/invalid-dependency/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/middleware-content-length/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/middleware-endpoint/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/middleware-retry/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/middleware-retry/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + + "@smithy/middleware-serde/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/middleware-stack/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/node-config-provider/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/node-http-handler/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/property-provider/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/protocol-http/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/querystring-builder/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/querystring-parser/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/service-error-classification/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/shared-ini-file-loader/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/signature-v4/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/smithy-client/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/url-parser/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/util-defaults-mode-browser/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/util-defaults-mode-node/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/util-endpoints/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/util-middleware/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/util-retry/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@smithy/util-stream/@smithy/types": ["@smithy/types@4.3.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw=="], + + "@tufjs/models/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "ai/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0-beta.2", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-vqhtZA7R24q1XnmfmIb1fZSmHMIaJH1BVQ+0kFnNJgqWsc+V8i+yfetZ37gUc4fXATFmBuS/6O7+RPoHsZ2Fqg=="], "ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0-beta.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.2", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-e6WSsgM01au04/1L/v5daXHn00eKjPBQXl3jq3BfvQbQ1jo8Rls2pvrdkyVc25jBW4TV4Zm+tw+v6NAh5NPXMA=="], @@ -2209,30 +2976,56 @@ "astro/diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="], + "astro/esbuild": ["esbuild@0.25.8", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], + "astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + "astro/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], + "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="], "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + "cacache/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "cacheable-request/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], + + "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], + + "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "estree-util-to-js/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], - "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], "gaxios/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], "giget/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "gray-matter/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], "hast-util-to-parse5/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="], + "hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + "ignore-walk/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "ip-address/sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="], + "jaeger-client/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "local-pkg/pkg-types": ["pkg-types@2.2.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ=="], + + "make-fetch-happen/negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="], + + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "miniflare/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], "miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], @@ -2241,14 +3034,24 @@ "miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], "mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "node-gyp/env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + "nypm/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@1.2.12", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ=="], + "opencode/@openauthjs/openauth": ["@openauthjs/openauth@0.4.3", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-RlnjqvHzqcbFVymEwhlUEuac4utA5h4nhSK/i2szZuQmxTIqbGUxZ+nM+avM+VV4Ing+/ZaNLKILoXS3yrkOOw=="], + "opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="], "opencontrol/hono": ["hono@4.7.4", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="], @@ -2259,8 +3062,12 @@ "openid-client/jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="], + "p-locate/p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="], + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "prebuild-install/tar-fs": ["tar-fs@2.1.3", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg=="], @@ -2269,6 +3076,8 @@ "protobufjs/long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + "rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "router/path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="], "sitemap/@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="], @@ -2277,8 +3086,18 @@ "sst/jose": ["jose@5.2.3", "", {}, "sha512-KUXdbctm1uHVL8BYhnyHkgp3zDX5KW8ZhAKVFEfUbU2P8Alpzjb+48hHvjOdQIyPshoblhzsuqOwEEAbtHVirA=="], + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "tar/chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="], + "tar/fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="], + + "tar/minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="], + "to-buffer/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], "tree-sitter/node-addon-api": ["node-addon-api@8.5.0", "", {}, "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A=="], @@ -2297,8 +3116,18 @@ "uri-js/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + "vite/esbuild": ["esbuild@0.25.8", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], + "wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], + "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "write-file-atomic/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "xml2js/sax": ["sax@1.4.1", "", {}, "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="], "yargs/yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], @@ -2325,16 +3154,74 @@ "@actions/github/@octokit/request-error/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], + "@astrojs/cloudflare/vite/esbuild": ["esbuild@0.25.8", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], + "@astrojs/mdx/@astrojs/markdown-remark/@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], + "@astrojs/solid-js/vite/esbuild": ["esbuild@0.25.8", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], + + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], + "@grpc/proto-loader/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], "@grpc/proto-loader/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "@openauthjs/solid/@openauthjs/openauth/@standard-schema/spec": ["@standard-schema/spec@1.0.0-beta.3", "", {}, "sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw=="], + + "@openauthjs/solid/@openauthjs/openauth/aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], + + "@openauthjs/solid/@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="], + "@opentelemetry/exporter-otlp-http/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.0.0", "", {}, "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA=="], "@opentelemetry/exporter-otlp-http/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.0.0", "", {}, "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA=="], @@ -2345,16 +3232,100 @@ "@opentelemetry/sdk-metrics-base/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.0.0", "", {}, "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA=="], + "@pulumi/pulumi/@opentelemetry/exporter-zipkin/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], + + "@pulumi/pulumi/@opentelemetry/exporter-zipkin/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], + + "@pulumi/pulumi/@opentelemetry/instrumentation/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.55.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg=="], + + "@pulumi/pulumi/@opentelemetry/instrumentation-grpc/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.27.0", "", {}, "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg=="], + + "@pulumi/pulumi/@opentelemetry/resources/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], + + "@pulumi/pulumi/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], + + "@pulumi/pulumi/@opentelemetry/sdk-trace-base/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], + + "@pulumi/pulumi/@opentelemetry/sdk-trace-base/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], + + "@pulumi/pulumi/@opentelemetry/sdk-trace-node/@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@1.30.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA=="], + + "@pulumi/pulumi/@opentelemetry/sdk-trace-node/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], + + "@pulumi/pulumi/@opentelemetry/sdk-trace-node/@opentelemetry/propagator-b3": ["@opentelemetry/propagator-b3@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ=="], + + "@pulumi/pulumi/@opentelemetry/sdk-trace-node/@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg=="], + + "@pulumi/pulumi/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "astro/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.8", "", { "os": "aix", "cpu": "ppc64" }, "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA=="], + + "astro/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.8", "", { "os": "android", "cpu": "arm" }, "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw=="], + + "astro/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.8", "", { "os": "android", "cpu": "arm64" }, "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w=="], + + "astro/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.8", "", { "os": "android", "cpu": "x64" }, "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA=="], + + "astro/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw=="], + + "astro/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg=="], + + "astro/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.8", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA=="], + + "astro/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw=="], + + "astro/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.8", "", { "os": "linux", "cpu": "arm" }, "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg=="], + + "astro/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w=="], + + "astro/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.8", "", { "os": "linux", "cpu": "ia32" }, "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg=="], + + "astro/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ=="], + + "astro/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw=="], + + "astro/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.8", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ=="], + + "astro/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg=="], + + "astro/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.8", "", { "os": "linux", "cpu": "s390x" }, "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg=="], + + "astro/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.8", "", { "os": "linux", "cpu": "x64" }, "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ=="], + + "astro/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.8", "", { "os": "none", "cpu": "x64" }, "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg=="], + + "astro/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.8", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ=="], + + "astro/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.8", "", { "os": "sunos", "cpu": "x64" }, "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w=="], + + "astro/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ=="], + + "astro/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg=="], + + "astro/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="], + + "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "gray-matter/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "local-pkg/pkg-types/confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + + "local-pkg/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "opencode/@ai-sdk/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], "opencode/@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], + "opencode/@openauthjs/openauth/@standard-schema/spec": ["@standard-schema/spec@1.0.0-beta.3", "", {}, "sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw=="], + + "opencode/@openauthjs/openauth/aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], + + "opencode/@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="], + "opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="], "opencontrol/@modelcontextprotocol/sdk/zod": ["zod@3.25.49", "", {}, "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q=="], @@ -2363,6 +3334,56 @@ "prebuild-install/tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "tar/fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.8", "", { "os": "aix", "cpu": "ppc64" }, "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA=="], + + "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.8", "", { "os": "android", "cpu": "arm" }, "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw=="], + + "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.8", "", { "os": "android", "cpu": "arm64" }, "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w=="], + + "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.8", "", { "os": "android", "cpu": "x64" }, "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA=="], + + "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw=="], + + "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg=="], + + "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.8", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA=="], + + "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw=="], + + "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.8", "", { "os": "linux", "cpu": "arm" }, "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg=="], + + "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w=="], + + "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.8", "", { "os": "linux", "cpu": "ia32" }, "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg=="], + + "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ=="], + + "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw=="], + + "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.8", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ=="], + + "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg=="], + + "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.8", "", { "os": "linux", "cpu": "s390x" }, "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg=="], + + "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.8", "", { "os": "linux", "cpu": "x64" }, "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ=="], + + "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.8", "", { "os": "none", "cpu": "x64" }, "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg=="], + + "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.8", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ=="], + + "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.8", "", { "os": "sunos", "cpu": "x64" }, "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w=="], + + "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ=="], + + "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg=="], + + "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="], + "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="], "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="], @@ -2413,6 +3434,10 @@ "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="], + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "@actions/github/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], "@actions/github/@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="], @@ -2423,6 +3448,100 @@ "@actions/github/@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], + "@astrojs/cloudflare/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.8", "", { "os": "aix", "cpu": "ppc64" }, "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.8", "", { "os": "android", "cpu": "arm" }, "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.8", "", { "os": "android", "cpu": "arm64" }, "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.8", "", { "os": "android", "cpu": "x64" }, "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.8", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.8", "", { "os": "linux", "cpu": "arm" }, "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.8", "", { "os": "linux", "cpu": "ia32" }, "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.8", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.8", "", { "os": "linux", "cpu": "s390x" }, "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.8", "", { "os": "linux", "cpu": "x64" }, "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.8", "", { "os": "none", "cpu": "x64" }, "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.8", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.8", "", { "os": "sunos", "cpu": "x64" }, "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg=="], + + "@astrojs/cloudflare/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.8", "", { "os": "aix", "cpu": "ppc64" }, "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.8", "", { "os": "android", "cpu": "arm" }, "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.8", "", { "os": "android", "cpu": "arm64" }, "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.8", "", { "os": "android", "cpu": "x64" }, "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.8", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.8", "", { "os": "linux", "cpu": "arm" }, "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.8", "", { "os": "linux", "cpu": "ia32" }, "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.8", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.8", "", { "os": "linux", "cpu": "s390x" }, "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.8", "", { "os": "linux", "cpu": "x64" }, "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.8", "", { "os": "none", "cpu": "x64" }, "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.8", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.8", "", { "os": "sunos", "cpu": "x64" }, "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg=="], + + "@astrojs/solid-js/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="], + + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], "@grpc/proto-loader/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -2433,6 +3552,8 @@ "@grpc/proto-loader/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@pulumi/pulumi/@opentelemetry/sdk-trace-node/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], + "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "@grpc/proto-loader/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], From fe26b4a7b11a0443c3dab0cb97f8f9b10746b485 Mon Sep 17 00:00:00 2001 From: Yihui Khuu Date: Sat, 9 Aug 2025 04:14:09 +1000 Subject: [PATCH 012/178] fix(tui): preserve scroll position when reflowing due to message stream (#1716) --- packages/tui/internal/components/chat/messages.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go index 46036b75f..22cb97fb5 100644 --- a/packages/tui/internal/components/chat/messages.go +++ b/packages/tui/internal/components/chat/messages.go @@ -210,7 +210,18 @@ func (m *messagesComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.clipboard = msg.clipboard m.loading = false m.tail = m.viewport.AtBottom() + + // Preserve scroll across reflow + // if the user was at bottom, keep following; otherwise restore the previous offset. + wasAtBottom := m.viewport.AtBottom() + prevYOffset := m.viewport.YOffset m.viewport = msg.viewport + if wasAtBottom { + m.viewport.GotoBottom() + } else { + m.viewport.YOffset = prevYOffset + } + m.header = msg.header if m.dirty { cmds = append(cmds, m.renderView()) @@ -218,7 +229,6 @@ func (m *messagesComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } m.tail = m.viewport.AtBottom() - viewport, cmd := m.viewport.Update(msg) m.viewport = viewport cmds = append(cmds, cmd) From 0a9463541ac5dfff2f2f38c5295e37e644d2c5d4 Mon Sep 17 00:00:00 2001 From: Typing Turtle Date: Fri, 8 Aug 2025 16:57:31 -0400 Subject: [PATCH 013/178] docs: Adds required models field to variables documentation (#1709) --- packages/web/src/content/docs/docs/config.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/src/content/docs/docs/config.mdx b/packages/web/src/content/docs/docs/config.mdx index 0bacf2bff..2119be5fa 100644 --- a/packages/web/src/content/docs/docs/config.mdx +++ b/packages/web/src/content/docs/docs/config.mdx @@ -317,6 +317,7 @@ Use `{env:VARIABLE_NAME}` to substitute environment variables: "model": "{env:OPENCODE_MODEL}", "provider": { "anthropic": { + "models": {}, "options": { "apiKey": "{env:ANTHROPIC_API_KEY}" } From d1208bf0a179985b33760d8dd22dcd3df8ec0025 Mon Sep 17 00:00:00 2001 From: Max Pod <63061392+max-pod@users.noreply.github.com> Date: Fri, 8 Aug 2025 17:11:06 -0400 Subject: [PATCH 014/178] docs: Update plugins.mdx (#1690) --- packages/web/src/content/docs/docs/plugins.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/content/docs/docs/plugins.mdx b/packages/web/src/content/docs/docs/plugins.mdx index 1bd662778..910ee4cdc 100644 --- a/packages/web/src/content/docs/docs/plugins.mdx +++ b/packages/web/src/content/docs/docs/plugins.mdx @@ -18,7 +18,7 @@ functions. Each function receives a context object and returns a hooks object. Plugins are loaded from: -1. `.opencode/plugin` directory either in your proejct +1. `.opencode/plugin` directory either in your project 2. Or, globally in `~/.config/opencode/plugin` --- From 0dde6d0840b47a216e7324acc1499ec8c70a14e8 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 8 Aug 2025 17:59:21 -0400 Subject: [PATCH 015/178] fix deploy script --- .github/workflows/deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 99d96eeb8..314ae6241 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,3 +24,4 @@ jobs: - run: bun sst deploy --stage=${{ github.ref_name }} env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + STRIPE_SECRET_KEY: ${{ github.ref_name == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }} From c9a3b35ac23ccb105a8f0a4feb79d5bdfbe9ad46 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 8 Aug 2025 18:39:47 -0400 Subject: [PATCH 016/178] fix deploy --- bun.lock | 3 +++ cloud/core/package.json | 1 + 2 files changed, 4 insertions(+) diff --git a/bun.lock b/bun.lock index 996a788a3..c4db3c6c3 100644 --- a/bun.lock +++ b/bun.lock @@ -17,6 +17,7 @@ "dependencies": { "@aws-sdk/client-sts": "3.782.0", "drizzle-orm": "0.41.0", + "postgres": "3.4.7", "stripe": "18.0.0", "ulid": "3.0.0", }, @@ -2206,6 +2207,8 @@ "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="], + "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], "postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="], diff --git a/cloud/core/package.json b/cloud/core/package.json index eeebf4392..9fc71f056 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -7,6 +7,7 @@ "dependencies": { "@aws-sdk/client-sts": "3.782.0", "drizzle-orm": "0.41.0", + "postgres": "3.4.7", "stripe": "18.0.0", "ulid": "3.0.0" }, From 7af2771a7e5d7c7230f7c5880d40b4adba54ea75 Mon Sep 17 00:00:00 2001 From: gsbain <64037650+gsbain@users.noreply.github.com> Date: Fri, 8 Aug 2025 19:04:02 -0500 Subject: [PATCH 017/178] Docs: Homebrew can install Opencode on Linux (#1737) --- README.md | 2 +- packages/web/src/content/docs/docs/index.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 259edaf12..1e16bde8c 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ curl -fsSL https://opencode.ai/install | bash # Package managers npm i -g opencode-ai@latest # or bun/pnpm/yarn -brew install sst/tap/opencode # macOS +brew install sst/tap/opencode # macOS and Linux paru -S opencode-bin # Arch Linux ``` diff --git a/packages/web/src/content/docs/docs/index.mdx b/packages/web/src/content/docs/docs/index.mdx index 15ed855a8..ccea974ca 100644 --- a/packages/web/src/content/docs/docs/index.mdx +++ b/packages/web/src/content/docs/docs/index.mdx @@ -63,7 +63,7 @@ You can also install it with the following: -- **Using Homebrew on macOS** +- **Using Homebrew on macOS and Linux** ```bash brew install sst/tap/opencode From 83f4e8e1560c1489796bd8dbf2e07e3f3e7e2471 Mon Sep 17 00:00:00 2001 From: rmoriz Date: Sat, 9 Aug 2025 02:04:26 +0200 Subject: [PATCH 018/178] Clarify remote mcp error (#1729) Co-authored-by: opencode --- packages/opencode/src/mcp/index.ts | 77 +++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index 7057be511..8f614b63a 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -35,35 +35,58 @@ export namespace MCP { log.info("found", { key, type: mcp.type }) if (mcp.type === "remote") { const transports = [ - new StreamableHTTPClientTransport(new URL(mcp.url), { - requestInit: { - headers: mcp.headers, - }, - }), - new SSEClientTransport(new URL(mcp.url), { - requestInit: { - headers: mcp.headers, - }, - }), + { + name: "StreamableHTTP", + transport: new StreamableHTTPClientTransport(new URL(mcp.url), { + requestInit: { + headers: mcp.headers, + }, + }), + }, + { + name: "SSE", + transport: new SSEClientTransport(new URL(mcp.url), { + requestInit: { + headers: mcp.headers, + }, + }), + }, ] - for (const transport of transports) { + let lastError: Error | undefined + for (const { name, transport } of transports) { const client = await experimental_createMCPClient({ name: key, transport, - }).catch(() => {}) - if (!client) continue - clients[key] = client - break + }).catch((error) => { + lastError = error instanceof Error ? error : new Error(String(error)) + log.debug("transport connection failed", { + key, + transport: name, + url: mcp.url, + error: lastError.message, + }) + return null + }) + if (client) { + log.debug("transport connection succeeded", { key, transport: name }) + clients[key] = client + break + } } - if (!clients[key]) + if (!clients[key]) { + const errorMessage = lastError + ? `MCP server ${key} failed to connect: ${lastError.message}` + : `MCP server ${key} failed to connect to ${mcp.url}` + log.error("remote mcp connection failed", { key, url: mcp.url, error: lastError?.message }) Bus.publish(Session.Event.Error, { error: { name: "UnknownError", data: { - message: `MCP server ${key} failed to start`, + message: errorMessage, }, }, }) + } } if (mcp.type === "local") { @@ -80,19 +103,29 @@ export namespace MCP { ...mcp.environment, }, }), - }).catch(() => {}) - if (!client) { + }).catch((error) => { + const errorMessage = + error instanceof Error + ? `MCP server ${key} failed to start: ${error.message}` + : `MCP server ${key} failed to start` + log.error("local mcp startup failed", { + key, + command: mcp.command, + error: error instanceof Error ? error.message : String(error), + }) Bus.publish(Session.Event.Error, { error: { name: "UnknownError", data: { - message: `MCP server ${key} failed to start`, + message: errorMessage, }, }, }) - continue + return null + }) + if (client) { + clients[key] = client } - clients[key] = client } } From 2f4291672b4f0d1f8145c5722a5f2886494a6dfe Mon Sep 17 00:00:00 2001 From: zWing <371657110@qq.com> Date: Sat, 9 Aug 2025 08:05:50 +0800 Subject: [PATCH 019/178] chore(js-sdk): Compatible with nodenext (#1667) --- packages/sdk/js/script/generate.ts | 5 ++++- packages/sdk/js/src/gen/client.gen.ts | 4 ++-- packages/sdk/js/src/gen/client/client.ts | 4 ++-- packages/sdk/js/src/gen/client/index.ts | 14 +++++++------- packages/sdk/js/src/gen/client/types.ts | 6 +++--- packages/sdk/js/src/gen/client/utils.ts | 10 +++++----- packages/sdk/js/src/gen/core/bodySerializer.ts | 2 +- packages/sdk/js/src/gen/core/types.ts | 4 ++-- packages/sdk/js/src/gen/sdk.gen.ts | 6 +++--- packages/sdk/js/src/index.ts | 8 ++++---- packages/sdk/js/tsconfig.json | 4 ++-- 11 files changed, 35 insertions(+), 32 deletions(-) diff --git a/packages/sdk/js/script/generate.ts b/packages/sdk/js/script/generate.ts index aba7d1430..ffe0779c7 100755 --- a/packages/sdk/js/script/generate.ts +++ b/packages/sdk/js/script/generate.ts @@ -12,7 +12,10 @@ await $`bun dev generate > ${dir}/openapi.json`.cwd(path.resolve(dir, "../../ope await createClient({ input: "./openapi.json", - output: "./src/gen", + output: { + path: "./src/gen", + tsConfigPath: path.join(dir, 'tsconfig.json') + }, plugins: [ { name: "@hey-api/typescript", diff --git a/packages/sdk/js/src/gen/client.gen.ts b/packages/sdk/js/src/gen/client.gen.ts index 5566242bd..e7cdb292c 100644 --- a/packages/sdk/js/src/gen/client.gen.ts +++ b/packages/sdk/js/src/gen/client.gen.ts @@ -1,7 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { ClientOptions } from "./types.gen" -import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from "./client" +import type { ClientOptions } from "./types.gen.js" +import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from "./client/index.js" /** * The `createClientConfig()` function will be called on client initialization diff --git a/packages/sdk/js/src/gen/client/client.ts b/packages/sdk/js/src/gen/client/client.ts index bc009574b..46a62694c 100644 --- a/packages/sdk/js/src/gen/client/client.ts +++ b/packages/sdk/js/src/gen/client/client.ts @@ -1,4 +1,4 @@ -import type { Client, Config, RequestOptions } from "./types" +import type { Client, Config, RequestOptions } from "./types.js" import { buildUrl, createConfig, @@ -7,7 +7,7 @@ import { mergeConfigs, mergeHeaders, setAuthParams, -} from "./utils" +} from "./utils.js" type ReqInit = Omit & { body?: any diff --git a/packages/sdk/js/src/gen/client/index.ts b/packages/sdk/js/src/gen/client/index.ts index c6b869b86..ce89a34cc 100644 --- a/packages/sdk/js/src/gen/client/index.ts +++ b/packages/sdk/js/src/gen/client/index.ts @@ -1,8 +1,8 @@ -export type { Auth } from "../core/auth" -export type { QuerySerializerOptions } from "../core/bodySerializer" -export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer } from "../core/bodySerializer" -export { buildClientParams } from "../core/params" -export { createClient } from "./client" +export type { Auth } from "../core/auth.js" +export type { QuerySerializerOptions } from "../core/bodySerializer.js" +export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer } from "../core/bodySerializer.js" +export { buildClientParams } from "../core/params.js" +export { createClient } from "./client.js" export type { Client, ClientOptions, @@ -14,5 +14,5 @@ export type { RequestResult, ResponseStyle, TDataShape, -} from "./types" -export { createConfig, mergeHeaders } from "./utils" +} from "./types.js" +export { createConfig, mergeHeaders } from "./utils.js" diff --git a/packages/sdk/js/src/gen/client/types.ts b/packages/sdk/js/src/gen/client/types.ts index 7f76fc517..f3b116bae 100644 --- a/packages/sdk/js/src/gen/client/types.ts +++ b/packages/sdk/js/src/gen/client/types.ts @@ -1,6 +1,6 @@ -import type { Auth } from "../core/auth" -import type { Client as CoreClient, Config as CoreConfig } from "../core/types" -import type { Middleware } from "./utils" +import type { Auth } from "../core/auth.js" +import type { Client as CoreClient, Config as CoreConfig } from "../core/types.js" +import type { Middleware } from "./utils.js" export type ResponseStyle = "data" | "fields" diff --git a/packages/sdk/js/src/gen/client/utils.ts b/packages/sdk/js/src/gen/client/utils.ts index 7b7942633..84648c855 100644 --- a/packages/sdk/js/src/gen/client/utils.ts +++ b/packages/sdk/js/src/gen/client/utils.ts @@ -1,8 +1,8 @@ -import { getAuthToken } from "../core/auth" -import type { QuerySerializer, QuerySerializerOptions } from "../core/bodySerializer" -import { jsonBodySerializer } from "../core/bodySerializer" -import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam } from "../core/pathSerializer" -import type { Client, ClientOptions, Config, RequestOptions } from "./types" +import { getAuthToken } from "../core/auth.js" +import type { QuerySerializer, QuerySerializerOptions } from "../core/bodySerializer.js" +import { jsonBodySerializer } from "../core/bodySerializer.js" +import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam } from "../core/pathSerializer.js" +import type { Client, ClientOptions, Config, RequestOptions } from "./types.js" interface PathSerializer { path: Record diff --git a/packages/sdk/js/src/gen/core/bodySerializer.ts b/packages/sdk/js/src/gen/core/bodySerializer.ts index 45b2e9943..8a4a13410 100644 --- a/packages/sdk/js/src/gen/core/bodySerializer.ts +++ b/packages/sdk/js/src/gen/core/bodySerializer.ts @@ -1,4 +1,4 @@ -import type { ArrayStyle, ObjectStyle, SerializerOptions } from "./pathSerializer" +import type { ArrayStyle, ObjectStyle, SerializerOptions } from "./pathSerializer.js" export type QuerySerializer = (query: Record) => string diff --git a/packages/sdk/js/src/gen/core/types.ts b/packages/sdk/js/src/gen/core/types.ts index 87cc8fec9..3a12e74c6 100644 --- a/packages/sdk/js/src/gen/core/types.ts +++ b/packages/sdk/js/src/gen/core/types.ts @@ -1,5 +1,5 @@ -import type { Auth, AuthToken } from "./auth" -import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from "./bodySerializer" +import type { Auth, AuthToken } from "./auth.js" +import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from "./bodySerializer.js" export interface Client { /** diff --git a/packages/sdk/js/src/gen/sdk.gen.ts b/packages/sdk/js/src/gen/sdk.gen.ts index 44ed1d5f6..4c42bd547 100644 --- a/packages/sdk/js/src/gen/sdk.gen.ts +++ b/packages/sdk/js/src/gen/sdk.gen.ts @@ -1,6 +1,6 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Options as ClientOptions, TDataShape, Client } from "./client" +import type { Options as ClientOptions, TDataShape, Client } from "./client/index.js" import type { EventSubscribeData, EventSubscribeResponses, @@ -73,8 +73,8 @@ import type { TuiClearPromptResponses, TuiExecuteCommandData, TuiExecuteCommandResponses, -} from "./types.gen" -import { client as _heyApiClient } from "./client.gen" +} from "./types.gen.js" +import { client as _heyApiClient } from "./client.gen.js" export type Options = ClientOptions< TData, diff --git a/packages/sdk/js/src/index.ts b/packages/sdk/js/src/index.ts index d133609f3..eeb837903 100644 --- a/packages/sdk/js/src/index.ts +++ b/packages/sdk/js/src/index.ts @@ -1,7 +1,7 @@ -import { createClient } from "./gen/client/client" -import { type Config } from "./gen/client/types" -import { OpencodeClient } from "./gen/sdk.gen" -export * from "./gen/types.gen" +import { createClient } from "./gen/client/client.js" +import { type Config } from "./gen/client/types.js" +import { OpencodeClient } from "./gen/sdk.gen.js" +export * from "./gen/types.gen.js" export function createOpencodeClient(config?: Config) { const client = createClient(config) diff --git a/packages/sdk/js/tsconfig.json b/packages/sdk/js/tsconfig.json index 519519408..6c5919a91 100644 --- a/packages/sdk/js/tsconfig.json +++ b/packages/sdk/js/tsconfig.json @@ -3,9 +3,9 @@ "extends": "@tsconfig/node22/tsconfig.json", "compilerOptions": { "outDir": "dist", - "module": "preserve", + "module": "nodenext", "declaration": true, - "moduleResolution": "bundler", + "moduleResolution": "nodenext", "lib": [ "es2022", "dom", From 9b6ef074f0a13883e49d491c4f24f467f2d17ca3 Mon Sep 17 00:00:00 2001 From: Clayton <63122915+chjudge@users.noreply.github.com> Date: Fri, 8 Aug 2025 20:07:00 -0400 Subject: [PATCH 020/178] Reference the actual name of the windows package (#1700) --- packages/opencode/bin/opencode.cmd | 2 +- packages/opencode/script/postinstall.mjs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opencode/bin/opencode.cmd b/packages/opencode/bin/opencode.cmd index 5908a815f..3a4ef3e72 100644 --- a/packages/opencode/bin/opencode.cmd +++ b/packages/opencode/bin/opencode.cmd @@ -11,7 +11,7 @@ set "script_dir=%~dp0" set "script_dir=%script_dir:~0,-1%" rem Detect platform and architecture -set "platform=win32" +set "platform=windows" rem Detect architecture if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( diff --git a/packages/opencode/script/postinstall.mjs b/packages/opencode/script/postinstall.mjs index 650aa66a2..2c6974123 100644 --- a/packages/opencode/script/postinstall.mjs +++ b/packages/opencode/script/postinstall.mjs @@ -20,7 +20,7 @@ function detectPlatformAndArch() { platform = "linux" break case "win32": - platform = "win32" + platform = "windows" break default: platform = os.platform() @@ -50,7 +50,7 @@ function detectPlatformAndArch() { function findBinary() { const { platform, arch } = detectPlatformAndArch() const packageName = `opencode-${platform}-${arch}` - const binary = platform === "win32" ? "opencode.exe" : "opencode" + const binary = platform === "windows" ? "opencode.exe" : "opencode" try { // Use require.resolve to find the package From b789844b9ca4f9943e6b1bc711fcd568512211f1 Mon Sep 17 00:00:00 2001 From: Erick Christian <54889298+ericc-ch@users.noreply.github.com> Date: Sat, 9 Aug 2025 07:07:20 +0700 Subject: [PATCH 021/178] feat(agent): allow mode selection during creation (#1699) --- packages/opencode/src/cli/cmd/agent.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/opencode/src/cli/cmd/agent.ts b/packages/opencode/src/cli/cmd/agent.ts index e929c3a8f..f96da6cfd 100644 --- a/packages/opencode/src/cli/cmd/agent.ts +++ b/packages/opencode/src/cli/cmd/agent.ts @@ -73,6 +73,29 @@ const AgentCreateCommand = cmd({ }) if (prompts.isCancel(selectedTools)) throw new UI.CancelledError() + const modeResult = await prompts.select({ + message: "Agent mode", + options: [ + { + label: "All", + value: "all" as const, + hint: "Can function in both primary and subagent roles", + }, + { + label: "Primary", + value: "primary" as const, + hint: "Acts as a primary/main agent", + }, + { + label: "Subagent", + value: "subagent" as const, + hint: "Can be used as a subagent by other agents", + }, + ], + initialValue: "all", + }) + if (prompts.isCancel(modeResult)) throw new UI.CancelledError() + const tools: Record = {} for (const tool of availableTools) { if (!selectedTools.includes(tool)) { @@ -82,6 +105,7 @@ const AgentCreateCommand = cmd({ const frontmatter: any = { description: generated.whenToUse, + mode: modeResult, } if (Object.keys(tools).length > 0) { frontmatter.tools = tools From 85eaa5b58bb9c78f164d9a1cb5303977dfebb4b4 Mon Sep 17 00:00:00 2001 From: Dax Date: Fri, 8 Aug 2025 20:20:01 -0400 Subject: [PATCH 022/178] Remove unused OpenTelemetry tracing and fix overlapping highlights (#1738) Co-authored-by: opencode --- .opencode/agent/docs.md | 2 +- packages/opencode/package.json | 7 ------- packages/opencode/src/index.ts | 2 -- packages/opencode/src/trace/index.ts | 17 ----------------- .../tui/internal/components/chat/message.go | 19 +++++++++++++++++++ 5 files changed, 20 insertions(+), 27 deletions(-) delete mode 100644 packages/opencode/src/trace/index.ts diff --git a/.opencode/agent/docs.md b/.opencode/agent/docs.md index ca317e00e..8ba4b52d4 100644 --- a/.opencode/agent/docs.md +++ b/.opencode/agent/docs.md @@ -1,5 +1,5 @@ --- -description: You MUST use this agent when writing documentation +description: ALWAYS use this when writing docs --- You are an expert technical documentation writer diff --git a/packages/opencode/package.json b/packages/opencode/package.json index d27727d73..bfd674876 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -38,13 +38,6 @@ "@openauthjs/openauth": "0.4.3", "@opencode-ai/plugin": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentelemetry/auto-instrumentations-node": "0.62.0", - "@opentelemetry/exporter-jaeger": "2.0.1", - "@opentelemetry/exporter-otlp-http": "0.26.0", - "@opentelemetry/exporter-trace-otlp-http": "0.203.0", - "@opentelemetry/instrumentation-fetch": "0.203.0", - "@opentelemetry/sdk-node": "0.203.0", - "@opentelemetry/sdk-trace-node": "2.0.1", "@standard-schema/spec": "1.0.0", "@zip.js/zip.js": "2.7.62", "ai": "catalog:", diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 8ef14e576..d2d52d0b5 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -1,6 +1,4 @@ import "zod-openapi/extend" -import { Trace } from "./trace" -Trace.init() import yargs from "yargs" import { hideBin } from "yargs/helpers" import { RunCommand } from "./cli/cmd/run" diff --git a/packages/opencode/src/trace/index.ts b/packages/opencode/src/trace/index.ts deleted file mode 100644 index 06b849d64..000000000 --- a/packages/opencode/src/trace/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NodeSDK } from "@opentelemetry/sdk-node" -import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch" -import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http" - -export namespace Trace { - export function init() { - const sdk = new NodeSDK({ - serviceName: "opencode", - instrumentations: [new FetchInstrumentation()], - traceExporter: new OTLPTraceExporter({ - url: "http://localhost:4318/v1/traces", // or your OTLP endpoint - }), - }) - - sdk.start() - } -} diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index 60d0fab55..6dcdcc989 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -268,7 +268,26 @@ func renderText( return 0 }) + // Merge overlapping highlights to prevent duplication + merged := make([]highlightPart, 0) for _, part := range highlights { + if len(merged) == 0 { + merged = append(merged, part) + continue + } + + last := &merged[len(merged)-1] + // If current part overlaps with the last one, merge them + if part.start <= last.end { + if part.end > last.end { + last.end = part.end + } + } else { + merged = append(merged, part) + } + } + + for _, part := range merged { highlight := base.Foreground(part.color) start, end := part.start, part.end From 53630ebdce8a3dc64dd11b0fb38205b35fad74fc Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 8 Aug 2025 20:42:22 -0400 Subject: [PATCH 023/178] gpt-5 lower verbosity --- bun.lock | 315 ++------------------ package.json | 4 +- packages/opencode/src/global/index.ts | 2 +- packages/opencode/src/provider/provider.ts | 30 +- packages/opencode/src/provider/transform.ts | 9 + packages/opencode/src/session/index.ts | 23 +- 6 files changed, 68 insertions(+), 315 deletions(-) diff --git a/bun.lock b/bun.lock index c4db3c6c3..08ea5be4b 100644 --- a/bun.lock +++ b/bun.lock @@ -96,13 +96,6 @@ "@openauthjs/openauth": "0.4.3", "@opencode-ai/plugin": "workspace:*", "@opencode-ai/sdk": "workspace:*", - "@opentelemetry/auto-instrumentations-node": "0.62.0", - "@opentelemetry/exporter-jaeger": "2.0.1", - "@opentelemetry/exporter-otlp-http": "0.26.0", - "@opentelemetry/exporter-trace-otlp-http": "0.203.0", - "@opentelemetry/instrumentation-fetch": "0.203.0", - "@opentelemetry/sdk-node": "0.203.0", - "@opentelemetry/sdk-trace-node": "2.0.1", "@standard-schema/spec": "1.0.0", "@zip.js/zip.js": "2.7.62", "ai": "catalog:", @@ -202,11 +195,11 @@ "@hono/zod-validator": "0.4.2", "@tsconfig/node22": "22.0.2", "@types/node": "22.13.9", - "ai": "5.0.0-beta.34", + "ai": "5.0.8", "hono": "4.7.10", "remeda": "2.26.0", "typescript": "5.8.2", - "zod": "3.25.49", + "zod": "3.25.76", }, "packages": { "@actions/core": ["@actions/core@1.11.1", "", { "dependencies": { "@actions/exec": "^1.1.1", "@actions/http-client": "^2.0.1" } }, "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A=="], @@ -223,7 +216,7 @@ "@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-uyyaO4KhxoIKZztREqLPh+6/K3ZJx/rp72JKoUEL9/kC+vfQTThUfPnY/bUryUpcnawx8IY/tSoYNOi/8PCv7w=="], - "@ai-sdk/gateway": ["@ai-sdk/gateway@1.0.0-beta.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.2", "@ai-sdk/provider-utils": "3.0.0-beta.10" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-felWPMuECZRGx8xnmvH5dW3jywKTkGnw/tXN8szphGzEDr/BfxywuXijfPBG2WBUS6frPXsvSLDRdCm5W38PXA=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@1.0.4", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-1roLdgMbFU3Nr4MC97/te7w6OqxsWBkDUkpbCcvxF3jz/ku91WVaJldn/PKU8feMKNyI5W9wnqhbjb1BqbExOQ=="], "@ai-sdk/openai": ["@ai-sdk/openai@2.0.2", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-D4zYz2uR90aooKQvX1XnS00Z7PkbrcY+snUvPfm5bCabTG7bzLrVtD56nJ5bSaZG8lmuOMfXpyiEEArYLyWPpw=="], @@ -633,169 +626,29 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.203.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ=="], + "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.55.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg=="], - "@opentelemetry/api-metrics": ["@opentelemetry/api-metrics@0.26.0", "", { "peerDependencies": { "@opentelemetry/api": "^1.0.2" } }, "sha512-idDSUTx+LRwJiHhVHhdh45SWow5u9lKNDROKu5AMzsIVPI29utH5FfT9vor8qMM6blxWWvlT22HUNdNMWqUQfQ=="], + "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@1.30.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA=="], - "@opentelemetry/auto-instrumentations-node": ["@opentelemetry/auto-instrumentations-node@0.62.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/instrumentation-amqplib": "^0.50.0", "@opentelemetry/instrumentation-aws-lambda": "^0.54.0", "@opentelemetry/instrumentation-aws-sdk": "^0.56.0", "@opentelemetry/instrumentation-bunyan": "^0.49.0", "@opentelemetry/instrumentation-cassandra-driver": "^0.49.0", "@opentelemetry/instrumentation-connect": "^0.47.0", "@opentelemetry/instrumentation-cucumber": "^0.18.0", "@opentelemetry/instrumentation-dataloader": "^0.21.0", "@opentelemetry/instrumentation-dns": "^0.47.0", "@opentelemetry/instrumentation-express": "^0.52.0", "@opentelemetry/instrumentation-fastify": "^0.48.0", "@opentelemetry/instrumentation-fs": "^0.23.0", "@opentelemetry/instrumentation-generic-pool": "^0.47.0", "@opentelemetry/instrumentation-graphql": "^0.51.0", "@opentelemetry/instrumentation-grpc": "^0.203.0", "@opentelemetry/instrumentation-hapi": "^0.50.0", "@opentelemetry/instrumentation-http": "^0.203.0", "@opentelemetry/instrumentation-ioredis": "^0.51.0", "@opentelemetry/instrumentation-kafkajs": "^0.12.0", "@opentelemetry/instrumentation-knex": "^0.48.0", "@opentelemetry/instrumentation-koa": "^0.51.0", "@opentelemetry/instrumentation-lru-memoizer": "^0.48.0", "@opentelemetry/instrumentation-memcached": "^0.47.0", "@opentelemetry/instrumentation-mongodb": "^0.56.0", "@opentelemetry/instrumentation-mongoose": "^0.50.0", "@opentelemetry/instrumentation-mysql": "^0.49.0", "@opentelemetry/instrumentation-mysql2": "^0.49.0", "@opentelemetry/instrumentation-nestjs-core": "^0.49.0", "@opentelemetry/instrumentation-net": "^0.47.0", "@opentelemetry/instrumentation-oracledb": "^0.29.0", "@opentelemetry/instrumentation-pg": "^0.55.0", "@opentelemetry/instrumentation-pino": "^0.50.0", "@opentelemetry/instrumentation-redis": "^0.51.0", "@opentelemetry/instrumentation-restify": "^0.49.0", "@opentelemetry/instrumentation-router": "^0.48.0", "@opentelemetry/instrumentation-runtime-node": "^0.17.0", "@opentelemetry/instrumentation-socket.io": "^0.50.0", "@opentelemetry/instrumentation-tedious": "^0.22.0", "@opentelemetry/instrumentation-undici": "^0.14.0", "@opentelemetry/instrumentation-winston": "^0.48.0", "@opentelemetry/resource-detector-alibaba-cloud": "^0.31.3", "@opentelemetry/resource-detector-aws": "^2.3.0", "@opentelemetry/resource-detector-azure": "^0.10.0", "@opentelemetry/resource-detector-container": "^0.7.3", "@opentelemetry/resource-detector-gcp": "^0.37.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-node": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.4.1", "@opentelemetry/core": "^2.0.0" } }, "sha512-h5g+VNJjiyX6u/IQpn36ZCHOENg1QW0GgBOHBcFGnHBBhmTww4R3brExdeuYbvLj3UQY09n+UHFEoMOqkhq07A=="], + "@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], - "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.0.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-XuY23lSI3d4PEqKA+7SLtAgwqIfc6E/E9eAQWLN1vlpC53ybO3o6jW4BsXo1xvz9lYyyWItfQDDLzezER01mCw=="], + "@opentelemetry/exporter-zipkin": ["@opentelemetry/exporter-zipkin@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-6S2QIMJahIquvFaaxmcwpvQQRD/YFaMTNoIxrfPIPOeITN+a8lfEcPDxNxn8JDAaxkg+4EnXhz8upVDYenoQjA=="], - "@opentelemetry/core": ["@opentelemetry/core@2.0.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw=="], + "@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.55.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.55.0", "@types/shimmer": "^1.2.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1", "semver": "^7.5.2", "shimmer": "^1.2.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-YDCMlaQRZkziLL3t6TONRgmmGxDx6MyQDXRD0dknkkgUZtOK5+8MWft1OXzmNu6XfBOdT12MKN5rz+jHUkafKQ=="], - "@opentelemetry/exporter-jaeger": ["@opentelemetry/exporter-jaeger@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0", "jaeger-client": "^3.15.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-FeHtOp2XMhYxzYhC8sXhsc3gMeoDzjI+CGuPX+vRSyUdHZHDKTMoY9jRfk8ObmZsZDTWmd63Yqcf4X472YtHeA=="], + "@opentelemetry/instrumentation-grpc": ["@opentelemetry/instrumentation-grpc@0.55.0", "", { "dependencies": { "@opentelemetry/instrumentation": "0.55.0", "@opentelemetry/semantic-conventions": "1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-n2ZH4pRwOy0Vhag/3eKqiyDBwcpUnGgJI9iiIRX7vivE0FMncaLazWphNFezRRaM/LuKwq1TD8pVUvieP68mow=="], - "@opentelemetry/exporter-logs-otlp-grpc": ["@opentelemetry/exporter-logs-otlp-grpc@0.203.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/sdk-logs": "0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-g/2Y2noc/l96zmM+g0LdeuyYKINyBwN6FJySoU15LHPLcMN/1a0wNk2SegwKcxrRdE7Xsm7fkIR5n6XFe3QpPw=="], + "@opentelemetry/propagator-b3": ["@opentelemetry/propagator-b3@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ=="], - "@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/sdk-logs": "0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-s0hys1ljqlMTbXx2XiplmMJg9wG570Z5lH7wMvrZX6lcODI56sG4HL03jklF63tBeyNwK2RV1/ntXGo3HgG4Qw=="], + "@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg=="], - "@opentelemetry/exporter-logs-otlp-proto": ["@opentelemetry/exporter-logs-otlp-proto@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-logs": "0.203.0", "@opentelemetry/sdk-trace-base": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-nl/7S91MXn5R1aIzoWtMKGvqxgJgepB/sH9qW0rZvZtabnsjbf8OQ1uSx3yogtvLr0GzwD596nQKz2fV7q2RBw=="], + "@opentelemetry/resources": ["@opentelemetry/resources@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA=="], - "@opentelemetry/exporter-metrics-otlp-grpc": ["@opentelemetry/exporter-metrics-otlp-grpc@0.203.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.1", "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-metrics": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-FCCj9nVZpumPQSEI57jRAA89hQQgONuoC35Lt+rayWY/mzCAc6BQT7RFyFaZKJ2B7IQ8kYjOCPsF/HGFWjdQkQ=="], + "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg=="], - "@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-metrics": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HFSW10y8lY6BTZecGNpV3GpoSy7eaO0Z6GATwZasnT4bEsILp8UJXNG5OmEsz4SdwCSYvyCbTJdNbZP3/8LGCQ=="], + "@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@1.30.1", "", { "dependencies": { "@opentelemetry/context-async-hooks": "1.30.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/propagator-b3": "1.30.1", "@opentelemetry/propagator-jaeger": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1", "semver": "^7.5.2" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ=="], - "@opentelemetry/exporter-metrics-otlp-proto": ["@opentelemetry/exporter-metrics-otlp-proto@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-metrics": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-OZnhyd9npU7QbyuHXFEPVm3LnjZYifuKpT3kTnF84mXeEQ84pJJZgyLBpU4FSkSwUkt/zbMyNAI7y5+jYTWGIg=="], - - "@opentelemetry/exporter-otlp-http": ["@opentelemetry/exporter-otlp-http@0.26.0", "", { "dependencies": { "@opentelemetry/api-metrics": "0.26.0", "@opentelemetry/core": "1.0.0", "@opentelemetry/resources": "1.0.0", "@opentelemetry/sdk-metrics-base": "0.26.0", "@opentelemetry/sdk-trace-base": "1.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.2" } }, "sha512-V3FcUEIVDZ66b3/6vjSBjwwozf/XV5eUXuELNzN8PAvGZH4mw36vaWlaxnGEV8HaZb2hbu2KbRpcOzqxx3tFDA=="], - - "@opentelemetry/exporter-prometheus": ["@opentelemetry/exporter-prometheus@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-metrics": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-2jLuNuw5m4sUj/SncDf/mFPabUxMZmmYetx5RKIMIQyPnl6G6ooFzfeE8aXNRf8YD1ZXNlCnRPcISxjveGJHNg=="], - - "@opentelemetry/exporter-trace-otlp-grpc": ["@opentelemetry/exporter-trace-otlp-grpc@0.203.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-322coOTf81bm6cAA8+ML6A+m4r2xTCdmAZzGNTboPXRzhwPt4JEmovsFAs+grpdarObd68msOJ9FfH3jxM6wqA=="], - - "@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-ZDiaswNYo0yq/cy1bBLJFe691izEJ6IgNmkjm4C6kE9ub/OMQqDXORx2D2j8fzTBTxONyzusbaZlqtfmyqURPw=="], - - "@opentelemetry/exporter-trace-otlp-proto": ["@opentelemetry/exporter-trace-otlp-proto@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-1xwNTJ86L0aJmWRwENCJlH4LULMG2sOXWIVw+Szta4fkqKVY50Eo4HoVKKq6U9QEytrWCr8+zjw0q/ZOeXpcAQ=="], - - "@opentelemetry/exporter-zipkin": ["@opentelemetry/exporter-zipkin@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-a9eeyHIipfdxzCfc2XPrE+/TI3wmrZUDFtG2RRXHSbZZULAny7SyybSvaDvS77a7iib5MPiAvluwVvbGTsHxsw=="], - - "@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ=="], - - "@opentelemetry/instrumentation-amqplib": ["@opentelemetry/instrumentation-amqplib@0.50.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-kwNs/itehHG/qaQBcVrLNcvXVPW0I4FCOVtw3LHMLdYIqD7GJ6Yv2nX+a4YHjzbzIeRYj8iyMp0Bl7tlkidq5w=="], - - "@opentelemetry/instrumentation-aws-lambda": ["@opentelemetry/instrumentation-aws-lambda@0.54.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/aws-lambda": "8.10.150" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-uiYI+kcMUJ/H9cxAwB8c9CaG8behLRgcYSOEA8M/tMQ54Y1ZmzAuEE3QKOi21/s30x5Q+by9g7BwiVfDtqzeMA=="], - - "@opentelemetry/instrumentation-aws-sdk": ["@opentelemetry/instrumentation-aws-sdk@0.56.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/propagation-utils": "^0.31.3", "@opentelemetry/semantic-conventions": "^1.34.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Jl2B/FYEb6tBCk9G31CMomKPikGU2g+CEhrGddDI0o1YeNpg3kAO9dExF+w489/IJUGZX6/wudyNvV7z4k9NjQ=="], - - "@opentelemetry/instrumentation-bunyan": ["@opentelemetry/instrumentation-bunyan@0.49.0", "", { "dependencies": { "@opentelemetry/api-logs": "^0.203.0", "@opentelemetry/instrumentation": "^0.203.0", "@types/bunyan": "1.8.11" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-ky5Am1y6s3Ex/3RygHxB/ZXNG07zPfg9Z6Ora+vfeKcr/+I6CJbWXWhSBJor3gFgKN3RvC11UWVURnmDpBS6Pg=="], - - "@opentelemetry/instrumentation-cassandra-driver": ["@opentelemetry/instrumentation-cassandra-driver@0.49.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-BNIvqldmLkeikfI5w5Rlm9vG5NnQexfPoxOgEMzfDVOEF+vS6351I6DzWLLgWWR9CNF/jQJJi/lr6am2DLp0Rw=="], - - "@opentelemetry/instrumentation-connect": ["@opentelemetry/instrumentation-connect@0.47.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/connect": "3.4.38" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-pjenvjR6+PMRb6/4X85L4OtkQCootgb/Jzh/l/Utu3SJHBid1F+gk9sTGU2FWuhhEfV6P7MZ7BmCdHXQjgJ42g=="], - - "@opentelemetry/instrumentation-cucumber": ["@opentelemetry/instrumentation-cucumber@0.18.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-i+cUbLHvRShuevtM0NwjQR9wnABhmYw8+dbgD57LNBde7xkuSDot0CTzX+pYn32djtQ1bPYZiLf+uwS0JsMUrw=="], - - "@opentelemetry/instrumentation-dataloader": ["@opentelemetry/instrumentation-dataloader@0.21.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Xu4CZ1bfhdkV3G6iVHFgKTgHx8GbKSqrTU01kcIJRGHpowVnyOPEv1CW5ow+9GU2X4Eki8zoNuVUenFc3RluxQ=="], - - "@opentelemetry/instrumentation-dns": ["@opentelemetry/instrumentation-dns@0.47.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-775fOnewWkTF4iXMGKgwvOGqEmPrU1PZpXjjqvTrEErYBJe7Fz1WlEeUStHepyKOdld7Ghv7TOF/kE3QDctvrg=="], - - "@opentelemetry/instrumentation-express": ["@opentelemetry/instrumentation-express@0.52.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-W7pizN0Wh1/cbNhhTf7C62NpyYw7VfCFTYg0DYieSTrtPBT1vmoSZei19wfKLnrMsz3sHayCg0HxCVL2c+cz5w=="], - - "@opentelemetry/instrumentation-fastify": ["@opentelemetry/instrumentation-fastify@0.48.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-3zQlE/DoVfVH6/ycuTv7vtR/xib6WOa0aLFfslYcvE62z0htRu/ot8PV/zmMZfnzpTQj8S/4ULv36R6UIbpJIg=="], - - "@opentelemetry/instrumentation-fetch": ["@opentelemetry/instrumentation-fetch@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/instrumentation": "0.203.0", "@opentelemetry/sdk-trace-web": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Z+mls3rOP2BaVykDZLLZPvchjj9l2oj3dYG1GTnrc27Y8o3biE+5M1b0izblycbbQHXjMPHQCpmjHbLMQuWtBg=="], - - "@opentelemetry/instrumentation-fs": ["@opentelemetry/instrumentation-fs@0.23.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Puan+QopWHA/KNYvDfOZN6M/JtF6buXEyD934vrb8WhsX1/FuM7OtoMlQyIqAadnE8FqqDL4KDPiEfCQH6pQcQ=="], - - "@opentelemetry/instrumentation-generic-pool": ["@opentelemetry/instrumentation-generic-pool@0.47.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-UfHqf3zYK+CwDwEtTjaD12uUqGGTswZ7ofLBEdQ4sEJp9GHSSJMQ2hT3pgBxyKADzUdoxQAv/7NqvL42ZI+Qbw=="], - - "@opentelemetry/instrumentation-graphql": ["@opentelemetry/instrumentation-graphql@0.51.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-LchkOu9X5DrXAnPI1+Z06h/EH/zC7D6sA86hhPrk3evLlsJTz0grPrkL/yUJM9Ty0CL/y2HSvmWQCjbJEz/ADg=="], - - "@opentelemetry/instrumentation-grpc": ["@opentelemetry/instrumentation-grpc@0.203.0", "", { "dependencies": { "@opentelemetry/instrumentation": "0.203.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Qmjx2iwccHYRLoE4RFS46CvQE9JG9Pfeae4EPaNZjvIuJxb/pZa2R9VWzRlTehqQWpAvto/dGhtkw8Tv+o0LTg=="], - - "@opentelemetry/instrumentation-hapi": ["@opentelemetry/instrumentation-hapi@0.50.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-5xGusXOFQXKacrZmDbpHQzqYD1gIkrMWuwvlrEPkYOsjUqGUjl1HbxCsn5Y9bUXOCgP1Lj6A4PcKt1UiJ2MujA=="], - - "@opentelemetry/instrumentation-http": ["@opentelemetry/instrumentation-http@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/instrumentation": "0.203.0", "@opentelemetry/semantic-conventions": "^1.29.0", "forwarded-parse": "2.1.2" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-y3uQAcCOAwnO6vEuNVocmpVzG3PER6/YZqbPbbffDdJ9te5NkHEkfSMNzlC3+v7KlE+WinPGc3N7MR30G1HY2g=="], - - "@opentelemetry/instrumentation-ioredis": ["@opentelemetry/instrumentation-ioredis@0.51.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/redis-common": "^0.38.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-9IUws0XWCb80NovS+17eONXsw1ZJbHwYYMXiwsfR9TSurkLV5UNbRSKb9URHO+K+pIJILy9wCxvyiOneMr91Ig=="], - - "@opentelemetry/instrumentation-kafkajs": ["@opentelemetry/instrumentation-kafkajs@0.12.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-bIe4aSAAxytp88nzBstgr6M7ZiEpW6/D1/SuKXdxxuprf18taVvFL2H5BDNGZ7A14K27haHqzYqtCTqFXHZOYg=="], - - "@opentelemetry/instrumentation-knex": ["@opentelemetry/instrumentation-knex@0.48.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.33.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-V5wuaBPv/lwGxuHjC6Na2JFRjtPgstw19jTFl1B1b6zvaX8zVDYUDaR5hL7glnQtUSCMktPttQsgK4dhXpddcA=="], - - "@opentelemetry/instrumentation-koa": ["@opentelemetry/instrumentation-koa@0.51.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-XNLWeMTMG1/EkQBbgPYzCeBD0cwOrfnn8ao4hWgLv0fNCFQu1kCsJYygz2cvKuCs340RlnG4i321hX7R8gj3Rg=="], - - "@opentelemetry/instrumentation-lru-memoizer": ["@opentelemetry/instrumentation-lru-memoizer@0.48.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-KUW29wfMlTPX1wFz+NNrmE7IzN7NWZDrmFWHM/VJcmFEuQGnnBuTIdsP55CnBDxKgQ/qqYFp4udQFNtjeFosPw=="], - - "@opentelemetry/instrumentation-memcached": ["@opentelemetry/instrumentation-memcached@0.47.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/memcached": "^2.2.6" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-vXDs/l4hlWy1IepPG1S6aYiIZn+tZDI24kAzwKKJmR2QEJRL84PojmALAEJGazIOLl/VdcCPZdMb0U2K0VzojA=="], - - "@opentelemetry/instrumentation-mongodb": ["@opentelemetry/instrumentation-mongodb@0.56.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-YG5IXUUmxX3Md2buVMvxm9NWlKADrnavI36hbJsihqqvBGsWnIfguf0rUP5Srr0pfPqhQjUP+agLMsvu0GmUpA=="], - - "@opentelemetry/instrumentation-mongoose": ["@opentelemetry/instrumentation-mongoose@0.50.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Am8pk1Ct951r4qCiqkBcGmPIgGhoDiFcRtqPSLbJrUZqEPUsigjtMjoWDRLG1Ki1NHgOF7D0H7d+suWz1AAizw=="], - - "@opentelemetry/instrumentation-mysql": ["@opentelemetry/instrumentation-mysql@0.49.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/mysql": "2.15.27" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-QU9IUNqNsrlfE3dJkZnFHqLjlndiU39ll/YAAEvWE40sGOCi9AtOF6rmEGzJ1IswoZ3oyePV7q2MP8SrhJfVAA=="], - - "@opentelemetry/instrumentation-mysql2": ["@opentelemetry/instrumentation-mysql2@0.49.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@opentelemetry/sql-common": "^0.41.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-dCub9wc02mkJWNyHdVEZ7dvRzy295SmNJa+LrAJY2a/+tIiVBQqEAajFzKwp9zegVVnel9L+WORu34rGLQDzxA=="], - - "@opentelemetry/instrumentation-nestjs-core": ["@opentelemetry/instrumentation-nestjs-core@0.49.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-1R/JFwdmZIk3T/cPOCkVvFQeKYzbbUvDxVH3ShXamUwBlGkdEu5QJitlRMyVNZaHkKZKWgYrBarGQsqcboYgaw=="], - - "@opentelemetry/instrumentation-net": ["@opentelemetry/instrumentation-net@0.47.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-csoJ++Njpf7C09JH+0HNGenuNbDZBqO1rFhMRo6s0rAmJwNh9zY3M/urzptmKlqbKnf4eH0s+CKHy/+M8fbFsQ=="], - - "@opentelemetry/instrumentation-oracledb": ["@opentelemetry/instrumentation-oracledb@0.29.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/oracledb": "6.5.2" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-2aHLiJdkyiUbooIUm7FaZf+O4jyqEl+RfFpgud1dxT87QeeYM216wi+xaMNzsb5yKtRBqbA3qeHBCyenYrOZwA=="], - - "@opentelemetry/instrumentation-pg": ["@opentelemetry/instrumentation-pg@0.55.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@opentelemetry/sql-common": "^0.41.0", "@types/pg": "8.15.4", "@types/pg-pool": "2.0.6" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-yfJ5bYE7CnkW/uNsnrwouG/FR7nmg09zdk2MSs7k0ZOMkDDAE3WBGpVFFApGgNu2U+gtzLgEzOQG4I/X+60hXw=="], - - "@opentelemetry/instrumentation-pino": ["@opentelemetry/instrumentation-pino@0.50.0", "", { "dependencies": { "@opentelemetry/api-logs": "^0.203.0", "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Pi0cWGp4f2gresq2xqef4IsuunLdebJ9n9tZxytDz2ci4euIfW36ILpszQmRNhwCVDCZLmUgGDKZGj4PXyPd0w=="], - - "@opentelemetry/instrumentation-redis": ["@opentelemetry/instrumentation-redis@0.51.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/redis-common": "^0.38.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-uL/GtBA0u72YPPehwOvthAe+Wf8k3T+XQPBssJmTYl6fzuZjNq8zTfxVFhl9nRFjFVEe+CtiYNT0Q3AyqW1Z0A=="], - - "@opentelemetry/instrumentation-restify": ["@opentelemetry/instrumentation-restify@0.49.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-tsGZZhS4mVZH7omYxw5jpsrD3LhWizqWc0PYtAnzpFUvL5ZINHE+cm57bssTQ2AK/GtZMxu9LktwCvIIf3dSmw=="], - - "@opentelemetry/instrumentation-router": ["@opentelemetry/instrumentation-router@0.48.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Wixrc8CchuJojXpaS/dCQjFOMc+3OEil1H21G+WLYQb8PcKt5kzW9zDBT19nyjjQOx/D/uHPfgbrT+Dc7cfJ9w=="], - - "@opentelemetry/instrumentation-runtime-node": ["@opentelemetry/instrumentation-runtime-node@0.17.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-O+xc0woqrSjue5IgpCCMvlgsuDrq6DDEfiHW3S3vRMCjXE1ZoPjaDE/K6EURorN+tjnzZQN1gOMSrscSGAbjHg=="], - - "@opentelemetry/instrumentation-socket.io": ["@opentelemetry/instrumentation-socket.io@0.50.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-6JN6lnKN9ZuZtZdMQIR+no1qHzQvXSZUsNe3sSWMgqmNRyEXuDUWBIyKKeG0oHRHtR4xE4QhJyD4D5kKRPWZFA=="], - - "@opentelemetry/instrumentation-tedious": ["@opentelemetry/instrumentation-tedious@0.22.0", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/tedious": "^4.0.14" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-XrrNSUCyEjH1ax9t+Uo6lv0S2FCCykcF7hSxBMxKf7Xn0bPRxD3KyFUZy25aQXzbbbUHhtdxj3r2h88SfEM3aA=="], - - "@opentelemetry/instrumentation-undici": ["@opentelemetry/instrumentation-undici@0.14.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.7.0" } }, "sha512-2HN+7ztxAReXuxzrtA3WboAKlfP5OsPA57KQn2AdYZbJ3zeRPcLXyW4uO/jpLE6PLm0QRtmeGCmfYpqRlwgSwg=="], - - "@opentelemetry/instrumentation-winston": ["@opentelemetry/instrumentation-winston@0.48.0", "", { "dependencies": { "@opentelemetry/api-logs": "^0.203.0", "@opentelemetry/instrumentation": "^0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-QuKbswAaQfRULhtlYbeNC9gOAXPxOSCE4BjIzuY1oEsc84kIsHUjn3yvY9Q83s3eg3j0JycNcAMi8u0yTl5PIQ=="], - - "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.203.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-transformer": "0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Wbxf7k+87KyvxFr5D7uOiSq/vHXWommvdnNE7vECO3tAhsA2GfOlpWINCMWUEPdHZ7tCXxw6Epp3vgx3jU7llQ=="], - - "@opentelemetry/otlp-grpc-exporter-base": ["@opentelemetry/otlp-grpc-exporter-base@0.203.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.203.0", "@opentelemetry/otlp-transformer": "0.203.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-te0Ze1ueJF+N/UOFl5jElJW4U0pZXQ8QklgSfJ2linHN0JJsuaHG8IabEUi2iqxY8ZBDlSiz1Trfv5JcjWWWwQ=="], - - "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-logs": "0.203.0", "@opentelemetry/sdk-metrics": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Y8I6GgoCna0qDQ2W6GCRtaF24SnvqvA8OfeTi7fqigD23u8Jpb4R5KFv/pRvrlGagcCLICMIyh9wiejp4TXu/A=="], - - "@opentelemetry/propagation-utils": ["@opentelemetry/propagation-utils@0.31.3", "", { "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-ZI6LKjyo+QYYZY5SO8vfoCQ9A69r1/g+pyjvtu5RSK38npINN1evEmwqbqhbg2CdcIK3a4PN6pDAJz/yC5/gAA=="], - - "@opentelemetry/propagator-b3": ["@opentelemetry/propagator-b3@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Hc09CaQ8Tf5AGLmf449H726uRoBNGPBL4bjr7AnnUpzWMvhdn61F78z9qb6IqB737TffBsokGAK1XykFEZ1igw=="], - - "@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-7PMdPBmGVH2eQNb/AtSJizQNgeNTfh6jQFqys6lfhd6P4r+m/nTh3gKPPpaCXVdRQ+z93vfKk+4UGty390283w=="], - - "@opentelemetry/redis-common": ["@opentelemetry/redis-common@0.38.0", "", {}, "sha512-4Wc0AWURII2cfXVVoZ6vDqK+s5n4K5IssdrlVrvGsx6OEOKdghKtJZqXAHWFiZv4nTDLH2/2fldjIHY8clMOjQ=="], - - "@opentelemetry/resource-detector-alibaba-cloud": ["@opentelemetry/resource-detector-alibaba-cloud@0.31.3", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-I556LHcLVsBXEgnbPgQISP/JezDt5OfpgOaJNR1iVJl202r+K145OSSOxnH5YOc/KvrydBD0FOE03F7x0xnVTw=="], - - "@opentelemetry/resource-detector-aws": ["@opentelemetry/resource-detector-aws@2.3.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-PkD/lyXG3B3REq1Y6imBLckljkJYXavtqGYSryAeJYvGOf5Ds3doR+BCGjmKeF6ObAtI5MtpBeUStTDtGtBsWA=="], - - "@opentelemetry/resource-detector-azure": ["@opentelemetry/resource-detector-azure@0.10.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-5cNAiyPBg53Uxe/CW7hsCq8HiKNAUGH+gi65TtgpzSR9bhJG4AEbuZhbJDFwe97tn2ifAD1JTkbc/OFuaaFWbA=="], - - "@opentelemetry/resource-detector-container": ["@opentelemetry/resource-detector-container@0.7.3", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-SK+xUFw6DKYbQniaGmIFsFxAZsr8RpRSRWxKi5/ZJAoqqPnjcyGI/SeUx8zzPk4XLO084zyM4pRHgir0hRTaSQ=="], - - "@opentelemetry/resource-detector-gcp": ["@opentelemetry/resource-detector-gcp@0.37.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.27.0", "gcp-metadata": "^6.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-LGpJBECIMsVKhiulb4nxUw++m1oF4EiDDPmFGW2aqYaAF0oUvJNv8Z/55CAzcZ7SxvlTgUwzewXDBsuCup7iqw=="], - - "@opentelemetry/resources": ["@opentelemetry/resources@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw=="], - - "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-vM2+rPq0Vi3nYA5akQD2f3QwossDnTDLvKbea6u/A2NZ3XDkPxMfo/PNrDoXhDUD/0pPo2CdH5ce/thn9K0kLw=="], - - "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g=="], - - "@opentelemetry/sdk-metrics-base": ["@opentelemetry/sdk-metrics-base@0.26.0", "", { "dependencies": { "@opentelemetry/api-metrics": "0.26.0", "@opentelemetry/core": "1.0.0", "@opentelemetry/resources": "1.0.0", "lodash.merge": "^4.6.2" }, "peerDependencies": { "@opentelemetry/api": "^1.0.2" } }, "sha512-PbJsso7Vy/CLATAOyXbt/VP7ZQ2QYnvlq28lhOWaLPw8aqLogMBvidNGRrt7rF4/hfzLT6pMgpAAcit2C/nUMA=="], - - "@opentelemetry/sdk-node": ["@opentelemetry/sdk-node@0.203.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.203.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/exporter-logs-otlp-grpc": "0.203.0", "@opentelemetry/exporter-logs-otlp-http": "0.203.0", "@opentelemetry/exporter-logs-otlp-proto": "0.203.0", "@opentelemetry/exporter-metrics-otlp-grpc": "0.203.0", "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", "@opentelemetry/exporter-metrics-otlp-proto": "0.203.0", "@opentelemetry/exporter-prometheus": "0.203.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.203.0", "@opentelemetry/exporter-trace-otlp-http": "0.203.0", "@opentelemetry/exporter-trace-otlp-proto": "0.203.0", "@opentelemetry/exporter-zipkin": "2.0.1", "@opentelemetry/instrumentation": "0.203.0", "@opentelemetry/propagator-b3": "2.0.1", "@opentelemetry/propagator-jaeger": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-logs": "0.203.0", "@opentelemetry/sdk-metrics": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "@opentelemetry/sdk-trace-node": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-zRMvrZGhGVMvAbbjiNQW3eKzW/073dlrSiAKPVWmkoQzah9wfynpVPeL55f9fVIm0GaBxTLcPeukWGy0/Wj7KQ=="], - - "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ=="], - - "@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.0.1", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.0.1", "@opentelemetry/core": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-UhdbPF19pMpBtCWYP5lHbTogLWx9N0EBxtdagvkn5YtsAnCBZzL7SjktG+ZmupRgifsHMjwUaCCaVmqGfSADmA=="], - - "@opentelemetry/sdk-trace-web": ["@opentelemetry/sdk-trace-web@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-R4/i0rISvAujG4Zwk3s6ySyrWG+Db3SerZVM4jZ2lEzjrNylF7nRAy1hVvWe8gTbwIxX+6w6ZvZwdtl2C7UQHQ=="], - - "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.36.0", "", {}, "sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ=="], - - "@opentelemetry/sql-common": ["@opentelemetry/sql-common@0.41.0", "", { "dependencies": { "@opentelemetry/core": "^2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0" } }, "sha512-pmzXctVbEERbqSfiAgdes9Y63xjoOyXcD7B6IXBkVb+vbM7M9U98mn33nGXxPf4dfYR0M+vhcKRZmbSJ7HfqFA=="], + "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], @@ -1047,8 +900,6 @@ "@tufjs/models": ["@tufjs/models@2.0.1", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^9.0.4" } }, "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg=="], - "@types/aws-lambda": ["@types/aws-lambda@8.10.150", "", {}, "sha512-AX+AbjH/rH5ezX1fbK8onC/a+HyQHo7QGmvoxAE42n22OsciAxvZoZNEr22tbXs8WfP1nIsBjKDpgPm3HjOZbA=="], - "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], @@ -1059,12 +910,8 @@ "@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="], - "@types/bunyan": ["@types/bunyan@1.8.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ=="], - "@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="], - "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], - "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], @@ -1091,22 +938,14 @@ "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], - "@types/memcached": ["@types/memcached@2.2.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg=="], - "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/mysql": ["@types/mysql@2.15.27", "", { "dependencies": { "@types/node": "*" } }, "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA=="], - "@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="], "@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="], - "@types/oracledb": ["@types/oracledb@6.5.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ=="], - "@types/pg": ["@types/pg@8.15.4", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg=="], - "@types/pg-pool": ["@types/pg-pool@2.0.6", "", { "dependencies": { "@types/pg": "*" } }, "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ=="], - "@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="], "@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="], @@ -1117,8 +956,6 @@ "@types/shimmer": ["@types/shimmer@1.2.0", "", {}, "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg=="], - "@types/tedious": ["@types/tedious@4.0.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw=="], - "@types/tmp": ["@types/tmp@0.2.6", "", {}, "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA=="], "@types/turndown": ["@types/turndown@5.0.5", "", {}, "sha512-TL2IgGgc7B5j78rIccBtlYAnkuv8nUQqhQc+DSYV5j9Be9XOcm/SKOVRuA47xAVI3680Tk9B1d8flK2GWT2+4w=="], @@ -1151,14 +988,12 @@ "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], - "ai": ["ai@5.0.0-beta.34", "", { "dependencies": { "@ai-sdk/gateway": "1.0.0-beta.19", "@ai-sdk/provider": "2.0.0-beta.2", "@ai-sdk/provider-utils": "3.0.0-beta.10", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-AFJ4p35AxA+1KFtnoouePLaAUpoj0IxIAoq/xgIv88qzYajTg4Sac5KaV4CDHFRLoF0L2cwhlFXt/Ss/zyBKkA=="], + "ai": ["ai@5.0.8", "", { "dependencies": { "@ai-sdk/gateway": "1.0.4", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.1", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-qbnhj046UvG30V1S5WhjBn+RBGEAmi8PSZWqMhRsE3EPxvO5BcePXTZFA23e9MYyWS9zr4Vm8Mv3wQXwLmtIBw=="], "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], - "ansi-color": ["ansi-color@0.2.1", "", {}, "sha512-bF6xLaZBLpOQzgYUtYEhJx090nPSZk1BQ/q2oyBK9aMMcJHzx9uXGCjI2Y+LebsN4Jwoykr0V9whbPiogdyHoQ=="], - "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], @@ -1223,8 +1058,6 @@ "before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], - "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], - "bin-links": ["bin-links@4.0.4", "", { "dependencies": { "cmd-shim": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", "read-cmd-shim": "^4.0.0", "write-file-atomic": "^5.0.0" } }, "sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA=="], "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], @@ -1253,8 +1086,6 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - "bufrw": ["bufrw@1.4.0", "", { "dependencies": { "ansi-color": "^0.2.1", "error": "^7.0.0", "hexer": "^1.5.0", "xtend": "^4.0.0" } }, "sha512-sWm8iPbqvL9+5SiYxXH73UOkyEbGQg7kyHQmReF89WJHQJw2eV4P/yZ0E+b71cczJ4pPobVhXxgQcmfSTgGHxQ=="], - "bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="], "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], @@ -1447,8 +1278,6 @@ "err-code": ["err-code@2.0.3", "", {}, "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="], - "error": ["error@7.0.2", "", { "dependencies": { "string-template": "~0.2.1", "xtend": "~4.0.0" } }, "sha512-UtVv4l5MhijsYUxPJo4390gzfZvAnTHreNnDjnTZaKIiZ/SemXxAhBkYSKtWa5RtBXbLP8tMgn/n0RUa/H7jXw=="], - "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="], "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], @@ -1557,8 +1386,6 @@ "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], - "forwarded-parse": ["forwarded-parse@2.1.2", "", {}, "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw=="], - "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], @@ -1569,10 +1396,6 @@ "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - "gaxios": ["gaxios@6.7.1", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="], - - "gcp-metadata": ["gcp-metadata@6.1.1", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="], - "gel": ["gel@2.1.1", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-Newg9X7mRYskoBjSw70l1YnJ/ZGbq64VPyR821H5WVkTGpHG2O0mQILxCeUhxdYERLFY9B4tUyKLyf3uMTjtKw=="], "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], @@ -1601,8 +1424,6 @@ "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], - "google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="], - "google-protobuf": ["google-protobuf@3.21.4", "", {}, "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ=="], "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], @@ -1667,8 +1488,6 @@ "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], - "hexer": ["hexer@1.5.0", "", { "dependencies": { "ansi-color": "^0.2.1", "minimist": "^1.1.0", "process": "^0.10.0", "xtend": "^4.0.0" }, "bin": { "hexer": "./cli.js" } }, "sha512-dyrPC8KzBzUJ19QTIo1gXNqIISRXQ0NwteW6OeQHRN4ZuZeHkdODfj0zHBdOlHbRY8GqbqK57C9oWSvQZizFsg=="], - "hono": ["hono@4.7.10", "", {}, "sha512-QkACju9MiN59CKSY5JsGZCYmPZkA6sIW6OFCUp7qDjZu6S6KHtJHhAc9Uy9mV9F8PJ1/HQ3ybZF2yjCa/73fvQ=="], "hono-openapi": ["hono-openapi@0.4.8", "", { "dependencies": { "json-schema-walker": "^2.0.0" }, "peerDependencies": { "@hono/arktype-validator": "^2.0.0", "@hono/effect-validator": "^1.2.0", "@hono/typebox-validator": "^0.2.0 || ^0.3.0", "@hono/valibot-validator": "^0.5.1", "@hono/zod-validator": "^0.4.1", "@sinclair/typebox": "^0.34.9", "@valibot/to-json-schema": "^1.0.0-beta.3", "arktype": "^2.0.0", "effect": "^3.11.3", "hono": "^4.6.13", "openapi-types": "^12.1.3", "valibot": "^1.0.0-beta.9", "zod": "^3.23.8", "zod-openapi": "^4.0.0" }, "optionalPeers": ["@hono/arktype-validator", "@hono/effect-validator", "@hono/typebox-validator", "@hono/valibot-validator", "@hono/zod-validator", "@sinclair/typebox", "@valibot/to-json-schema", "arktype", "effect", "hono", "valibot", "zod", "zod-openapi"] }, "sha512-LYr5xdtD49M7hEAduV1PftOMzuT8ZNvkyWfh1DThkLsIr4RkvDb12UxgIiFbwrJB6FLtFXLoOZL9x4IeDk2+VA=="], @@ -1781,8 +1600,6 @@ "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], - "jaeger-client": ["jaeger-client@3.19.0", "", { "dependencies": { "node-int64": "^0.4.0", "opentracing": "^0.14.4", "thriftrw": "^3.5.0", "uuid": "^8.3.2", "xorshift": "^1.1.1" } }, "sha512-M0c7cKHmdyEUtjemnJyx/y9uX16XHocL46yQvyqDlPdvAcwPDbHrIbKjQdBqtiE4apQ/9dmr+ZLJYYPGnurgpw=="], - "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="], "jmespath": ["jmespath@0.16.0", "", {}, "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw=="], @@ -1799,8 +1616,6 @@ "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], - "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], "json-parse-even-better-errors": ["json-parse-even-better-errors@3.0.2", "", {}, "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ=="], @@ -1843,9 +1658,7 @@ "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], - "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - - "long": ["long@2.4.0", "", {}, "sha512-ijUtjmO/n2A5PaosNG9ZGDsQ3vxJg7ZW8vsY8Kp0f2yIZWhSJvjmegV7t+9RPQKxKrvj8yKGehhS+po14hPLGQ=="], + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], @@ -2061,8 +1874,6 @@ "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], - "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], - "node-mock-http": ["node-mock-http@1.0.2", "", {}, "sha512-zWaamgDUdo9SSLw47we78+zYw/bDr5gH8pH7oRRs8V3KmBtu8GLgGIbV2p/gRPd3LWpEOpjQj7X1FOU3VFMJ8g=="], "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], @@ -2129,8 +1940,6 @@ "openid-client": ["openid-client@5.6.4", "", { "dependencies": { "jose": "^4.15.4", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" } }, "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA=="], - "opentracing": ["opentracing@0.14.7", "", {}, "sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q=="], - "p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="], "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], @@ -2225,8 +2034,6 @@ "proc-log": ["proc-log@4.2.0", "", {}, "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA=="], - "process": ["process@0.10.1", "", {}, "sha512-dyIett8dgGIZ/TXKUzeYExt7WA6ldDzys9vTDU/cCA9L17Ypme+KzS+NjQCjpn9xsvi/shbMC+yP/BcFMBz0NA=="], - "proggy": ["proggy@2.0.0", "", {}, "sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A=="], "promise-all-reject-late": ["promise-all-reject-late@1.0.1", "", {}, "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw=="], @@ -2487,8 +2294,6 @@ "streamx": ["streamx@2.22.1", "", { "dependencies": { "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" }, "optionalDependencies": { "bare-events": "^2.2.0" } }, "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA=="], - "string-template": ["string-template@0.2.1", "", {}, "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw=="], - "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -2527,8 +2332,6 @@ "text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="], - "thriftrw": ["thriftrw@3.11.4", "", { "dependencies": { "bufrw": "^1.2.1", "error": "7.0.2", "long": "^2.4.0" }, "bin": { "thrift2json": "thrift2json.js" } }, "sha512-UcuBd3eanB3T10nXWRRMwfwoaC6VMk7qe3/5YIWP2Jtw+EbHqJ0p1/K3x8ixiR5dozKSSfcg1W+0e33G1Di3XA=="], - "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="], "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], @@ -2713,8 +2516,6 @@ "xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="], - "xorshift": ["xorshift@1.2.0", "", {}, "sha512-iYgNnGyeeJ4t6U11NpA/QiKy+PXn5Aa3Azg5qkwIFz1tBLllQrjjsk9yzD7IAK0naNU4JxdeDgqW9ov4u/hc4g=="], - "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], @@ -2739,7 +2540,7 @@ "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], - "zod": ["zod@3.25.49", "", {}, "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q=="], + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "zod-openapi": ["zod-openapi@4.1.0", "", { "peerDependencies": { "zod": "^3.21.4" } }, "sha512-bRCwRYhEO9CmFLyKgJX8h6j1dRtRiwOe+TLzMVPyV0pRW5vRIgb1rLgIGcuRZ5z3MmSVrZqbv3yva4IJrtZK4g=="], @@ -2765,9 +2566,7 @@ "@ai-sdk/amazon-bedrock/aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], - "@ai-sdk/gateway/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0-beta.2", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-vqhtZA7R24q1XnmfmIb1fZSmHMIaJH1BVQ+0kFnNJgqWsc+V8i+yfetZ37gUc4fXATFmBuS/6O7+RPoHsZ2Fqg=="], - - "@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0-beta.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.2", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-e6WSsgM01au04/1L/v5daXHn00eKjPBQXl3jq3BfvQbQ1jo8Rls2pvrdkyVc25jBW4TV4Zm+tw+v6NAh5NPXMA=="], + "@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.1", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-/iP1sKc6UdJgGH98OCly7sWJKv+J9G47PnTjIj40IJMUQKwDrUMyf7zOOfRtPwSuNifYhSoJQ4s1WltI65gJ/g=="], "@astrojs/cloudflare/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], @@ -2775,6 +2574,8 @@ "@astrojs/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + "@astrojs/sitemap/zod": ["zod@3.25.49", "", {}, "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q=="], + "@astrojs/solid-js/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], "@astrojs/solid-js/vite-plugin-solid": ["vite-plugin-solid@2.11.8", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-hFrCxBfv3B1BmFqnJF4JOCYpjrmi/zwyeKjcomQ0khh8HFyQ8SbuBWQ7zGojfrz6HUOBFrJBNySDi/JgAHytWg=="], @@ -2841,8 +2642,6 @@ "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], - "@grpc/proto-loader/long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], - "@grpc/proto-loader/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], @@ -2851,6 +2650,8 @@ "@mdx-js/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + "@modelcontextprotocol/sdk/zod": ["zod@3.25.49", "", {}, "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q=="], + "@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "@npmcli/arborist/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -2873,32 +2674,12 @@ "@opencode/web/solid-js": ["solid-js@1.9.7", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw=="], - "@opentelemetry/exporter-otlp-http/@opentelemetry/core": ["@opentelemetry/core@1.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.0.0", "semver": "^7.3.5" }, "peerDependencies": { "@opentelemetry/api": "^1.0.2" } }, "sha512-1+qvKilADnSFW4PiXy+f7D22pvfGVxepZ69GcbF8cTcbQTUt7w63xEBWn5f5j92x9I3c0sqbW1RUx5/a4wgzxA=="], - - "@opentelemetry/exporter-otlp-http/@opentelemetry/resources": ["@opentelemetry/resources@1.0.0", "", { "dependencies": { "@opentelemetry/core": "1.0.0", "@opentelemetry/semantic-conventions": "1.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.2" } }, "sha512-ORP8F2LLcJEm5M3H24RmdlMdiDc70ySPushpkrAW34KZGdZXwkrFoFXZhhs5MUxPT+fLrTuBafXxZVr8eHtFuQ=="], - - "@opentelemetry/exporter-otlp-http/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@1.0.0", "", { "dependencies": { "@opentelemetry/core": "1.0.0", "@opentelemetry/resources": "1.0.0", "@opentelemetry/semantic-conventions": "1.0.0", "lodash.merge": "^4.6.2" }, "peerDependencies": { "@opentelemetry/api": "^1.0.2" } }, "sha512-/rXoyQlDlJTJ4SOVAbP0Gpj89B8oZ2hJApYG2Dq5klkgFAtDifN8271TIzwtM8/ET8HUhgx9eyoUJi42LhIesg=="], - - "@opentelemetry/sdk-metrics-base/@opentelemetry/core": ["@opentelemetry/core@1.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.0.0", "semver": "^7.3.5" }, "peerDependencies": { "@opentelemetry/api": "^1.0.2" } }, "sha512-1+qvKilADnSFW4PiXy+f7D22pvfGVxepZ69GcbF8cTcbQTUt7w63xEBWn5f5j92x9I3c0sqbW1RUx5/a4wgzxA=="], - - "@opentelemetry/sdk-metrics-base/@opentelemetry/resources": ["@opentelemetry/resources@1.0.0", "", { "dependencies": { "@opentelemetry/core": "1.0.0", "@opentelemetry/semantic-conventions": "1.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.2" } }, "sha512-ORP8F2LLcJEm5M3H24RmdlMdiDc70ySPushpkrAW34KZGdZXwkrFoFXZhhs5MUxPT+fLrTuBafXxZVr8eHtFuQ=="], + "@opentelemetry/instrumentation-grpc/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.27.0", "", {}, "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg=="], "@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="], "@poppinss/dumper/@sindresorhus/is": ["@sindresorhus/is@7.0.2", "", {}, "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw=="], - "@pulumi/pulumi/@opentelemetry/exporter-zipkin": ["@opentelemetry/exporter-zipkin@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-6S2QIMJahIquvFaaxmcwpvQQRD/YFaMTNoIxrfPIPOeITN+a8lfEcPDxNxn8JDAaxkg+4EnXhz8upVDYenoQjA=="], - - "@pulumi/pulumi/@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.55.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.55.0", "@types/shimmer": "^1.2.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1", "semver": "^7.5.2", "shimmer": "^1.2.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-YDCMlaQRZkziLL3t6TONRgmmGxDx6MyQDXRD0dknkkgUZtOK5+8MWft1OXzmNu6XfBOdT12MKN5rz+jHUkafKQ=="], - - "@pulumi/pulumi/@opentelemetry/instrumentation-grpc": ["@opentelemetry/instrumentation-grpc@0.55.0", "", { "dependencies": { "@opentelemetry/instrumentation": "0.55.0", "@opentelemetry/semantic-conventions": "1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-n2ZH4pRwOy0Vhag/3eKqiyDBwcpUnGgJI9iiIRX7vivE0FMncaLazWphNFezRRaM/LuKwq1TD8pVUvieP68mow=="], - - "@pulumi/pulumi/@opentelemetry/resources": ["@opentelemetry/resources@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA=="], - - "@pulumi/pulumi/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg=="], - - "@pulumi/pulumi/@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@1.30.1", "", { "dependencies": { "@opentelemetry/context-async-hooks": "1.30.1", "@opentelemetry/core": "1.30.1", "@opentelemetry/propagator-b3": "1.30.1", "@opentelemetry/propagator-jaeger": "1.30.1", "@opentelemetry/sdk-trace-base": "1.30.1", "semver": "^7.5.2" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ=="], - "@pulumi/pulumi/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], "@pulumi/pulumi/picomatch": ["picomatch@3.0.1", "", {}, "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag=="], @@ -2969,9 +2750,7 @@ "@tufjs/models/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - "ai/@ai-sdk/provider": ["@ai-sdk/provider@2.0.0-beta.2", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-vqhtZA7R24q1XnmfmIb1fZSmHMIaJH1BVQ+0kFnNJgqWsc+V8i+yfetZ37gUc4fXATFmBuS/6O7+RPoHsZ2Fqg=="], - - "ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0-beta.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.2", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-e6WSsgM01au04/1L/v5daXHn00eKjPBQXl3jq3BfvQbQ1jo8Rls2pvrdkyVc25jBW4TV4Zm+tw+v6NAh5NPXMA=="], + "ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.1", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-/iP1sKc6UdJgGH98OCly7sWJKv+J9G47PnTjIj40IJMUQKwDrUMyf7zOOfRtPwSuNifYhSoJQ4s1WltI65gJ/g=="], "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -2985,6 +2764,8 @@ "astro/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], + "astro/zod": ["zod@3.25.49", "", {}, "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q=="], + "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="], "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], @@ -3003,8 +2784,6 @@ "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "gaxios/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], - "giget/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -3021,8 +2800,6 @@ "ip-address/sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="], - "jaeger-client/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], - "local-pkg/pkg-types": ["pkg-types@2.2.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ=="], "make-fetch-happen/negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="], @@ -3077,8 +2854,6 @@ "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], - "protobufjs/long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], - "rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], "router/path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="], @@ -3225,40 +3000,6 @@ "@openauthjs/solid/@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="], - "@opentelemetry/exporter-otlp-http/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.0.0", "", {}, "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA=="], - - "@opentelemetry/exporter-otlp-http/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.0.0", "", {}, "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA=="], - - "@opentelemetry/exporter-otlp-http/@opentelemetry/sdk-trace-base/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.0.0", "", {}, "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA=="], - - "@opentelemetry/sdk-metrics-base/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.0.0", "", {}, "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA=="], - - "@opentelemetry/sdk-metrics-base/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.0.0", "", {}, "sha512-XCZ6ZSmc8FOspxKUU+Ow9UtJeSSRcS5rFBYGpjzix02U2v+X9ofjOjgNRnpvxlSvkccYIhdTuwcvNskmZ46SeA=="], - - "@pulumi/pulumi/@opentelemetry/exporter-zipkin/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], - - "@pulumi/pulumi/@opentelemetry/exporter-zipkin/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], - - "@pulumi/pulumi/@opentelemetry/instrumentation/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.55.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg=="], - - "@pulumi/pulumi/@opentelemetry/instrumentation-grpc/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.27.0", "", {}, "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg=="], - - "@pulumi/pulumi/@opentelemetry/resources/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], - - "@pulumi/pulumi/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], - - "@pulumi/pulumi/@opentelemetry/sdk-trace-base/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], - - "@pulumi/pulumi/@opentelemetry/sdk-trace-base/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], - - "@pulumi/pulumi/@opentelemetry/sdk-trace-node/@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@1.30.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA=="], - - "@pulumi/pulumi/@opentelemetry/sdk-trace-node/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], - - "@pulumi/pulumi/@opentelemetry/sdk-trace-node/@opentelemetry/propagator-b3": ["@opentelemetry/propagator-b3@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ=="], - - "@pulumi/pulumi/@opentelemetry/sdk-trace-node/@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg=="], - "@pulumi/pulumi/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -3555,8 +3296,6 @@ "@grpc/proto-loader/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "@pulumi/pulumi/@opentelemetry/sdk-trace-node/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], - "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "@grpc/proto-loader/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], diff --git a/package.json b/package.json index 52d9a8566..d26b9c066 100644 --- a/package.json +++ b/package.json @@ -20,10 +20,10 @@ "@hono/zod-validator": "0.4.2", "@types/node": "22.13.9", "@tsconfig/node22": "22.0.2", - "ai": "5.0.0-beta.34", + "ai": "5.0.8", "hono": "4.7.10", "typescript": "5.8.2", - "zod": "3.25.49", + "zod": "3.25.76", "remeda": "2.26.0" } }, diff --git a/packages/opencode/src/global/index.ts b/packages/opencode/src/global/index.ts index a2e4b4b1f..ca83b4695 100644 --- a/packages/opencode/src/global/index.ts +++ b/packages/opencode/src/global/index.ts @@ -27,7 +27,7 @@ await Promise.all([ fs.mkdir(Global.Path.log, { recursive: true }), ]) -const CACHE_VERSION = "4" +const CACHE_VERSION = "5" const version = await Bun.file(path.join(Global.Path.cache, "version")) .text() diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 759e3b917..6c8273c33 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -97,7 +97,7 @@ export namespace Provider { Array.isArray(msg.content) && msg.content.some((part: any) => part.type === "image_url"), ) } - } catch { } + } catch {} const headers: Record = { ...init.headers, ...copilot.HEADERS, @@ -283,26 +283,26 @@ export namespace Provider { cost: !model.cost && !existing?.cost ? { - input: 0, - output: 0, - cache_read: 0, - cache_write: 0, - } + input: 0, + output: 0, + cache_read: 0, + cache_write: 0, + } : { - cache_read: 0, - cache_write: 0, - ...existing?.cost, - ...model.cost, - }, + cache_read: 0, + cache_write: 0, + ...existing?.cost, + ...model.cost, + }, options: { ...existing?.options, ...model.options, }, limit: model.limit ?? existing?.limit ?? { - context: 0, - output: 0, - }, + context: 0, + output: 0, + }, } parsed.models[modelID] = parsedModel } @@ -373,7 +373,7 @@ export namespace Provider { const existing = s.sdk.get(provider.id) if (existing) return existing const pkg = provider.npm ?? provider.id - const mod = await import(await BunProc.install(pkg, "beta")) + const mod = await import(await BunProc.install(pkg, "latest")) const fn = mod[Object.keys(mod).find((key) => key.startsWith("create"))!] const loaded = fn({ name: provider.id, diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 3264dd05d..9bebda346 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -81,4 +81,13 @@ export namespace ProviderTransform { if (modelID.toLowerCase().includes("qwen")) return 1 return undefined } + + export function options(_providerID: string, modelID: string) { + if (modelID.includes("gpt-5")) { + return { + reasoningEffort: "low", + textVerbosity: "low", + } + } + } } diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index b562c433a..3e82610b3 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -322,9 +322,9 @@ export namespace Session { for (const child of await children(sessionID)) { await remove(child.id, false) } - await unshare(sessionID).catch(() => { }) - await Storage.remove(`session/info/${sessionID}`).catch(() => { }) - await Storage.removeDir(`session/message/${sessionID}/`).catch(() => { }) + await unshare(sessionID).catch(() => {}) + await Storage.remove(`session/info/${sessionID}`).catch(() => {}) + await Storage.removeDir(`session/message/${sessionID}/`).catch(() => {}) state().sessions.delete(sessionID) state().messages.delete(sessionID) if (emitEvent) { @@ -523,7 +523,7 @@ export namespace Session { sessionID: input.sessionID, abort: new AbortController().signal, messageID: userMsg.id, - metadata: async () => { }, + metadata: async () => {}, }), ) return [ @@ -632,7 +632,7 @@ export namespace Session { // mark session as updated // used for session list sorting (indicates when session was most recently interacted with) - await update(input.sessionID, (_draft) => { }) + await update(input.sessionID, (_draft) => {}) if (isLocked(input.sessionID)) { return new Promise((resolve) => { @@ -679,7 +679,9 @@ export namespace Session { generateText({ maxOutputTokens: small.info.reasoning ? 1024 : 20, providerOptions: { - [input.providerID]: small.info.options, + [input.providerID]: { + ...small.info.options, + }, }, messages: [ ...SystemPrompt.title(input.providerID).map( @@ -712,7 +714,7 @@ export namespace Session { draft.title = title.trim() }) }) - .catch(() => { }) + .catch(() => {}) } const agent = await Agent.get(inputAgent) @@ -927,11 +929,14 @@ export namespace Session { }, maxRetries: 3, activeTools: Object.keys(tools).filter((x) => x !== "invalid"), - maxOutputTokens: model.info.id.startsWith("gpt-5") ? undefined : outputLimit, + maxOutputTokens: outputLimit, abortSignal: abort.signal, stopWhen: stepCountIs(1000), providerOptions: { - [input.providerID]: model.info.options, + [input.providerID]: { + ...ProviderTransform.options(input.providerID, input.modelID), + ...model.info.options, + }, }, temperature: params.temperature, topP: params.topP, From 7bbc643600a8a669f4dd9136a29f220a5b0e81ab Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 8 Aug 2025 20:44:59 -0400 Subject: [PATCH 024/178] remove synthetic message in plan mode, fixes being confused in build mode --- packages/opencode/src/session/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 3e82610b3..494a39c4d 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -16,7 +16,6 @@ import { } from "ai" import PROMPT_INITIALIZE from "../session/prompt/initialize.txt" -import PROMPT_PLAN from "../session/prompt/plan.txt" import { App } from "../app/app" import { Bus } from "../bus" @@ -608,6 +607,7 @@ export namespace Session { ] }), ).then((x) => x.flat()) + /* if (inputAgent === "plan") userParts.push({ id: Identifier.ascending("part"), @@ -617,6 +617,7 @@ export namespace Session { text: PROMPT_PLAN, synthetic: true, }) + */ await Plugin.trigger( "chat.message", {}, From 81a3e02474445bc4d22c7857d4c3b1f591c1cb56 Mon Sep 17 00:00:00 2001 From: Dominik Engelhardt Date: Sat, 9 Aug 2025 03:06:38 +0200 Subject: [PATCH 025/178] feat: improve file attachment pasting (#1704) --- .../tui/internal/components/chat/editor.go | 204 +++++++++---- .../internal/components/chat/editor_test.go | 277 ++++++++++++++++++ packages/tui/internal/tui/tui.go | 3 + 3 files changed, 435 insertions(+), 49 deletions(-) create mode 100644 packages/tui/internal/components/chat/editor_test.go diff --git a/packages/tui/internal/components/chat/editor.go b/packages/tui/internal/components/chat/editor.go index 509de6244..1cfbbc064 100644 --- a/packages/tui/internal/components/chat/editor.go +++ b/packages/tui/internal/components/chat/editor.go @@ -27,6 +27,50 @@ import ( "github.com/sst/opencode/internal/util" ) +type AttachmentInsertedMsg struct{} + +// unescapeClipboardText trims surrounding quotes from clipboard text and returns the inner content. +// It avoids interpreting backslash escape sequences unless the text is explicitly quoted. +func (m *editorComponent) unescapeClipboardText(s string) string { + t := strings.TrimSpace(s) + if len(t) >= 2 { + first := t[0] + last := t[len(t)-1] + if (first == '"' && last == '"') || (first == '\'' && last == '\'') { + if u, err := strconv.Unquote(t); err == nil { + return u + } + return t[1 : len(t)-1] + } + } + return t +} + +// pathExists checks if the given path exists. Relative paths are resolved against the app CWD. +// Supports expanding '~' to the user's home directory. +func (m *editorComponent) pathExists(p string) bool { + if p == "" { + return false + } + if strings.HasPrefix(p, "~") { + if home, err := os.UserHomeDir(); err == nil { + if p == "~" { + p = home + } else if strings.HasPrefix(p, "~/") { + p = filepath.Join(home, p[2:]) + } + } + } + check := p + if !filepath.IsAbs(check) { + check = filepath.Join(m.app.Info.Path.Cwd, check) + } + if _, err := os.Stat(check); err == nil { + return true + } + return false +} + type EditorComponent interface { tea.Model tea.ViewModel @@ -153,60 +197,123 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } case tea.PasteMsg: - text := string(msg) + // Normalize clipboard text first + textRaw := string(msg) + text := m.unescapeClipboardText(textRaw) - if filePath := strings.TrimSpace(strings.TrimPrefix(text, "@")); strings.HasPrefix(text, "@") && filePath != "" { - statPath := filePath - if !filepath.IsAbs(filePath) { - statPath = filepath.Join(m.app.Info.Path.Cwd, filePath) + // Case 1: pasted content contains one or more inline @paths -> insert attachments inline + // We scan the raw pasted text to preserve original content around attachments. + if strings.Contains(textRaw, "@") { + last := 0 + idx := 0 + inserted := 0 + for idx < len(textRaw) { + r, size := utf8.DecodeRuneInString(textRaw[idx:]) + if r != '@' { + idx += size + continue + } + + // Insert preceding chunk before attempting to consume a path + if idx > last { + m.textarea.InsertRunesFromUserInput([]rune(textRaw[last:idx])) + } + + // Extract candidate path after '@' up to whitespace + start := idx + size + end := start + for end < len(textRaw) { + nr, ns := utf8.DecodeRuneInString(textRaw[end:]) + if nr == ' ' || nr == '\t' || nr == '\n' || nr == '\r' { + break + } + end += ns + } + + if end > start { + raw := textRaw[start:end] + // Trim common trailing punctuation that may follow paths in prose + trimmed := strings.TrimRight(raw, ",.;:)]}\\\"'?!") + suffix := raw[len(trimmed):] + p := filepath.Clean(trimmed) + if m.pathExists(p) { + att := m.createAttachmentFromPath(p) + if att != nil { + m.textarea.InsertAttachment(att) + if suffix != "" { + m.textarea.InsertRunesFromUserInput([]rune(suffix)) + } + // Insert a trailing space only if the next rune isn't already whitespace + insertSpace := true + if end < len(textRaw) { + nr, _ := utf8.DecodeRuneInString(textRaw[end:]) + if nr == ' ' || nr == '\t' || nr == '\n' || nr == '\r' { + insertSpace = false + } + } + if insertSpace { + m.textarea.InsertString(" ") + } + inserted++ + last = end + idx = end + continue + } + } + } + + // No valid path -> keep the '@' literally + m.textarea.InsertRune('@') + last = start + idx = start } - if _, err := os.Stat(statPath); err == nil { - attachment := m.createAttachmentFromPath(filePath) - if attachment != nil { - m.textarea.InsertAttachment(attachment) + // Insert any trailing content after the last processed segment + if last < len(textRaw) { + m.textarea.InsertRunesFromUserInput([]rune(textRaw[last:])) + } + if inserted > 0 { + return m, util.CmdHandler(AttachmentInsertedMsg{}) + } + } + + // Case 2: user typed '@' and then pasted a valid path -> replace '@' with attachment + at := m.textarea.LastRuneIndex('@') + if at != -1 && at == m.textarea.CursorColumn()-1 { + p := filepath.Clean(text) + if m.pathExists(p) { + cur := m.textarea.CursorColumn() + m.textarea.ReplaceRange(at, cur, "") + att := m.createAttachmentFromPath(p) + if att != nil { + m.textarea.InsertAttachment(att) m.textarea.InsertString(" ") - return m, nil + return m, util.CmdHandler(AttachmentInsertedMsg{}) } } } - text = strings.ReplaceAll(text, "\\", "") - text, err := strconv.Unquote(`"` + text + `"`) - if err != nil { - slog.Error("Failed to unquote text", "error", err) - text := string(msg) - if m.shouldSummarizePastedText(text) { - m.handleLongPaste(text) - } else { - m.textarea.InsertRunesFromUserInput([]rune(msg)) + // Case 3: plain path pasted (e.g., drag-and-drop) -> attach if image or PDF + { + p := filepath.Clean(text) + if m.pathExists(p) { + mime := getMediaTypeFromExtension(strings.ToLower(filepath.Ext(p))) + if strings.HasPrefix(mime, "image/") || mime == "application/pdf" { + if att := m.createAttachmentFromFile(p); att != nil { + m.textarea.InsertAttachment(att) + m.textarea.InsertString(" ") + return m, util.CmdHandler(AttachmentInsertedMsg{}) + } + } } - return m, nil - } - if _, err := os.Stat(text); err != nil { - slog.Error("Failed to paste file", "error", err) - text := string(msg) - if m.shouldSummarizePastedText(text) { - m.handleLongPaste(text) - } else { - m.textarea.InsertRunesFromUserInput([]rune(msg)) - } - return m, nil } - filePath := text - - attachment := m.createAttachmentFromFile(filePath) - if attachment == nil { - if m.shouldSummarizePastedText(text) { - m.handleLongPaste(text) - } else { - m.textarea.InsertRunesFromUserInput([]rune(msg)) - } + // Default: do not auto-convert. Insert raw text or summarize long pastes. + if m.shouldSummarizePastedText(textRaw) { + m.handleLongPaste(textRaw) return m, nil } - - m.textarea.InsertAttachment(attachment) - m.textarea.InsertString(" ") + m.textarea.InsertRunesFromUserInput([]rune(textRaw)) + return m, nil case tea.ClipboardMsg: text := string(msg) // Check if the pasted text is long and should be summarized @@ -233,7 +340,7 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if atIndex == -1 { // Should not happen, but as a fallback, just insert. m.textarea.InsertString(msg.Item.Value + " ") - return m, nil + return m, util.CmdHandler(AttachmentInsertedMsg{}) } // The range to replace is from the '@' up to the current cursor position. @@ -247,13 +354,13 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { attachment := m.createAttachmentFromPath(filePath) m.textarea.InsertAttachment(attachment) m.textarea.InsertString(" ") - return m, nil + return m, util.CmdHandler(AttachmentInsertedMsg{}) case "symbols": atIndex := m.textarea.LastRuneIndex('@') if atIndex == -1 { // Should not happen, but as a fallback, just insert. m.textarea.InsertString(msg.Item.Value + " ") - return m, nil + return m, util.CmdHandler(AttachmentInsertedMsg{}) } cursorCol := m.textarea.CursorColumn() @@ -287,13 +394,13 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } m.textarea.InsertAttachment(attachment) m.textarea.InsertString(" ") - return m, nil + return m, util.CmdHandler(AttachmentInsertedMsg{}) case "agents": atIndex := m.textarea.LastRuneIndex('@') if atIndex == -1 { // Should not happen, but as a fallback, just insert. m.textarea.InsertString(msg.Item.Value + " ") - return m, nil + return m, util.CmdHandler(AttachmentInsertedMsg{}) } cursorCol := m.textarea.CursorColumn() @@ -311,8 +418,7 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.textarea.InsertAttachment(attachment) m.textarea.InsertString(" ") - return m, nil - + return m, util.CmdHandler(AttachmentInsertedMsg{}) default: slog.Debug("Unknown provider", "provider", msg.Item.ProviderID) return m, nil diff --git a/packages/tui/internal/components/chat/editor_test.go b/packages/tui/internal/components/chat/editor_test.go new file mode 100644 index 000000000..f43a4078c --- /dev/null +++ b/packages/tui/internal/components/chat/editor_test.go @@ -0,0 +1,277 @@ +package chat + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/charmbracelet/bubbles/v2/spinner" + tea "github.com/charmbracelet/bubbletea/v2" + "github.com/sst/opencode/internal/app" + "github.com/sst/opencode/internal/completions" + "github.com/sst/opencode/internal/components/dialog" + "github.com/sst/opencode/internal/components/textarea" + "github.com/sst/opencode/internal/styles" +) + +func newTestEditor() *editorComponent { + m := &editorComponent{ + app: &app.App{}, + textarea: textarea.New(), + spinner: spinner.New(), + } + return m +} + +func TestPasteAtPathWithTrailingComma_PreservesPunctuation_NoDoubleSpace(t *testing.T) { + m := newTestEditor() + p := createTempTextFile(t, "", "pc.txt", "x") + + paste := "See @" + p + ", next" + _, cmd := m.Update(tea.PasteMsg(paste)) + if cmd == nil { + t.Fatalf("expected command to be returned for comma punctuation paste") + } + if _, ok := cmd().(AttachmentInsertedMsg); !ok { + t.Fatalf("expected AttachmentInsertedMsg for comma punctuation paste") + } + if len(m.textarea.GetAttachments()) != 1 { + t.Fatalf("expected 1 attachment, got %d", len(m.textarea.GetAttachments())) + } + v := m.Value() + if !strings.Contains(v, ", next") { + t.Fatalf("expected comma and following text to be preserved, got: %q", v) + } + if strings.Contains(v, ", next") { + t.Fatalf("did not expect double space after comma, got: %q", v) + } +} + +func TestPasteAtPathWithTrailingQuestion_PreservesPunctuation_NoDoubleSpace(t *testing.T) { + m := newTestEditor() + p := createTempTextFile(t, "", "pq.txt", "x") + + paste := "Check @" + p + "? Done" + _, cmd := m.Update(tea.PasteMsg(paste)) + if cmd == nil { + t.Fatalf("expected command to be returned for question punctuation paste") + } + if _, ok := cmd().(AttachmentInsertedMsg); !ok { + t.Fatalf("expected AttachmentInsertedMsg for question punctuation paste") + } + if len(m.textarea.GetAttachments()) != 1 { + t.Fatalf("expected 1 attachment, got %d", len(m.textarea.GetAttachments())) + } + v := m.Value() + if !strings.Contains(v, "? Done") { + t.Fatalf("expected question mark and following text to be preserved, got: %q", v) + } + if strings.Contains(v, "? Done") { + t.Fatalf("did not expect double space after question mark, got: %q", v) + } +} + +func TestPasteMultipleInlineAtPaths_AttachesEach(t *testing.T) { + m := newTestEditor() + dir := t.TempDir() + p1 := createTempTextFile(t, dir, "m1.txt", "one") + p2 := createTempTextFile(t, dir, "m2.txt", "two") + + // Build a paste with text around, two @paths, and punctuation after the first + paste := "Please check @" + p1 + ", and also @" + p2 + " thanks" + + _, cmd := m.Update(tea.PasteMsg(paste)) + if cmd == nil { + t.Fatalf("expected command to be returned for multi inline paste") + } + if _, ok := cmd().(AttachmentInsertedMsg); !ok { + t.Fatalf("expected AttachmentInsertedMsg for multi inline paste") + } + + atts := m.textarea.GetAttachments() + if len(atts) != 2 { + t.Fatalf("expected 2 attachments, got %d", len(atts)) + } + v := m.Value() + if !strings.Contains(v, "Please check") || !strings.Contains(v, "and also") || !strings.Contains(v, "thanks") { + t.Fatalf("expected surrounding text to be preserved, got: %q", v) + } +} + +func createTempTextFile(t *testing.T, dir, name, content string) string { + t.Helper() + if dir == "" { + td, err := os.MkdirTemp("", "editor-test-*") + if err != nil { + t.Fatalf("failed to make temp dir: %v", err) + } + dir = td + } + p := filepath.Join(dir, name) + if err := os.WriteFile(p, []byte(content), 0o600); err != nil { + t.Fatalf("failed to write temp file: %v", err) + } + abs, err := filepath.Abs(p) + if err != nil { + t.Fatalf("failed to get abs path: %v", err) + } + return abs +} + +func createTempBinFile(t *testing.T, dir, name string, data []byte) string { + t.Helper() + if dir == "" { + td, err := os.MkdirTemp("", "editor-test-*") + if err != nil { + t.Fatalf("failed to make temp dir: %v", err) + } + dir = td + } + p := filepath.Join(dir, name) + if err := os.WriteFile(p, data, 0o600); err != nil { + t.Fatalf("failed to write temp bin file: %v", err) + } + abs, err := filepath.Abs(p) + if err != nil { + t.Fatalf("failed to get abs path: %v", err) + } + return abs +} + +func TestPasteStartsWithAt_AttachesAndEmitsMsg(t *testing.T) { + m := newTestEditor() + p := createTempTextFile(t, "", "a.txt", "hello") + + _, cmd := m.Update(tea.PasteMsg("@" + p)) + if cmd == nil { + t.Fatalf("expected command to be returned") + } + msg := cmd() + if _, ok := msg.(AttachmentInsertedMsg); !ok { + t.Fatalf("expected AttachmentInsertedMsg, got %T", msg) + } + + atts := m.textarea.GetAttachments() + if len(atts) != 1 { + t.Fatalf("expected 1 attachment, got %d", len(atts)) + } + if v := m.Value(); !strings.HasSuffix(v, " ") { + t.Fatalf("expected trailing space after attachment, got value: %q", v) + } +} + +func TestPasteAfterAt_ReplacesAtWithAttachment(t *testing.T) { + m := newTestEditor() + p := createTempTextFile(t, "", "b.txt", "hello") + + m.textarea.SetValue("@") + // Cursor should be at the end after SetValue; paste absolute path + _, cmd := m.Update(tea.PasteMsg(p)) + if cmd == nil { + t.Fatalf("expected command to be returned") + } + if _, ok := cmd().(AttachmentInsertedMsg); !ok { + t.Fatalf("expected AttachmentInsertedMsg from paste after '@'") + } + + // Ensure the raw '@' rune was removed (attachment inserted in its place) + if m.textarea.LastRuneIndex('@') != -1 { + t.Fatalf("'@' rune should have been removed from the text slice") + } + if len(m.textarea.GetAttachments()) != 1 { + t.Fatalf("expected 1 attachment inserted") + } + if v := m.Value(); !strings.HasSuffix(v, " ") { + t.Fatalf("expected trailing space after attachment, got value: %q", v) + } +} + +func TestPlainTextPaste_NoAttachment_NoMsg(t *testing.T) { + m := newTestEditor() + _, cmd := m.Update(tea.PasteMsg("hello")) + if cmd != nil { + t.Fatalf("expected no command for plain text paste") + } + if got := m.Value(); got != "hello" { + t.Fatalf("expected value 'hello', got %q", got) + } + if len(m.textarea.GetAttachments()) != 0 { + t.Fatalf("expected no attachments for plain text paste") + } +} + +func TestPlainPathPng_AttachesImage(t *testing.T) { + m := newTestEditor() + // Minimal bytes; content isn't validated, extension determines mime + p := createTempBinFile(t, "", "img.png", []byte{0x89, 'P', 'N', 'G'}) + + _, cmd := m.Update(tea.PasteMsg(p)) + if cmd == nil { + t.Fatalf("expected command to be returned for image path paste") + } + if _, ok := cmd().(AttachmentInsertedMsg); !ok { + t.Fatalf("expected AttachmentInsertedMsg for image path paste") + } + atts := m.textarea.GetAttachments() + if len(atts) != 1 { + t.Fatalf("expected 1 attachment, got %d", len(atts)) + } + if atts[0].MediaType != "image/png" { + t.Fatalf("expected image/png mime, got %q", atts[0].MediaType) + } + if v := m.Value(); !strings.HasSuffix(v, " ") { + t.Fatalf("expected trailing space after attachment, got value: %q", v) + } +} + +func TestPlainPathPdf_AttachesPDF(t *testing.T) { + m := newTestEditor() + p := createTempBinFile(t, "", "doc.pdf", []byte("%PDF-1.4")) + + _, cmd := m.Update(tea.PasteMsg(p)) + if cmd == nil { + t.Fatalf("expected command to be returned for pdf path paste") + } + if _, ok := cmd().(AttachmentInsertedMsg); !ok { + t.Fatalf("expected AttachmentInsertedMsg for pdf path paste") + } + atts := m.textarea.GetAttachments() + if len(atts) != 1 { + t.Fatalf("expected 1 attachment, got %d", len(atts)) + } + if atts[0].MediaType != "application/pdf" { + t.Fatalf("expected application/pdf mime, got %q", atts[0].MediaType) + } + if v := m.Value(); !strings.HasSuffix(v, " ") { + t.Fatalf("expected trailing space after attachment, got value: %q", v) + } +} + +func TestCompletionFiles_InsertsAttachment_EmitsMsg(t *testing.T) { + m := newTestEditor() + p := createTempTextFile(t, "", "c.txt", "hello") + m.textarea.SetValue("@") + + item := completions.CompletionSuggestion{ + ProviderID: "files", + Value: p, + Display: func(_ styles.Style) string { return p }, + } + // Build the completion selected message as if the user selected from the dialog + msg := dialog.CompletionSelectedMsg{Item: item, SearchString: "@"} + + _, cmd := m.Update(msg) + if cmd == nil { + t.Fatalf("expected command to be returned") + } + if _, ok := cmd().(AttachmentInsertedMsg); !ok { + t.Fatalf("expected AttachmentInsertedMsg from files completion selection") + } + if len(m.textarea.GetAttachments()) != 1 { + t.Fatalf("expected 1 attachment inserted from completion selection") + } + if v := m.Value(); !strings.HasSuffix(v, " ") { + t.Fatalf("expected trailing space after attachment, got value: %q", v) + } +} diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 22e8afdb3..5f178e15a 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -382,6 +382,9 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { a.app.Messages = []app.Message{} case dialog.CompletionDialogCloseMsg: a.showCompletionDialog = false + case chat.AttachmentInsertedMsg: + // Close completion dialog when the editor inserts an attachment + a.showCompletionDialog = false case opencode.EventListResponseEventInstallationUpdated: return a, toast.NewSuccessToast( "opencode updated to "+msg.Properties.Version+", restart to apply.", From 7446f5ad7b4c27ff1fa201bc48a67178ff10e7b7 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 9 Aug 2025 01:28:27 -0400 Subject: [PATCH 026/178] wip gateway --- cloud/function/src/auth.ts | 58 +++++++++++++++++- cloud/function/sst-env.d.ts | 4 ++ cloud/web/src/pages/index.tsx | 5 +- cloud/web/src/pages/lander.module.css | 86 --------------------------- infra/cloud.ts | 3 +- packages/function/sst-env.d.ts | 4 ++ sst-env.d.ts | 4 ++ 7 files changed, 75 insertions(+), 89 deletions(-) diff --git a/cloud/function/src/auth.ts b/cloud/function/src/auth.ts index 5eacb7a72..fe31651e5 100644 --- a/cloud/function/src/auth.ts +++ b/cloud/function/src/auth.ts @@ -2,7 +2,9 @@ import { Resource } from "sst" import { z } from "zod" import { issuer } from "@openauthjs/openauth" import { createSubjects } from "@openauthjs/openauth/subject" +import { CodeProvider } from "@openauthjs/openauth/provider/code" import { GithubProvider } from "@openauthjs/openauth/provider/github" +import { GoogleOidcProvider } from "@openauthjs/openauth/provider/google" import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare" import { Account } from "@opencode/cloud-core/account.js" @@ -30,6 +32,53 @@ export default { clientSecret: Resource.GITHUB_CLIENT_SECRET_CONSOLE.value, scopes: ["read:user", "user:email"], }), + google: GoogleOidcProvider({ + clientID: Resource.GOOGLE_CLIENT_ID.value, + scopes: ["openid", "email"], + }), + // email: CodeProvider({ + // async request(req, state, form, error) { + // console.log(state) + // const params = new URLSearchParams() + // if (error) { + // params.set("error", error.type) + // } + // if (state.type === "start") { + // return Response.redirect(process.env.AUTH_FRONTEND_URL + "/auth/email?" + params.toString(), 302) + // } + // + // if (state.type === "code") { + // return Response.redirect(process.env.AUTH_FRONTEND_URL + "/auth/code?" + params.toString(), 302) + // } + // + // return new Response("ok") + // }, + // async sendCode(claims, code) { + // const email = z.string().email().parse(claims.email) + // const cmd = new SendEmailCommand({ + // Destination: { + // ToAddresses: [email], + // }, + // FromEmailAddress: `SST `, + // Content: { + // Simple: { + // Body: { + // Html: { + // Data: `Your pin code is ${code}`, + // }, + // Text: { + // Data: `Your pin code is ${code}`, + // }, + // }, + // Subject: { + // Data: "SST Console Pin Code: " + code, + // }, + // }, + // }, + // }) + // await ses.send(cmd) + // }, + // }), }, storage: CloudflareStorage({ namespace: env.AuthStorage, @@ -50,7 +99,14 @@ export default { }) const user = (await userResponse.json()) as { email: string } email = user.email - } else throw new Error("Unsupported provider") + } else if (response.provider === "google") { + if (!response.id.email_verified) throw new Error("Google email not verified") + email = response.id.email as string + } + //if (response.provider === "email") { + // email = response.claims.email + //} + else throw new Error("Unsupported provider") if (!email) throw new Error("No email found") diff --git a/cloud/function/sst-env.d.ts b/cloud/function/sst-env.d.ts index 4e2b15921..520a033ff 100644 --- a/cloud/function/sst-env.d.ts +++ b/cloud/function/sst-env.d.ts @@ -50,6 +50,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "GOOGLE_CLIENT_ID": { + "type": "sst.sst.Secret" + "value": string + } "OPENAI_API_KEY": { "type": "sst.sst.Secret" "value": string diff --git a/cloud/web/src/pages/index.tsx b/cloud/web/src/pages/index.tsx index 903a3afd9..116ed156c 100644 --- a/cloud/web/src/pages/index.tsx +++ b/cloud/web/src/pages/index.tsx @@ -24,9 +24,12 @@ export default function Index() {
-
+
auth.authorize({ provider: "github" })}>Sign in with GitHub
+
+ auth.authorize({ provider: "google" })}>Sign in with Google +
diff --git a/cloud/web/src/pages/lander.module.css b/cloud/web/src/pages/lander.module.css index b66ed5fa7..251e243fb 100644 --- a/cloud/web/src/pages/lander.module.css +++ b/cloud/web/src/pages/lander.module.css @@ -80,90 +80,4 @@ border-left: 2px solid var(--color-border); } } - - [data-slot="images"] { - display: flex; - flex-direction: row; - align-items: stretch; - justify-content: space-between; - border-top: 2px solid var(--color-border); - - & > div { - flex: 1; - display: flex; - flex-direction: column; - gap: calc(var(--padding) / 4); - padding: calc(var(--padding) / 2); - border-width: 0; - border-style: solid; - border-color: var(--color-border); - - & > div, a { - flex: 1; - display: flex; - align-items: center; - } - } - - p { - letter-spacing: -0.03125rem; - text-transform: uppercase; - color: var(--color-text-dimmed); - } - - & > div + div { - border-width: 0 0 0 2px; - } - - @media (max-width: 30rem) { - & { - flex-direction: column; - } - & > div + div { - border-width: 2px 0 0 0; - } - } - } - - [data-slot="content"] { - border-top: 2px solid var(--color-border); - padding: var(--padding); - - & > p { - line-height: var(--font-line-height); - } - - ol { - margin-top: calc(var(--vertical-padding) / 2); - padding-left: 2.5rem; - list-style-type: decimal; - line-height: var(--font-line-height); - - & > li + li { - margin-top: calc(var(--vertical-padding) / 2); - } - - & > li b { - text-transform: uppercase; - } - } - - } - - [data-slot="footer"] { - border-top: 2px solid var(--color-border); - display: flex; - flex-direction: row; - - & > div { - flex: 1; - text-align: center; - text-transform: uppercase; - padding: calc(var(--padding) / 2) 0.5rem; - } - - & > div + div { - border-left: 2px solid var(--color-border); - } - } } diff --git a/infra/cloud.ts b/infra/cloud.ts index 4625bb282..1d93d58da 100644 --- a/infra/cloud.ts +++ b/infra/cloud.ts @@ -56,12 +56,13 @@ new sst.x.DevCommand("Studio", { const GITHUB_CLIENT_ID_CONSOLE = new sst.Secret("GITHUB_CLIENT_ID_CONSOLE") const GITHUB_CLIENT_SECRET_CONSOLE = new sst.Secret("GITHUB_CLIENT_SECRET_CONSOLE") +const GOOGLE_CLIENT_ID = new sst.Secret("GOOGLE_CLIENT_ID") const authStorage = new sst.cloudflare.Kv("AuthStorage") export const auth = new sst.cloudflare.Worker("AuthApi", { domain: `auth.${domain}`, handler: "cloud/function/src/auth.ts", url: true, - link: [database, authStorage, GITHUB_CLIENT_ID_CONSOLE, GITHUB_CLIENT_SECRET_CONSOLE], + link: [database, authStorage, GITHUB_CLIENT_ID_CONSOLE, GITHUB_CLIENT_SECRET_CONSOLE, GOOGLE_CLIENT_ID], }) const ANTHROPIC_API_KEY = new sst.Secret("ANTHROPIC_API_KEY") diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index 4e2b15921..520a033ff 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -50,6 +50,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "GOOGLE_CLIENT_ID": { + "type": "sst.sst.Secret" + "value": string + } "OPENAI_API_KEY": { "type": "sst.sst.Secret" "value": string diff --git a/sst-env.d.ts b/sst-env.d.ts index ffc049c93..3af3f82c0 100644 --- a/sst-env.d.ts +++ b/sst-env.d.ts @@ -63,6 +63,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "GOOGLE_CLIENT_ID": { + "type": "sst.sst.Secret" + "value": string + } "GatewayApi": { "type": "sst.cloudflare.Worker" "url": string From 8ba35eadd47fe9f683ab1d43f6de5d3573c81d8d Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 9 Aug 2025 12:04:16 +0000 Subject: [PATCH 027/178] ignore: update download stats 2025-08-09 --- STATS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/STATS.md b/STATS.md index 0101a72c6..54adf4c7d 100644 --- a/STATS.md +++ b/STATS.md @@ -42,3 +42,4 @@ | 2025-08-06 | 147,067 (+5,471) | 158,309 (+2,557) | 305,376 (+8,028) | | 2025-08-07 | 152,591 (+5,524) | 160,889 (+2,580) | 313,480 (+8,104) | | 2025-08-08 | 158,187 (+5,596) | 163,448 (+2,559) | 321,635 (+8,155) | +| 2025-08-09 | 162,770 (+4,583) | 165,721 (+2,273) | 328,491 (+6,856) | From e2fac991dc292053e835fc1dd7c25aaf025240f1 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sat, 9 Aug 2025 11:03:33 -0500 Subject: [PATCH 028/178] better permissions ux when denying (#1747) --- packages/opencode/src/session/index.ts | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 494a39c4d..c02e7ce02 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -11,7 +11,6 @@ import { type LanguageModelUsage, type ProviderMetadata, type ModelMessage, - stepCountIs, type StreamTextResult, } from "ai" @@ -41,6 +40,7 @@ import { mergeDeep, pipe, splitWhen } from "remeda" import { ToolRegistry } from "../tool/registry" import { Plugin } from "../plugin" import { Agent } from "../agent/agent" +import { Permission } from "../permission" export namespace Session { const log = Log.create({ service: "session" }) @@ -932,7 +932,18 @@ export namespace Session { activeTools: Object.keys(tools).filter((x) => x !== "invalid"), maxOutputTokens: outputLimit, abortSignal: abort.signal, - stopWhen: stepCountIs(1000), + stopWhen: async ({ steps }) => { + if (steps.length >= 1000) { + return true + } + + // Check if processor flagged that we should stop + if (processor.getShouldStop()) { + return true + } + + return false + }, providerOptions: { [input.providerID]: { ...ProviderTransform.options(input.providerID, input.modelID), @@ -983,10 +994,14 @@ export namespace Session { function createProcessor(assistantMsg: MessageV2.Assistant, model: ModelsDev.Model) { const toolcalls: Record = {} let snapshot: string | undefined + let shouldStop = false return { partFromToolCall(toolCallID: string) { return toolcalls[toolCallID] }, + getShouldStop() { + return shouldStop + }, async process(stream: StreamTextResult, never>) { try { let currentText: MessageV2.TextPart | undefined @@ -1063,6 +1078,9 @@ export namespace Session { case "tool-error": { const match = toolcalls[value.toolCallId] if (match && match.state.status === "running") { + if (value.error instanceof Permission.RejectedError) { + shouldStop = true + } await updatePart({ ...match, state: { @@ -1079,7 +1097,6 @@ export namespace Session { } break } - case "error": throw value.error From 1954b59167d0c743efa2fb3a0a47e57a025b4221 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sat, 9 Aug 2025 11:04:58 -0500 Subject: [PATCH 029/178] feat: eslint lsp (#1744) --- packages/opencode/src/lsp/server.ts | 60 ++++++++++++++++++++++ packages/opencode/src/tool/edit.ts | 1 + packages/web/src/content/docs/docs/lsp.mdx | 7 +-- 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index f4648f0c2..cf78fe7bf 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -65,6 +65,66 @@ export namespace LSPServer { }, } + export const ESLint: Info = { + id: "eslint", + root: NearestRoot([ + "eslint.config.js", + "eslint.config.mjs", + "eslint.config.cjs", + "eslint.config.ts", + "eslint.config.mts", + "eslint.config.cts", + ".eslintrc.js", + ".eslintrc.cjs", + ".eslintrc.yaml", + ".eslintrc.yml", + ".eslintrc.json", + "package.json", + ]), + extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"], + async spawn(app, root) { + const eslint = await Bun.resolve("eslint", app.path.cwd).catch(() => {}) + if (!eslint) return + const serverPath = path.join(Global.Path.bin, "vscode-eslint", "server", "out", "eslintServer.js") + if (!(await Bun.file(serverPath).exists())) { + log.info("downloading and building VS Code ESLint server") + const response = await fetch("https://github.com/microsoft/vscode-eslint/archive/refs/heads/main.zip") + if (!response.ok) return + + const zipPath = path.join(Global.Path.bin, "vscode-eslint.zip") + await Bun.file(zipPath).write(response) + + await $`unzip -o -q ${zipPath}`.cwd(Global.Path.bin).nothrow() + await fs.rm(zipPath, { force: true }) + + const extractedPath = path.join(Global.Path.bin, "vscode-eslint-main") + const finalPath = path.join(Global.Path.bin, "vscode-eslint") + + if (await Bun.file(finalPath).exists()) { + await fs.rm(finalPath, { force: true, recursive: true }) + } + await fs.rename(extractedPath, finalPath) + + await $`npm install`.cwd(finalPath).quiet() + await $`npm run compile`.cwd(finalPath).quiet() + + log.info("installed VS Code ESLint server", { serverPath }) + } + + const proc = spawn(BunProc.which(), ["--max-old-space-size=8192", serverPath, "--stdio"], { + cwd: root, + env: { + ...process.env, + BUN_BE_BUN: "1", + }, + }) + + return { + process: proc, + } + }, + } + export const Gopls: Info = { id: "golang", root: async (file, app) => { diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts index fbda9e4d1..d3883b539 100644 --- a/packages/opencode/src/tool/edit.ts +++ b/packages/opencode/src/tool/edit.ts @@ -111,6 +111,7 @@ export const EditTool = Tool.define("edit", { continue } output += `\n\n${file}\n${issues + // TODO: may want to make more leniant for eslint .filter((item) => item.severity === 1) .map(LSP.Diagnostic.pretty) .join("\n")}\n\n` diff --git a/packages/web/src/content/docs/docs/lsp.mdx b/packages/web/src/content/docs/docs/lsp.mdx index 4783683bb..76edad3ee 100644 --- a/packages/web/src/content/docs/docs/lsp.mdx +++ b/packages/web/src/content/docs/docs/lsp.mdx @@ -11,9 +11,10 @@ opencode integrates with your Language Server Protocol (LSP) to help the LLM int opencode comes with several built-in LSP servers for popular languages: -| LSP Server | Extensions | Requirements | +| LSP Server | Extensions | Requirements | | ---------- | -------------------------------------------- | ----------------------------------- | | typescript | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | `typescript` dependency in project | +| eslint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | `eslint` dependency in project | | gopls | .go | `go` command available | | ruby-lsp | .rb, .rake, .gemspec, .ru | `ruby` and `gem` commands available | | pyright | .py, .pyi | `pyright` dependency installed | @@ -41,7 +42,7 @@ You can customize LSP servers through the `lsp` section in your opencode config. ```json title="opencode.json" { "$schema": "https://opencode.ai/config.json", - "lsp": { } + "lsp": {} } ``` @@ -49,7 +50,7 @@ Each LSP server supports the following: | Property | Type | Description | | ---------------- | -------- | ------------------------------------------------- | -| `disabled` | boolean | Set this to `true` to disable the LSP server | +| `disabled` | boolean | Set this to `true` to disable the LSP server | | `command` | string[] | The command to start the LSP server | | `extensions` | string[] | File extensions this LSP server should handle | | `env` | object | Environment variables to set when starting server | From d3ff66e911b0df6ca57cde00c1da2c81031c6422 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sat, 9 Aug 2025 15:38:33 -0400 Subject: [PATCH 030/178] use minimal reasoning effort for gpt-5 --- packages/opencode/src/global/index.ts | 2 +- packages/opencode/src/provider/provider.ts | 2 +- packages/opencode/src/provider/transform.ts | 2 +- packages/opencode/src/session/index.ts | 10 + .../opencode/src/session/prompt/codex.txt | 263 ++++++++++++++++++ 5 files changed, 276 insertions(+), 3 deletions(-) create mode 100644 packages/opencode/src/session/prompt/codex.txt diff --git a/packages/opencode/src/global/index.ts b/packages/opencode/src/global/index.ts index ca83b4695..4c3bc05be 100644 --- a/packages/opencode/src/global/index.ts +++ b/packages/opencode/src/global/index.ts @@ -27,7 +27,7 @@ await Promise.all([ fs.mkdir(Global.Path.log, { recursive: true }), ]) -const CACHE_VERSION = "5" +const CACHE_VERSION = "6" const version = await Bun.file(path.join(Global.Path.cache, "version")) .text() diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 6c8273c33..184b938da 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -440,7 +440,7 @@ export namespace Provider { const provider = await state().then((state) => state.providers[providerID]) if (!provider) return - const priority = ["3-5-haiku", "3.5-haiku", "gemini-2.5-flash"] + const priority = ["3-5-haiku", "3.5-haiku", "gemini-2.5-flash", "gpt-5-nano"] for (const item of priority) { for (const model of Object.keys(provider.info.models)) { if (model.includes(item)) return getModel(providerID, model) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 9bebda346..91a2777b8 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -85,7 +85,7 @@ export namespace ProviderTransform { export function options(_providerID: string, modelID: string) { if (modelID.includes("gpt-5")) { return { - reasoningEffort: "low", + reasoningEffort: "minimal", textVerbosity: "low", } } diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index c02e7ce02..218f7ba88 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -682,6 +682,7 @@ export namespace Session { providerOptions: { [input.providerID]: { ...small.info.options, + ...ProviderTransform.options(input.providerID, small.info.id), }, }, messages: [ @@ -1014,6 +1015,15 @@ export namespace Session { case "start": break + case "reasoning-start": + break + + case "reasoning-delta": + break + + case "reasoning-end": + break + case "tool-input-start": const part = await updatePart({ id: toolcalls[value.id]?.id ?? Identifier.ascending("part"), diff --git a/packages/opencode/src/session/prompt/codex.txt b/packages/opencode/src/session/prompt/codex.txt new file mode 100644 index 000000000..9cd3f8dcb --- /dev/null +++ b/packages/opencode/src/session/prompt/codex.txt @@ -0,0 +1,263 @@ +You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful. + +Your capabilities: +- Receive user prompts and other context provided by the harness, such as files in the workspace. +- Communicate with the user by streaming thinking & responses, and by making & updating plans. +- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the "Sandbox and approvals" section. + +Within this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI). + +# How you work + +## Personality + +Your default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work. + +## Responsiveness + +### Preamble messages + +Before making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples: + +- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each. +- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates). +- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions. +- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging. + +**Examples:** +- “I’ve explored the repo; now checking the API route definitions.” +- “Next, I’ll edit the config and update the related tests.” +- “I’m about to scaffold the CLI commands and helper functions.” +- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.” +- “Config’s looking tidy. Next up is editing helpers to keep things in sync.” +- “Finished poking at the DB gateway. I will now chase down error handling.” +- “Alright, build pipeline order is interesting. Checking how it reports failures.” +- “Spotted a clever caching util; now hunting where it gets used.” + +**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action. +- Jumping straight into tool calls without explaining what’s about to happen. +- Writing overly long or speculative preambles — focus on immediate, tangible next steps. + +## Planning + +You have access to an `todowrite` tool which tracks steps and progress and +renders them to the user. Using the tool helps demonstrate that you've +understood the task and convey how you're approaching it. Plans can help to make +complex, ambiguous, or multi-phase work clearer and more collaborative for the +user. A good plan should break the task into meaningful, logically ordered steps +that are easy to verify as you go. Note that plans are not for padding out +simple work with filler steps or stating the obvious. Do not repeat the full +contents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step. + +Use a plan when: +- The task is non-trivial and will require multiple actions over a long time horizon. +- There are logical phases or dependencies where sequencing matters. +- The work has ambiguity that benefits from outlining high-level goals. +- You want intermediate checkpoints for feedback and validation. +- When the user asked you to do more than one thing in a single prompt +- The user has asked you to use the plan tool (aka "TODOs") +- You generate additional steps while working, and plan to do them before yielding to the user + +Skip a plan when: +- The task is simple and direct. +- Breaking it down would only produce literal or trivial steps. + +Planning steps are called "steps" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like "Write the API spec", then "Update the backend", then "Implement the frontend". On the other hand, it's obvious that you'll usually have to "Explore the codebase" or "Implement the changes", so those are not worth tracking in your plan. + +It may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately. + +### Examples + +**High-quality plans** + +Example 1: + +1. Add CLI entry with file args +2. Parse Markdown via CommonMark library +3. Apply semantic HTML template +4. Handle code blocks, images, links +5. Add error handling for invalid files + +Example 2: + +1. Define CSS variables for colors +2. Add toggle with localStorage state +3. Refactor components to use variables +4. Verify all views for readability +5. Add smooth theme-change transition + +Example 3: + +1. Set up Node.js + WebSocket server +2. Add join/leave broadcast events +3. Implement messaging with timestamps +4. Add usernames + mention highlighting +5. Persist messages in lightweight DB +6. Add typing indicators + unread count + +**Low-quality plans** + +Example 1: + +1. Create CLI tool +2. Add Markdown parser +3. Convert to HTML + +Example 2: + +1. Add dark mode toggle +2. Save preference +3. Make styles look good + +Example 3: + +1. Create single-file HTML game +2. Run quick sanity check +3. Summarize usage instructions + +If you need to write a plan, only write high quality plans, not low quality ones. + +## Task execution + +You are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer. + +You MUST adhere to the following criteria when solving queries: +- Working on the repo(s) in the current environment is allowed, even if they are proprietary. +- Analyzing code for vulnerabilities is allowed. +- Showing user code and tool call details is allowed. +- Use the `edit` tool to edit files + +If completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines: + +- Fix the problem at the root cause rather than applying surface-level edits, when possible. +- Avoid unneeded complexity in your solution. +- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.) +- Update documentation as necessary. +- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task. +- Use `git log` and `git blame` to search the history of the codebase if additional context is required. +- NEVER add copyright or license headers unless specifically requested. +- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc. +- Do not `git commit` your changes or create new git branches unless explicitly requested. +- Do not add inline comments within code unless explicitly requested. +- Do not use one-letter variable names unless explicitly requested. +- NEVER output inline citations like "【F:README.md†L5-L14】" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor. + +## Testing your work + +If the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so. + +Once you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one. + +For all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.) + +## Sandbox and approvals + +The Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from. + +Filesystem sandboxing prevents you from editing files without user approval. The options are: +- *read-only*: You can only read files. +- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it. +- *danger-full-access*: No filesystem sandboxing. + +Network sandboxing prevents you from accessing network without approval. Options are +- *ON* +- *OFF* + +Approvals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are +- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe "read" commands. +- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox. +- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.) +- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding. + +When you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval: +- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp) +- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files. +- You are running sandboxed and need to run a command that requires network access (e.g. installing packages) +- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval. +- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for +- (For all of these, you should weigh alternative paths that do not require approval.) + +Note that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read. + +You will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure. + +## Ambition vs. precision + +For tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation. + +If you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature. + +You should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified. + +## Sharing progress updates + +For especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next. + +Before doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why. + +The messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along. + +## Presenting your work and final message + +Your final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges. + +You can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation. + +The user is working on the same computer as you, and has access to your work. As +such there's no need to show the full contents of large files you have already +written unless the user explicitly asks for them. Similarly, if you've created +or modified files using `edit`, there's no need to tell users to "save the file" or "copy the code into a file"—just reference the file path. + +If there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly. + +Brevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding. + +### Final answer structure and style guidelines + +You are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value. + +**Section Headers** +- Use only when they improve clarity — they are not mandatory for every answer. +- Choose descriptive names that fit the content +- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**` +- Leave no blank line before the first bullet under a header. +- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer. + +**Bullets** +- Use `-` followed by a space for every bullet. +- Bold the keyword, then colon + concise description. +- Merge related points when possible; avoid a bullet for every trivial detail. +- Keep bullets to one line unless breaking for clarity is unavoidable. +- Group into short lists (4–6 bullets) ordered by importance. +- Use consistent keyword phrasing and formatting across sections. + +**Monospace** +- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``). +- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command. +- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``). + +**Structure** +- Place related bullets together; don’t mix unrelated concepts in the same section. +- Order sections from general → specific → supporting info. +- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it. +- Match structure to complexity: + - Multi-part or detailed results → use clear headers and grouped bullets. + - Simple results → minimal headers, possibly just a short list or paragraph. + +**Tone** +- Keep the voice collaborative and natural, like a coding partner handing off work. +- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition +- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”). +- Keep descriptions self-contained; don’t refer to “above” or “below”. +- Use parallel structure in lists for consistency. + +**Don’t** +- Don’t use literal words “bold” or “monospace” in the content. +- Don’t nest bullets or create deep hierarchies. +- Don’t output ANSI escape codes directly — the CLI renderer applies them. +- Don’t cram unrelated keywords into a single bullet; split for clarity. +- Don’t let keyword lists run long — wrap or reformat for scanability. + +Generally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable. + +For casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting. From 696ab1a752e5bfa520379e21d933be1cd3058ce0 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 9 Aug 2025 19:22:50 -0400 Subject: [PATCH 031/178] Update moonshot ai provider doc --- .../web/src/content/docs/docs/providers.mdx | 46 +++---------------- 1 file changed, 6 insertions(+), 40 deletions(-) diff --git a/packages/web/src/content/docs/docs/providers.mdx b/packages/web/src/content/docs/docs/providers.mdx index cad9878ea..c6455b1d8 100644 --- a/packages/web/src/content/docs/docs/providers.mdx +++ b/packages/web/src/content/docs/docs/providers.mdx @@ -546,7 +546,7 @@ To use Kimi K2 from Moonshot AI: 1. Head over to the [Moonshot AI console](https://platform.moonshot.ai/console), create an account, and click **Create API key**. -2. Run `opencode auth login` and select **Other**. +2. Run `opencode auth login` and select **Moonshot AI**. ```bash $ opencode auth login @@ -555,11 +555,11 @@ To use Kimi K2 from Moonshot AI: │ ◆ Select provider │ ... - │ ● Other + │ ● Moonshot AI └ ``` -3. Enter `moonshot` as the provider ID. +3. Enter your Moonshot API key. ```bash $ opencode auth login @@ -567,48 +567,14 @@ To use Kimi K2 from Moonshot AI: ┌ Add credential │ ◇ Select provider - │ Other - │ - ◇ Enter provider id - │ moonshot - └ - ``` - -4. Enter your Moonshot API key. - - ```bash - $ opencode auth login - - ┌ Add credential + │ Moonshot AI │ ◇ Enter your API key - │ sk-... + │ _ └ ``` -5. Configure Moonshot in your opencode config. - - ```json title="opencode.json" ""moonshot"" {5-15} - { - "$schema": "https://opencode.ai/config.json", - "provider": { - "moonshot": { - "npm": "@ai-sdk/openai-compatible", - "name": "Moonshot AI", - "options": { - "baseURL": "https://api.moonshot.ai/v1" - }, - "models": { - "kimi-k2-0711-preview": { - "name": "Kimi K2" - } - } - } - } - } - ``` - -6. Run the `/models` command to select _Kimi K2_. +4. Run the `/models` command to select _Kimi K2_. --- From bd4319f2bc28c0ad16946107e7d1d50fa924527e Mon Sep 17 00:00:00 2001 From: spoons-and-mirrors <212802214+spoons-and-mirrors@users.noreply.github.com> Date: Sun, 10 Aug 2025 03:22:16 +0200 Subject: [PATCH 032/178] Feat: Add Agent Name in the LLM Response Footer (and re-order it) (#1770) --- .../tui/internal/components/chat/message.go | 32 +++++++++++++++++-- .../tui/internal/components/status/status.go | 30 ++++------------- packages/tui/internal/util/color.go | 24 +++++++++++++- packages/web/src/components/share/part.tsx | 26 ++++++++++++--- 4 files changed, 81 insertions(+), 31 deletions(-) diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index 6dcdcc989..5f4dc9612 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -324,9 +324,37 @@ func renderText( if time.Now().Format("02 Jan 2006") == timestamp[:11] { timestamp = timestamp[12:] } - info := fmt.Sprintf("%s (%s)", author, timestamp) - info = styles.NewStyle().Foreground(t.TextMuted()).Render(info) + // Check if this is an assistant message with mode (agent) information + var modelAndAgentSuffix string + if assistantMsg, ok := message.(opencode.AssistantMessage); ok && assistantMsg.Mode != "" { + // Find the agent index by name to get the correct color + var agentIndex int + for i, agent := range app.Agents { + if agent.Name == assistantMsg.Mode { + agentIndex = i + break + } + } + + // Get agent color based on the original agent index (same as status bar) + agentColor := util.GetAgentColor(agentIndex) + + // Style the agent name with the same color as status bar + agentName := strings.Title(assistantMsg.Mode) + styledAgentName := styles.NewStyle().Foreground(agentColor).Bold(true).Render(agentName) + modelAndAgentSuffix = fmt.Sprintf(" | %s | %s", assistantMsg.ModelID, styledAgentName) + } + + var info string + if modelAndAgentSuffix != "" { + // For assistant messages: "timestamp | modelID | agentName" + info = fmt.Sprintf("%s%s", timestamp, modelAndAgentSuffix) + } else { + // For user messages: "author (timestamp)" + info = fmt.Sprintf("%s (%s)", author, timestamp) + } + info = styles.NewStyle().Foreground(t.TextMuted()).Render(info) if !showToolDetails && toolCalls != nil && len(toolCalls) > 0 { content = content + "\n\n" for _, toolCall := range toolCalls { diff --git a/packages/tui/internal/components/status/status.go b/packages/tui/internal/components/status/status.go index be34dd938..1634bd6c5 100644 --- a/packages/tui/internal/components/status/status.go +++ b/packages/tui/internal/components/status/status.go @@ -121,30 +121,14 @@ func (m *statusComponent) View() string { var modeBackground compat.AdaptiveColor var modeForeground compat.AdaptiveColor - switch m.app.AgentIndex { - case 0: + + agentColor := util.GetAgentColor(m.app.AgentIndex) + + if m.app.AgentIndex == 0 { modeBackground = t.BackgroundElement() - modeForeground = t.TextMuted() - case 1: - modeBackground = t.Secondary() - modeForeground = t.BackgroundPanel() - case 2: - modeBackground = t.Accent() - modeForeground = t.BackgroundPanel() - case 3: - modeBackground = t.Success() - modeForeground = t.BackgroundPanel() - case 4: - modeBackground = t.Warning() - modeForeground = t.BackgroundPanel() - case 5: - modeBackground = t.Primary() - modeForeground = t.BackgroundPanel() - case 6: - modeBackground = t.Error() - modeForeground = t.BackgroundPanel() - default: - modeBackground = t.Secondary() + modeForeground = agentColor + } else { + modeBackground = agentColor modeForeground = t.BackgroundPanel() } diff --git a/packages/tui/internal/util/color.go b/packages/tui/internal/util/color.go index f0d73bcb2..b387ca655 100644 --- a/packages/tui/internal/util/color.go +++ b/packages/tui/internal/util/color.go @@ -3,6 +3,9 @@ package util import ( "regexp" "strings" + + "github.com/charmbracelet/lipgloss/v2/compat" + "github.com/sst/opencode/internal/theme" ) var csiRE *regexp.Regexp @@ -89,5 +92,24 @@ func ConvertRGBToAnsi16Colors(s string) string { // func looksLikeByte(tok string) bool { // v, err := strconv.Atoi(tok) -// return err == nil && v >= 0 && v <= 255 +// return err == nil && v >= 0 && v <= 255 // } + +// GetAgentColor returns the color for a given agent index, matching the status bar colors +func GetAgentColor(agentIndex int) compat.AdaptiveColor { + t := theme.CurrentTheme() + agentColors := []compat.AdaptiveColor{ + t.TextMuted(), + t.Secondary(), + t.Accent(), + t.Success(), + t.Warning(), + t.Primary(), + t.Error(), + } + + if agentIndex >= 0 && agentIndex < len(agentColors) { + return agentColors[agentIndex] + } + return t.Secondary() // default fallback +} diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index 4a9320e6d..772a80dc6 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -144,7 +144,15 @@ export function Part(props: PartProps) { DateTime.DATETIME_FULL_WITH_SECONDS, )} > - {DateTime.fromMillis(props.message.time.completed).toLocaleString(DateTime.DATETIME_MED)} + {DateTime.fromMillis(props.message.time.completed || props.message.time.created).toLocaleString( + DateTime.DATETIME_MED, + )} + {` | ${props.message.modelID}`} + {props.message.mode && ( + + {` | ${props.message.mode}`} + + )} )} @@ -158,7 +166,17 @@ export function Part(props: PartProps) { {props.part.type === "step-start" && props.message.role === "assistant" && (
{props.message.providerID}
-
{props.message.modelID}
+
+ {DateTime.fromMillis(props.message.time.completed || props.message.time.created).toLocaleString( + DateTime.DATETIME_MED, + )} + {` | ${props.message.modelID}`} + {props.message.mode && ( + + {` | ${props.message.mode}`} + + )} +
)} {props.part.type === "tool" && props.part.state.status === "error" && ( @@ -653,9 +671,7 @@ function TaskTool(props: ToolProps) { Task {props.state.input.description} -
- “{props.state.input.prompt}” -
+
“{props.state.input.prompt}”
From 34ac0e895d48da28ab864b9dd42a9b546405121f Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 10 Aug 2025 00:28:59 -0400 Subject: [PATCH 033/178] wip: gateway --- cloud/function/src/gateway.ts | 98 ++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/cloud/function/src/gateway.ts b/cloud/function/src/gateway.ts index aefea7ac4..50625ee74 100644 --- a/cloud/function/src/gateway.ts +++ b/cloud/function/src/gateway.ts @@ -4,11 +4,11 @@ import { cors } from "hono/cors" import { HTTPException } from "hono/http-exception" import { zValidator } from "@hono/zod-validator" import { Resource } from "sst" -import { generateText, streamText } from "ai" +import { type ProviderMetadata, type LanguageModelUsage, generateText, streamText, Tool } from "ai" import { createAnthropic } from "@ai-sdk/anthropic" import { createOpenAI } from "@ai-sdk/openai" import { createOpenAICompatible } from "@ai-sdk/openai-compatible" -import type { LanguageModelV2Usage, LanguageModelV2Prompt } from "@ai-sdk/provider" +import type { LanguageModelV2Prompt } from "@ai-sdk/provider" import { type ChatCompletionCreateParamsBase } from "openai/resources/chat/completions" import { Actor } from "@opencode/cloud-core/actor.js" import { and, Database, eq, sql } from "@opencode/cloud-core/drizzle/index.js" @@ -181,8 +181,6 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor try { const body = await c.req.json() - console.log(body) - const model = SUPPORTED_MODELS[body.model as keyof typeof SUPPORTED_MODELS]?.model() if (!model) throw new Error(`Unsupported model: ${body.model}`) @@ -191,7 +189,7 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor return body.stream ? await handleStream() : await handleGenerate() async function handleStream() { - const result = await streamText({ + const result = streamText({ model, ...requestBody, }) @@ -204,8 +202,7 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor try { for await (const chunk of result.fullStream) { - // TODO - //console.log("!!! CHUCK !!!", chunk); + console.log("!!! CHUNK !!! : " + chunk.type) switch (chunk.type) { case "text-delta": { const data = { @@ -282,8 +279,15 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor object: "chat.completion.chunk", created, model: body.model, + choices: [ + { + index: 0, + delta: {}, + finish_reason: "stop", + }, + ], error: { - message: chunk.error, + message: typeof chunk.error === "string" ? chunk.error : JSON.stringify(chunk.error), type: "server_error", }, } @@ -294,17 +298,6 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor } case "finish": { - const finishReason = - { - stop: "stop", - length: "length", - "content-filter": "content_filter", - "tool-calls": "tool_calls", - error: "stop", - other: "stop", - unknown: "stop", - }[chunk.finishReason] || "stop" - const data = { id, object: "chat.completion.chunk", @@ -314,7 +307,16 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor { index: 0, delta: {}, - finish_reason: finishReason, + finish_reason: + { + stop: "stop", + length: "length", + "content-filter": "content_filter", + "tool-calls": "tool_calls", + error: "stop", + other: "stop", + unknown: "stop", + }[chunk.finishReason] || "stop", }, ], usage: { @@ -335,10 +337,13 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor break } + case "finish-step": { + await trackUsage(body.model, chunk.usage, chunk.providerMetadata) + } + //case "stream-start": //case "response-metadata": case "start-step": - case "finish-step": case "text-start": case "text-end": case "reasoning-start": @@ -373,7 +378,7 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor model, ...requestBody, }) - await trackUsage(body.model, response.usage) + await trackUsage(body.model, response.usage, response.providerMetadata) return c.json({ id: `chatcmpl-${Date.now()}`, object: "chat.completion" as const, @@ -427,6 +432,7 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor function transformOpenAIRequestToAiSDK() { const prompt = transformMessages() + const tools = transformTools() return { prompt, @@ -456,6 +462,8 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor throw new Error("Unsupported response format") })(), seed: body.seed ?? undefined, + //tools: tools.tools, + //toolChoice: tools.toolChoice, } function transformTools() { @@ -468,7 +476,6 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor const aiSdkTools = tools.reduce( (acc, tool) => { acc[tool.function.name] = { - type: "function" as const, name: tool.function.name, description: tool.function.description, inputSchema: tool.function.parameters, @@ -482,14 +489,14 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor if (tool_choice == null) { aiSdkToolChoice = undefined } else if (tool_choice === "auto") { - aiSdkToolChoice = "auto" + aiSdkToolChoice = "auto" as const } else if (tool_choice === "none") { - aiSdkToolChoice = "none" + aiSdkToolChoice = "none" as const } else if (tool_choice === "required") { - aiSdkToolChoice = "required" + aiSdkToolChoice = "required" as const } else if (tool_choice.type === "function") { aiSdkToolChoice = { - type: "tool", + type: "tool" as const, toolName: tool_choice.function.name, } } @@ -604,30 +611,39 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor } } - async function trackUsage(model: string, usage: LanguageModelV2Usage) { + async function trackUsage(model: string, usage: LanguageModelUsage, providerMetadata?: ProviderMetadata) { const keyRecord = c.get("keyRecord") if (!keyRecord) return const modelData = SUPPORTED_MODELS[model as keyof typeof SUPPORTED_MODELS] if (!modelData) throw new Error(`Unsupported model: ${model}`) - const inputCost = modelData.input * (usage.inputTokens ?? 0) - const outputCost = modelData.output * (usage.outputTokens ?? 0) - const reasoningCost = modelData.reasoning * (usage.reasoningTokens ?? 0) - const cacheReadCost = modelData.cacheRead * (usage.cachedInputTokens ?? 0) - const cacheWriteCost = modelData.cacheWrite * (usage.outputTokens ?? 0) + const inputTokens = usage.inputTokens ?? 0 + const outputTokens = usage.outputTokens ?? 0 + const reasoningTokens = usage.reasoningTokens ?? 0 + const cacheReadTokens = usage.cachedInputTokens ?? 0 + const cacheWriteTokens = + providerMetadata?.["anthropic"]?.["cacheCreationInputTokens"] ?? + // @ts-expect-error + providerMetadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ?? + 0 - const totalCost = inputCost + outputCost + reasoningCost + cacheReadCost + cacheWriteCost + const inputCost = modelData.input * inputTokens + const outputCost = modelData.output * outputTokens + const reasoningCost = modelData.reasoning * reasoningTokens + const cacheReadCost = modelData.cacheRead * cacheReadTokens + const cacheWriteCost = modelData.cacheWrite * cacheWriteTokens + const costInCents = (inputCost + outputCost + reasoningCost + cacheReadCost + cacheWriteCost) * 100 await Actor.provide("system", { workspaceID: keyRecord.workspaceID }, async () => { await Billing.consume({ model, - inputTokens: usage.inputTokens ?? 0, - outputTokens: usage.outputTokens ?? 0, - reasoningTokens: usage.reasoningTokens ?? 0, - cacheReadTokens: usage.cachedInputTokens ?? 0, - cacheWriteTokens: usage.outputTokens ?? 0, - costInCents: totalCost * 100, + inputTokens, + outputTokens, + reasoningTokens, + cacheReadTokens, + cacheWriteTokens, + costInCents, }) }) @@ -699,7 +715,7 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor price_data: { currency: "usd", product_data: { - name: "OpenControl credits", + name: "opencode credits", }, unit_amount: 2000, // $20 minimum }, From e86adb2ec81e9cbc507ed99fb7e11ed66c5b4c4a Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 10 Aug 2025 01:03:15 -0400 Subject: [PATCH 034/178] wip: gateway --- cloud/function/src/gateway.ts | 70 ++++++++++++++++------------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/cloud/function/src/gateway.ts b/cloud/function/src/gateway.ts index 50625ee74..fe421239e 100644 --- a/cloud/function/src/gateway.ts +++ b/cloud/function/src/gateway.ts @@ -4,7 +4,7 @@ import { cors } from "hono/cors" import { HTTPException } from "hono/http-exception" import { zValidator } from "@hono/zod-validator" import { Resource } from "sst" -import { type ProviderMetadata, type LanguageModelUsage, generateText, streamText, Tool } from "ai" +import { type ProviderMetadata, type LanguageModelUsage, generateText, streamText } from "ai" import { createAnthropic } from "@ai-sdk/anthropic" import { createOpenAI } from "@ai-sdk/openai" import { createOpenAICompatible } from "@ai-sdk/openai-compatible" @@ -189,8 +189,7 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor return body.stream ? await handleStream() : await handleGenerate() async function handleStream() { - const result = streamText({ - model, + const result = await model.doStream({ ...requestBody, }) @@ -201,7 +200,7 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor const created = Math.floor(Date.now() / 1000) try { - for await (const chunk of result.fullStream) { + for await (const chunk of result.stream) { console.log("!!! CHUNK !!! : " + chunk.type) switch (chunk.type) { case "text-delta": { @@ -214,7 +213,7 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor { index: 0, delta: { - content: chunk.text, + content: chunk.delta, }, finish_reason: null, }, @@ -234,7 +233,7 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor { index: 0, delta: { - reasoning_content: chunk.text, + reasoning_content: chunk.delta, }, finish_reason: null, }, @@ -256,11 +255,12 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor delta: { tool_calls: [ { + index: 0, id: chunk.toolCallId, type: "function", function: { name: chunk.toolName, - arguments: JSON.stringify(chunk.input), + arguments: chunk.input, }, }, ], @@ -287,7 +287,7 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor }, ], error: { - message: typeof chunk.error === "string" ? chunk.error : JSON.stringify(chunk.error), + message: typeof chunk.error === "string" ? chunk.error : chunk.error, type: "server_error", }, } @@ -320,30 +320,26 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor }, ], usage: { - prompt_tokens: chunk.totalUsage.inputTokens, - completion_tokens: chunk.totalUsage.outputTokens, - total_tokens: chunk.totalUsage.totalTokens, + prompt_tokens: chunk.usage.inputTokens, + completion_tokens: chunk.usage.outputTokens, + total_tokens: chunk.usage.totalTokens, completion_tokens_details: { - reasoning_tokens: chunk.totalUsage.reasoningTokens, + reasoning_tokens: chunk.usage.reasoningTokens, }, prompt_tokens_details: { - cached_tokens: chunk.totalUsage.cachedInputTokens, + cached_tokens: chunk.usage.cachedInputTokens, }, }, } + await trackUsage(body.model, chunk.usage, chunk.providerMetadata) controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) controller.enqueue(encoder.encode("data: [DONE]\n\n")) controller.close() break } - case "finish-step": { - await trackUsage(body.model, chunk.usage, chunk.providerMetadata) - } - //case "stream-start": //case "response-metadata": - case "start-step": case "text-start": case "text-end": case "reasoning-start": @@ -374,8 +370,7 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor } async function handleGenerate() { - const response = await generateText({ - model, + const response = await model.doGenerate({ ...requestBody, }) await trackUsage(body.model, response.usage, response.providerMetadata) @@ -450,20 +445,20 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor : undefined, stopSequences: (typeof body.stop === "string" ? [body.stop] : body.stop) ?? undefined, responseFormat: (() => { - if (!body.response_format) return { type: "text" } + if (!body.response_format) return { type: "text" as const } if (body.response_format.type === "json_schema") return { - type: "json", + type: "json" as const, schema: body.response_format.json_schema.schema, name: body.response_format.json_schema.name, description: body.response_format.json_schema.description, } - if (body.response_format.type === "json_object") return { type: "json" } + if (body.response_format.type === "json_object") return { type: "json" as const } throw new Error("Unsupported response format") })(), seed: body.seed ?? undefined, - //tools: tools.tools, - //toolChoice: tools.toolChoice, + tools: tools.tools, + toolChoice: tools.toolChoice, } function transformTools() { @@ -473,27 +468,24 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor return { tools: undefined, toolChoice: undefined } } - const aiSdkTools = tools.reduce( - (acc, tool) => { - acc[tool.function.name] = { - name: tool.function.name, - description: tool.function.description, - inputSchema: tool.function.parameters, - } - return acc - }, - {} as Record, - ) + const aiSdkTools = tools.map((tool) => { + return { + type: tool.type, + name: tool.function.name, + description: tool.function.description, + inputSchema: tool.function.parameters!, + } + }) let aiSdkToolChoice if (tool_choice == null) { aiSdkToolChoice = undefined } else if (tool_choice === "auto") { - aiSdkToolChoice = "auto" as const + aiSdkToolChoice = { type: "auto" as const } } else if (tool_choice === "none") { - aiSdkToolChoice = "none" as const + aiSdkToolChoice = { type: "none" as const } } else if (tool_choice === "required") { - aiSdkToolChoice = "required" as const + aiSdkToolChoice = { type: "required" as const } } else if (tool_choice.type === "function") { aiSdkToolChoice = { type: "tool" as const, From 7fd2222976507b3488f4bee945f6fb0f73dde37b Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 10 Aug 2025 01:17:48 -0400 Subject: [PATCH 035/178] wip: gateway --- infra/cloud.ts | 81 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/infra/cloud.ts b/infra/cloud.ts index 1d93d58da..37fe35a0d 100644 --- a/infra/cloud.ts +++ b/infra/cloud.ts @@ -2,6 +2,50 @@ import { WebhookEndpoint } from "pulumi-stripe" import { domain } from "./stage" import { web } from "./app" +//////////////// +// DATABASE +//////////////// + +const DATABASE_USERNAME = new sst.Secret("DATABASE_USERNAME") +const DATABASE_PASSWORD = new sst.Secret("DATABASE_PASSWORD") +export const database = new sst.Linkable("Database", { + properties: { + host: "aws-us-east-2-1.pg.psdb.cloud", + database: "postgres", + username: DATABASE_USERNAME.value, + password: DATABASE_PASSWORD.value, + port: 5432, + }, +}) + +new sst.x.DevCommand("Studio", { + link: [database], + dev: { + command: "bun db studio", + directory: "cloud/core", + autostart: true, + }, +}) + +//////////////// +// AUTH +//////////////// + +const GITHUB_CLIENT_ID_CONSOLE = new sst.Secret("GITHUB_CLIENT_ID_CONSOLE") +const GITHUB_CLIENT_SECRET_CONSOLE = new sst.Secret("GITHUB_CLIENT_SECRET_CONSOLE") +const GOOGLE_CLIENT_ID = new sst.Secret("GOOGLE_CLIENT_ID") +const authStorage = new sst.cloudflare.Kv("AuthStorage") +export const auth = new sst.cloudflare.Worker("AuthApi", { + domain: `auth.${domain}`, + handler: "cloud/function/src/auth.ts", + url: true, + link: [database, authStorage, GITHUB_CLIENT_ID_CONSOLE, GITHUB_CLIENT_SECRET_CONSOLE, GOOGLE_CLIENT_ID], +}) + +//////////////// +// GATEWAY +//////////////// + export const stripeWebhook = new WebhookEndpoint("StripeWebhook", { url: $interpolate`https://api.gateway.${domain}/stripe/webhook`, enabledEvents: [ @@ -33,42 +77,9 @@ export const stripeWebhook = new WebhookEndpoint("StripeWebhook", { ], }) -const DATABASE_USERNAME = new sst.Secret("DATABASE_USERNAME") -const DATABASE_PASSWORD = new sst.Secret("DATABASE_PASSWORD") -export const database = new sst.Linkable("Database", { - properties: { - host: "aws-us-east-2-1.pg.psdb.cloud", - database: "postgres", - username: DATABASE_USERNAME.value, - password: DATABASE_PASSWORD.value, - port: 5432, - }, -}) - -new sst.x.DevCommand("Studio", { - link: [database], - dev: { - command: "bun db studio", - directory: "cloud/core", - autostart: true, - }, -}) - -const GITHUB_CLIENT_ID_CONSOLE = new sst.Secret("GITHUB_CLIENT_ID_CONSOLE") -const GITHUB_CLIENT_SECRET_CONSOLE = new sst.Secret("GITHUB_CLIENT_SECRET_CONSOLE") -const GOOGLE_CLIENT_ID = new sst.Secret("GOOGLE_CLIENT_ID") -const authStorage = new sst.cloudflare.Kv("AuthStorage") -export const auth = new sst.cloudflare.Worker("AuthApi", { - domain: `auth.${domain}`, - handler: "cloud/function/src/auth.ts", - url: true, - link: [database, authStorage, GITHUB_CLIENT_ID_CONSOLE, GITHUB_CLIENT_SECRET_CONSOLE, GOOGLE_CLIENT_ID], -}) - const ANTHROPIC_API_KEY = new sst.Secret("ANTHROPIC_API_KEY") const OPENAI_API_KEY = new sst.Secret("OPENAI_API_KEY") const ZHIPU_API_KEY = new sst.Secret("ZHIPU_API_KEY") - const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY") const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", { properties: { value: auth.url.apply((url) => url!) }, @@ -91,6 +102,10 @@ export const gateway = new sst.cloudflare.Worker("GatewayApi", { ], }) +//////////////// +// CONSOLE +//////////////// + export const console = new sst.cloudflare.x.StaticSite("Console", { domain: `console.${domain}`, path: "cloud/web", From c478d1bdbbb5165b7d5921d96d9c2ee8f55e8afa Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 10 Aug 2025 12:04:14 +0000 Subject: [PATCH 036/178] ignore: update download stats 2025-08-10 --- STATS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/STATS.md b/STATS.md index 54adf4c7d..733a7df49 100644 --- a/STATS.md +++ b/STATS.md @@ -43,3 +43,4 @@ | 2025-08-07 | 152,591 (+5,524) | 160,889 (+2,580) | 313,480 (+8,104) | | 2025-08-08 | 158,187 (+5,596) | 163,448 (+2,559) | 321,635 (+8,155) | | 2025-08-09 | 162,770 (+4,583) | 165,721 (+2,273) | 328,491 (+6,856) | +| 2025-08-10 | 165,695 (+2,925) | 167,109 (+1,388) | 332,804 (+4,313) | From 542186aa49e766c3acfe285c00bdfeae902be748 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sun, 10 Aug 2025 08:00:44 -0500 Subject: [PATCH 037/178] feat: webfetch permission support (#1772) --- packages/opencode/src/config/config.ts | 1 + packages/opencode/src/tool/registry.ts | 3 +++ packages/opencode/src/tool/webfetch.ts | 17 +++++++++++++++++ .../web/src/content/docs/docs/permissions.mdx | 8 ++++++++ 4 files changed, 29 insertions(+) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index c2491e8b9..2ba82904f 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -341,6 +341,7 @@ export namespace Config { .object({ edit: Permission.optional(), bash: z.union([Permission, z.record(z.string(), Permission)]).optional(), + webfetch: Permission.optional(), }) .optional(), experimental: z diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index 5b805ac69..c2fe5943b 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -79,6 +79,9 @@ export namespace ToolRegistry { if (cfg?.permission?.bash === "deny") { result["bash"] = false } + if (cfg?.permission?.webfetch === "deny") { + result["webfetch"] = false + } return result } diff --git a/packages/opencode/src/tool/webfetch.ts b/packages/opencode/src/tool/webfetch.ts index 16bcf048a..621421fe9 100644 --- a/packages/opencode/src/tool/webfetch.ts +++ b/packages/opencode/src/tool/webfetch.ts @@ -2,6 +2,8 @@ import { z } from "zod" import { Tool } from "./tool" import TurndownService from "turndown" import DESCRIPTION from "./webfetch.txt" +import { Config } from "../config/config" +import { Permission } from "../permission" const MAX_RESPONSE_SIZE = 5 * 1024 * 1024 // 5MB const DEFAULT_TIMEOUT = 30 * 1000 // 30 seconds @@ -22,6 +24,21 @@ export const WebFetchTool = Tool.define("webfetch", { throw new Error("URL must start with http:// or https://") } + const cfg = await Config.get() + if (cfg.permission?.webfetch === "ask") + await Permission.ask({ + type: "webfetch", + sessionID: ctx.sessionID, + messageID: ctx.messageID, + callID: ctx.callID, + title: "Fetch content from: " + params.url, + metadata: { + url: params.url, + format: params.format, + timeout: params.timeout, + }, + }) + const timeout = Math.min((params.timeout ?? DEFAULT_TIMEOUT / 1000) * 1000, MAX_TIMEOUT) const controller = new AbortController() diff --git a/packages/web/src/content/docs/docs/permissions.mdx b/packages/web/src/content/docs/docs/permissions.mdx index a3de452df..2ac7b58a1 100644 --- a/packages/web/src/content/docs/docs/permissions.mdx +++ b/packages/web/src/content/docs/docs/permissions.mdx @@ -13,6 +13,14 @@ The permissions system provides granular control to restrict what actions AI age Permissions are configured in your `opencode.json` file under the `permission` key. Here are the available options. +### Tool Permission Support + +| Tool | Description | +| ---------- | ------------------------------- | +| `edit` | Control file editing operations | +| `bash` | Control bash command execution | +| `webfetch` | Control web content fetching | + --- ### edit From 20e818ad05b385b51a29d265f0731155562e88c2 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 10 Aug 2025 12:30:41 -0400 Subject: [PATCH 038/178] wip gateway --- cloud/function/src/gateway.ts | 876 +++++++++++++++++----------------- opencode.json | 20 - 2 files changed, 445 insertions(+), 451 deletions(-) diff --git a/cloud/function/src/gateway.ts b/cloud/function/src/gateway.ts index fe421239e..2f498276f 100644 --- a/cloud/function/src/gateway.ts +++ b/cloud/function/src/gateway.ts @@ -178,456 +178,470 @@ const RestAuth: MiddlewareHandler = async (c, next) => { const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; workspaceID: string } } }>() .get("/", (c) => c.text("Hello, world!")) .post("/v1/chat/completions", GatewayAuth, async (c) => { - try { - const body = await c.req.json() + const keyRecord = c.get("keyRecord")! - const model = SUPPORTED_MODELS[body.model as keyof typeof SUPPORTED_MODELS]?.model() - if (!model) throw new Error(`Unsupported model: ${body.model}`) - - const requestBody = transformOpenAIRequestToAiSDK() - - return body.stream ? await handleStream() : await handleGenerate() - - async function handleStream() { - const result = await model.doStream({ - ...requestBody, - }) - - const encoder = new TextEncoder() - const stream = new ReadableStream({ - async start(controller) { - const id = `chatcmpl-${Date.now()}` - const created = Math.floor(Date.now() / 1000) - - try { - for await (const chunk of result.stream) { - console.log("!!! CHUNK !!! : " + chunk.type) - switch (chunk.type) { - case "text-delta": { - const data = { - id, - object: "chat.completion.chunk", - created, - model: body.model, - choices: [ - { - index: 0, - delta: { - content: chunk.delta, - }, - finish_reason: null, - }, - ], - } - controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) - break - } - - case "reasoning-delta": { - const data = { - id, - object: "chat.completion.chunk", - created, - model: body.model, - choices: [ - { - index: 0, - delta: { - reasoning_content: chunk.delta, - }, - finish_reason: null, - }, - ], - } - controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) - break - } - - case "tool-call": { - const data = { - id, - object: "chat.completion.chunk", - created, - model: body.model, - choices: [ - { - index: 0, - delta: { - tool_calls: [ - { - index: 0, - id: chunk.toolCallId, - type: "function", - function: { - name: chunk.toolName, - arguments: chunk.input, - }, - }, - ], - }, - finish_reason: null, - }, - ], - } - controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) - break - } - - case "error": { - const data = { - id, - object: "chat.completion.chunk", - created, - model: body.model, - choices: [ - { - index: 0, - delta: {}, - finish_reason: "stop", - }, - ], - error: { - message: typeof chunk.error === "string" ? chunk.error : chunk.error, - type: "server_error", - }, - } - controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) - controller.enqueue(encoder.encode("data: [DONE]\n\n")) - controller.close() - break - } - - case "finish": { - const data = { - id, - object: "chat.completion.chunk", - created, - model: body.model, - choices: [ - { - index: 0, - delta: {}, - finish_reason: - { - stop: "stop", - length: "length", - "content-filter": "content_filter", - "tool-calls": "tool_calls", - error: "stop", - other: "stop", - unknown: "stop", - }[chunk.finishReason] || "stop", - }, - ], - usage: { - prompt_tokens: chunk.usage.inputTokens, - completion_tokens: chunk.usage.outputTokens, - total_tokens: chunk.usage.totalTokens, - completion_tokens_details: { - reasoning_tokens: chunk.usage.reasoningTokens, - }, - prompt_tokens_details: { - cached_tokens: chunk.usage.cachedInputTokens, - }, - }, - } - await trackUsage(body.model, chunk.usage, chunk.providerMetadata) - controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) - controller.enqueue(encoder.encode("data: [DONE]\n\n")) - controller.close() - break - } - - //case "stream-start": - //case "response-metadata": - case "text-start": - case "text-end": - case "reasoning-start": - case "reasoning-end": - case "tool-input-start": - case "tool-input-delta": - case "tool-input-end": - case "raw": - default: - // Log unknown chunk types for debugging - console.warn(`Unknown chunk type: ${(chunk as any).type}`) - break - } - } - } catch (error) { - controller.error(error) - } - }, - }) - - return new Response(stream, { - headers: { - "Content-Type": "text/plain; charset=utf-8", - "Cache-Control": "no-cache", - Connection: "keep-alive", - }, - }) - } - - async function handleGenerate() { - const response = await model.doGenerate({ - ...requestBody, - }) - await trackUsage(body.model, response.usage, response.providerMetadata) - return c.json({ - id: `chatcmpl-${Date.now()}`, - object: "chat.completion" as const, - created: Math.floor(Date.now() / 1000), - model: body.model, - choices: [ + return await Actor.provide("system", { workspaceID: keyRecord.workspaceID }, async () => { + try { + // Check balance + const customer = await Billing.get() + if (customer.balance <= 0) { + return c.json( { - index: 0, - message: { - role: "assistant" as const, - content: response.content?.find((c) => c.type === "text")?.text ?? "", - reasoning_content: response.content?.find((c) => c.type === "reasoning")?.text, - tool_calls: response.content - ?.filter((c) => c.type === "tool-call") - .map((toolCall) => ({ - id: toolCall.toolCallId, - type: "function" as const, - function: { - name: toolCall.toolName, - arguments: toolCall.input, - }, - })), + error: { + message: "Insufficient balance", + type: "insufficient_quota", + param: null, + code: "insufficient_quota", }, - finish_reason: - ( - { - stop: "stop", - length: "length", - "content-filter": "content_filter", - "tool-calls": "tool_calls", - error: "stop", - other: "stop", - unknown: "stop", - } as const - )[response.finishReason] || "stop", }, - ], - usage: { - prompt_tokens: response.usage?.inputTokens, - completion_tokens: response.usage?.outputTokens, - total_tokens: response.usage?.totalTokens, - completion_tokens_details: { - reasoning_tokens: response.usage?.reasoningTokens, - }, - prompt_tokens_details: { - cached_tokens: response.usage?.cachedInputTokens, - }, - }, - }) - } - - function transformOpenAIRequestToAiSDK() { - const prompt = transformMessages() - const tools = transformTools() - - return { - prompt, - maxOutputTokens: body.max_tokens ?? body.max_completion_tokens ?? undefined, - temperature: body.temperature ?? undefined, - topP: body.top_p ?? undefined, - frequencyPenalty: body.frequency_penalty ?? undefined, - presencePenalty: body.presence_penalty ?? undefined, - providerOptions: body.reasoning_effort - ? { - anthropic: { - reasoningEffort: body.reasoning_effort, - }, - } - : undefined, - stopSequences: (typeof body.stop === "string" ? [body.stop] : body.stop) ?? undefined, - responseFormat: (() => { - if (!body.response_format) return { type: "text" as const } - if (body.response_format.type === "json_schema") - return { - type: "json" as const, - schema: body.response_format.json_schema.schema, - name: body.response_format.json_schema.name, - description: body.response_format.json_schema.description, - } - if (body.response_format.type === "json_object") return { type: "json" as const } - throw new Error("Unsupported response format") - })(), - seed: body.seed ?? undefined, - tools: tools.tools, - toolChoice: tools.toolChoice, + 401, + ) } - function transformTools() { - const { tools, tool_choice } = body + const body = await c.req.json() + const model = SUPPORTED_MODELS[body.model as keyof typeof SUPPORTED_MODELS]?.model() + if (!model) throw new Error(`Unsupported model: ${body.model}`) - if (!tools || tools.length === 0) { - return { tools: undefined, toolChoice: undefined } - } + const requestBody = transformOpenAIRequestToAiSDK() - const aiSdkTools = tools.map((tool) => { - return { - type: tool.type, - name: tool.function.name, - description: tool.function.description, - inputSchema: tool.function.parameters!, - } + return body.stream ? await handleStream() : await handleGenerate() + + async function handleStream() { + const result = await model.doStream({ + ...requestBody, }) - let aiSdkToolChoice - if (tool_choice == null) { - aiSdkToolChoice = undefined - } else if (tool_choice === "auto") { - aiSdkToolChoice = { type: "auto" as const } - } else if (tool_choice === "none") { - aiSdkToolChoice = { type: "none" as const } - } else if (tool_choice === "required") { - aiSdkToolChoice = { type: "required" as const } - } else if (tool_choice.type === "function") { - aiSdkToolChoice = { - type: "tool" as const, - toolName: tool_choice.function.name, - } - } + const encoder = new TextEncoder() + const stream = new ReadableStream({ + async start(controller) { + const id = `chatcmpl-${Date.now()}` + const created = Math.floor(Date.now() / 1000) - return { tools: aiSdkTools, toolChoice: aiSdkToolChoice } - } - - function transformMessages() { - const { messages } = body - const prompt: LanguageModelV2Prompt = [] - - for (const message of messages) { - switch (message.role) { - case "system": { - prompt.push({ - role: "system", - content: message.content as string, - }) - break - } - - case "user": { - if (typeof message.content === "string") { - prompt.push({ - role: "user", - content: [{ type: "text", text: message.content }], - }) - } else { - const content = message.content.map((part) => { - switch (part.type) { - case "text": - return { type: "text" as const, text: part.text } - case "image_url": - return { - type: "file" as const, - mediaType: "image/jpeg" as const, - data: part.image_url.url, - } - default: - throw new Error(`Unsupported content part type: ${(part as any).type}`) + try { + for await (const chunk of result.stream) { + console.log("!!! CHUNK !!! : " + chunk.type) + switch (chunk.type) { + case "text-delta": { + const data = { + id, + object: "chat.completion.chunk", + created, + model: body.model, + choices: [ + { + index: 0, + delta: { + content: chunk.delta, + }, + finish_reason: null, + }, + ], + } + controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) + break } - }) - prompt.push({ - role: "user", - content, - }) - } - break - } - case "assistant": { - const content: Array< - | { type: "text"; text: string } - | { - type: "tool-call" - toolCallId: string - toolName: string - input: any + case "reasoning-delta": { + const data = { + id, + object: "chat.completion.chunk", + created, + model: body.model, + choices: [ + { + index: 0, + delta: { + reasoning_content: chunk.delta, + }, + finish_reason: null, + }, + ], + } + controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) + break } - > = [] - if (message.content) { - content.push({ - type: "text", - text: message.content as string, - }) - } + case "tool-call": { + const data = { + id, + object: "chat.completion.chunk", + created, + model: body.model, + choices: [ + { + index: 0, + delta: { + tool_calls: [ + { + index: 0, + id: chunk.toolCallId, + type: "function", + function: { + name: chunk.toolName, + arguments: chunk.input, + }, + }, + ], + }, + finish_reason: null, + }, + ], + } + controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) + break + } - if (message.tool_calls) { - for (const toolCall of message.tool_calls) { - content.push({ - type: "tool-call", - toolCallId: toolCall.id, - toolName: toolCall.function.name, - input: JSON.parse(toolCall.function.arguments), - }) + case "error": { + const data = { + id, + object: "chat.completion.chunk", + created, + model: body.model, + choices: [ + { + index: 0, + delta: {}, + finish_reason: "stop", + }, + ], + error: { + message: typeof chunk.error === "string" ? chunk.error : chunk.error, + type: "server_error", + }, + } + controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) + controller.enqueue(encoder.encode("data: [DONE]\n\n")) + controller.close() + break + } + + case "finish": { + const data = { + id, + object: "chat.completion.chunk", + created, + model: body.model, + choices: [ + { + index: 0, + delta: {}, + finish_reason: + { + stop: "stop", + length: "length", + "content-filter": "content_filter", + "tool-calls": "tool_calls", + error: "stop", + other: "stop", + unknown: "stop", + }[chunk.finishReason] || "stop", + }, + ], + usage: { + prompt_tokens: chunk.usage.inputTokens, + completion_tokens: chunk.usage.outputTokens, + total_tokens: chunk.usage.totalTokens, + completion_tokens_details: { + reasoning_tokens: chunk.usage.reasoningTokens, + }, + prompt_tokens_details: { + cached_tokens: chunk.usage.cachedInputTokens, + }, + }, + } + await trackUsage(body.model, chunk.usage, chunk.providerMetadata) + controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) + controller.enqueue(encoder.encode("data: [DONE]\n\n")) + controller.close() + break + } + + //case "stream-start": + //case "response-metadata": + case "text-start": + case "text-end": + case "reasoning-start": + case "reasoning-end": + case "tool-input-start": + case "tool-input-delta": + case "tool-input-end": + case "raw": + default: + // Log unknown chunk types for debugging + console.warn(`Unknown chunk type: ${(chunk as any).type}`) + break } } - - prompt.push({ - role: "assistant", - content, - }) - break + } catch (error) { + controller.error(error) } + }, + }) - case "tool": { - prompt.push({ - role: "tool", - content: [ - { - type: "tool-result", - toolName: "placeholder", - toolCallId: message.tool_call_id, - output: { - type: "text", - value: message.content as string, + return new Response(stream, { + headers: { + "Content-Type": "text/plain; charset=utf-8", + "Cache-Control": "no-cache", + Connection: "keep-alive", + }, + }) + } + + async function handleGenerate() { + const response = await model.doGenerate({ + ...requestBody, + }) + await trackUsage(body.model, response.usage, response.providerMetadata) + return c.json({ + id: `chatcmpl-${Date.now()}`, + object: "chat.completion" as const, + created: Math.floor(Date.now() / 1000), + model: body.model, + choices: [ + { + index: 0, + message: { + role: "assistant" as const, + content: response.content?.find((c) => c.type === "text")?.text ?? "", + reasoning_content: response.content?.find((c) => c.type === "reasoning")?.text, + tool_calls: response.content + ?.filter((c) => c.type === "tool-call") + .map((toolCall) => ({ + id: toolCall.toolCallId, + type: "function" as const, + function: { + name: toolCall.toolName, + arguments: toolCall.input, }, - }, - ], - }) - break - } + })), + }, + finish_reason: + ( + { + stop: "stop", + length: "length", + "content-filter": "content_filter", + "tool-calls": "tool_calls", + error: "stop", + other: "stop", + unknown: "stop", + } as const + )[response.finishReason] || "stop", + }, + ], + usage: { + prompt_tokens: response.usage?.inputTokens, + completion_tokens: response.usage?.outputTokens, + total_tokens: response.usage?.totalTokens, + completion_tokens_details: { + reasoning_tokens: response.usage?.reasoningTokens, + }, + prompt_tokens_details: { + cached_tokens: response.usage?.cachedInputTokens, + }, + }, + }) + } - default: { - throw new Error(`Unsupported message role: ${message.role}`) - } - } + function transformOpenAIRequestToAiSDK() { + const prompt = transformMessages() + const tools = transformTools() + + return { + prompt, + maxOutputTokens: body.max_tokens ?? body.max_completion_tokens ?? undefined, + temperature: body.temperature ?? undefined, + topP: body.top_p ?? undefined, + frequencyPenalty: body.frequency_penalty ?? undefined, + presencePenalty: body.presence_penalty ?? undefined, + providerOptions: body.reasoning_effort + ? { + anthropic: { + reasoningEffort: body.reasoning_effort, + }, + } + : undefined, + stopSequences: (typeof body.stop === "string" ? [body.stop] : body.stop) ?? undefined, + responseFormat: (() => { + if (!body.response_format) return { type: "text" as const } + if (body.response_format.type === "json_schema") + return { + type: "json" as const, + schema: body.response_format.json_schema.schema, + name: body.response_format.json_schema.name, + description: body.response_format.json_schema.description, + } + if (body.response_format.type === "json_object") return { type: "json" as const } + throw new Error("Unsupported response format") + })(), + seed: body.seed ?? undefined, + tools: tools.tools, + toolChoice: tools.toolChoice, } - return prompt + function transformTools() { + const { tools, tool_choice } = body + + if (!tools || tools.length === 0) { + return { tools: undefined, toolChoice: undefined } + } + + const aiSdkTools = tools.map((tool) => { + return { + type: tool.type, + name: tool.function.name, + description: tool.function.description, + inputSchema: tool.function.parameters!, + } + }) + + let aiSdkToolChoice + if (tool_choice == null) { + aiSdkToolChoice = undefined + } else if (tool_choice === "auto") { + aiSdkToolChoice = { type: "auto" as const } + } else if (tool_choice === "none") { + aiSdkToolChoice = { type: "none" as const } + } else if (tool_choice === "required") { + aiSdkToolChoice = { type: "required" as const } + } else if (tool_choice.type === "function") { + aiSdkToolChoice = { + type: "tool" as const, + toolName: tool_choice.function.name, + } + } + + return { tools: aiSdkTools, toolChoice: aiSdkToolChoice } + } + + function transformMessages() { + const { messages } = body + const prompt: LanguageModelV2Prompt = [] + + for (const message of messages) { + switch (message.role) { + case "system": { + prompt.push({ + role: "system", + content: message.content as string, + }) + break + } + + case "user": { + if (typeof message.content === "string") { + prompt.push({ + role: "user", + content: [{ type: "text", text: message.content }], + }) + } else { + const content = message.content.map((part) => { + switch (part.type) { + case "text": + return { type: "text" as const, text: part.text } + case "image_url": + return { + type: "file" as const, + mediaType: "image/jpeg" as const, + data: part.image_url.url, + } + default: + throw new Error(`Unsupported content part type: ${(part as any).type}`) + } + }) + prompt.push({ + role: "user", + content, + }) + } + break + } + + case "assistant": { + const content: Array< + | { type: "text"; text: string } + | { + type: "tool-call" + toolCallId: string + toolName: string + input: any + } + > = [] + + if (message.content) { + content.push({ + type: "text", + text: message.content as string, + }) + } + + if (message.tool_calls) { + for (const toolCall of message.tool_calls) { + content.push({ + type: "tool-call", + toolCallId: toolCall.id, + toolName: toolCall.function.name, + input: JSON.parse(toolCall.function.arguments), + }) + } + } + + prompt.push({ + role: "assistant", + content, + }) + break + } + + case "tool": { + prompt.push({ + role: "tool", + content: [ + { + type: "tool-result", + toolName: "placeholder", + toolCallId: message.tool_call_id, + output: { + type: "text", + value: message.content as string, + }, + }, + ], + }) + break + } + + default: { + throw new Error(`Unsupported message role: ${message.role}`) + } + } + } + + return prompt + } } - } - async function trackUsage(model: string, usage: LanguageModelUsage, providerMetadata?: ProviderMetadata) { - const keyRecord = c.get("keyRecord") - if (!keyRecord) return + async function trackUsage(model: string, usage: LanguageModelUsage, providerMetadata?: ProviderMetadata) { + const modelData = SUPPORTED_MODELS[model as keyof typeof SUPPORTED_MODELS] + if (!modelData) throw new Error(`Unsupported model: ${model}`) - const modelData = SUPPORTED_MODELS[model as keyof typeof SUPPORTED_MODELS] - if (!modelData) throw new Error(`Unsupported model: ${model}`) + const inputTokens = usage.inputTokens ?? 0 + const outputTokens = usage.outputTokens ?? 0 + const reasoningTokens = usage.reasoningTokens ?? 0 + const cacheReadTokens = usage.cachedInputTokens ?? 0 + const cacheWriteTokens = + providerMetadata?.["anthropic"]?.["cacheCreationInputTokens"] ?? + // @ts-expect-error + providerMetadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ?? + 0 - const inputTokens = usage.inputTokens ?? 0 - const outputTokens = usage.outputTokens ?? 0 - const reasoningTokens = usage.reasoningTokens ?? 0 - const cacheReadTokens = usage.cachedInputTokens ?? 0 - const cacheWriteTokens = - providerMetadata?.["anthropic"]?.["cacheCreationInputTokens"] ?? - // @ts-expect-error - providerMetadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ?? - 0 + const inputCost = modelData.input * inputTokens + const outputCost = modelData.output * outputTokens + const reasoningCost = modelData.reasoning * reasoningTokens + const cacheReadCost = modelData.cacheRead * cacheReadTokens + const cacheWriteCost = modelData.cacheWrite * cacheWriteTokens + const costInCents = (inputCost + outputCost + reasoningCost + cacheReadCost + cacheWriteCost) * 100 - const inputCost = modelData.input * inputTokens - const outputCost = modelData.output * outputTokens - const reasoningCost = modelData.reasoning * reasoningTokens - const cacheReadCost = modelData.cacheRead * cacheReadTokens - const cacheWriteCost = modelData.cacheWrite * cacheWriteTokens - const costInCents = (inputCost + outputCost + reasoningCost + cacheReadCost + cacheWriteCost) * 100 - - await Actor.provide("system", { workspaceID: keyRecord.workspaceID }, async () => { await Billing.consume({ model, inputTokens, @@ -637,18 +651,18 @@ const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; wor cacheWriteTokens, costInCents, }) - }) - await Database.use((tx) => - tx - .update(KeyTable) - .set({ timeUsed: sql`now()` }) - .where(eq(KeyTable.id, keyRecord.id)), - ) + await Database.use((tx) => + tx + .update(KeyTable) + .set({ timeUsed: sql`now()` }) + .where(eq(KeyTable.id, keyRecord.id)), + ) + } + } catch (error: any) { + return c.json({ error: { message: error.message } }, 500) } - } catch (error: any) { - return c.json({ error: { message: error.message } }, 500) - } + }) }) .use("/*", cors()) .use(RestAuth) diff --git a/opencode.json b/opencode.json index 12a82643f..59f14ac75 100644 --- a/opencode.json +++ b/opencode.json @@ -1,25 +1,5 @@ { "$schema": "https://opencode.ai/config.json", - "provider": { - "oc-frank": { - "npm": "@ai-sdk/openai-compatible", - "name": "OC-Frank", - "options": { - "baseURL": "https://api.gateway.frank.dev.opencode.ai/v1" - }, - "models": { - "anthropic/claude-sonnet-4": { - "name": "Claude Sonnet 4" - }, - "openai/gpt-4.1": { - "name": "GPT-4.1" - }, - "zhipuai/glm-4.5-flash": { - "name": "GLM-4.5 Flash" - } - } - } - }, "mcp": { "context7": { "type": "remote", From b8d2aebf09313d451cec873b3d0807b818015b32 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Sun, 10 Aug 2025 19:24:16 -0500 Subject: [PATCH 039/178] feat: thinking blocks rendered in tui and share page --- packages/opencode/src/provider/transform.ts | 15 ++ packages/opencode/src/session/index.ts | 30 ++++ packages/opencode/src/session/message-v2.ts | 16 ++ packages/sdk/go/.stats.yml | 6 +- packages/sdk/go/api.md | 1 + packages/sdk/go/config.go | 30 +++- packages/sdk/go/scripts/mock | 4 +- packages/sdk/go/scripts/test | 2 +- packages/sdk/go/session.go | 148 ++++++++++++++---- packages/sdk/stainless/stainless.yml | 1 + .../tui/internal/components/chat/message.go | 20 ++- .../tui/internal/components/chat/messages.go | 32 ++++ packages/tui/internal/tui/tui.go | 4 + packages/web/src/components/Share.tsx | 12 +- packages/web/src/components/icons/custom.tsx | 23 ++- .../web/src/components/share/part.module.css | 23 +++ packages/web/src/components/share/part.tsx | 12 +- 17 files changed, 324 insertions(+), 55 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 91a2777b8..933df3dd4 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -87,7 +87,22 @@ export namespace ProviderTransform { return { reasoningEffort: "minimal", textVerbosity: "low", + // reasoningSummary: "auto", + // include: ["reasoning.encrypted_content"], } } + // if (modelID.includes("claude")) { + // return { + // thinking: { + // type: "enabled", + // budgetTokens: 32000, + // }, + // } + // } + // if (_providerID === "bedrock") { + // return { + // reasoningConfig: { type: "enabled", budgetTokens: 32000 }, + // } + // } } } diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 218f7ba88..583f3df7f 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -1006,6 +1006,7 @@ export namespace Session { async process(stream: StreamTextResult, never>) { try { let currentText: MessageV2.TextPart | undefined + let reasoningMap: Record = {} for await (const value of stream.fullStream) { log.info("part", { @@ -1016,12 +1017,41 @@ export namespace Session { break case "reasoning-start": + if (value.id in reasoningMap) { + continue + } + reasoningMap[value.id] = { + id: Identifier.ascending("part"), + messageID: assistantMsg.id, + sessionID: assistantMsg.sessionID, + type: "reasoning", + text: "", + time: { + start: Date.now(), + }, + } break case "reasoning-delta": + if (value.id in reasoningMap) { + const part = reasoningMap[value.id] + part.text += value.text + if (part.text) await updatePart(part) + } break case "reasoning-end": + if (value.id in reasoningMap) { + const part = reasoningMap[value.id] + part.text = part.text.trimEnd() + part.providerMetadata = value.providerMetadata + part.time = { + start: Date.now(), + end: Date.now(), + } + await updatePart(part) + delete reasoningMap[value.id] + } break case "tool-input-start": diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index f12526e79..a1daf9b9a 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -118,6 +118,21 @@ export namespace MessageV2 { }) export type TextPart = z.infer + export const ReasoningPart = PartBase.extend({ + type: z.literal("reasoning"), + text: z.string(), + providerMetadata: z.record(z.any()).optional(), + time: z + .object({ + start: z.number(), + end: z.number().optional(), + }) + .optional(), + }).openapi({ + ref: "ReasoningPart", + }) + export type ReasoningPart = z.infer + export const ToolPart = PartBase.extend({ type: z.literal("tool"), callID: z.string(), @@ -229,6 +244,7 @@ export namespace MessageV2 { export const Part = z .discriminatedUnion("type", [ TextPart, + ReasoningPart, FilePart, ToolPart, StepStartPart, diff --git a/packages/sdk/go/.stats.yml b/packages/sdk/go/.stats.yml index a2023f0e4..4db6fb4c0 100644 --- a/packages/sdk/go/.stats.yml +++ b/packages/sdk/go/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 34 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-da1c340135c3dd6b1edb4e00e7039d2ac54d59271683a8b6ed528e51137ce41a.yml -openapi_spec_hash: 0cdd9b6273d72f5a6f484a2999ff0632 -config_hash: 7581d5948150d4ef7dd7b13d0845dbeb +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-b86cf7bb8df4f60ebe8b8f51e281c8076cfdccc8554178c1b78beca4b025f0ff.yml +openapi_spec_hash: 47633b7481d91708643ea7b43fffffe6 +config_hash: bd7f6435ed0c0005f373b5526c07a055 diff --git a/packages/sdk/go/api.md b/packages/sdk/go/api.md index d71951994..eeef10331 100644 --- a/packages/sdk/go/api.md +++ b/packages/sdk/go/api.md @@ -92,6 +92,7 @@ Response Types: - opencode.FileSource - opencode.Message - opencode.Part +- opencode.ReasoningPart - opencode.Session - opencode.SnapshotPart - opencode.StepFinishPart diff --git a/packages/sdk/go/config.go b/packages/sdk/go/config.go index a7b75a1bf..ed3e3c5ab 100644 --- a/packages/sdk/go/config.go +++ b/packages/sdk/go/config.go @@ -74,9 +74,10 @@ type Config struct { // Control sharing behavior:'manual' allows manual sharing via commands, 'auto' // enables automatic sharing, 'disabled' disables all sharing Share ConfigShare `json:"share"` - // Small model to use for tasks like title generation in the - // format of provider/model + // Small model to use for tasks like title generation in the format of + // provider/model SmallModel string `json:"small_model"` + Snapshot bool `json:"snapshot"` // Theme name to use for the interface Theme string `json:"theme"` // Custom username to display in conversations instead of system username @@ -105,6 +106,7 @@ type configJSON struct { Provider apijson.Field Share apijson.Field SmallModel apijson.Field + Snapshot apijson.Field Theme apijson.Field Username apijson.Field raw string @@ -780,9 +782,10 @@ func (r ConfigModePlanMode) IsKnown() bool { } type ConfigPermission struct { - Bash ConfigPermissionBashUnion `json:"bash"` - Edit ConfigPermissionEdit `json:"edit"` - JSON configPermissionJSON `json:"-"` + Bash ConfigPermissionBashUnion `json:"bash"` + Edit ConfigPermissionEdit `json:"edit"` + Webfetch ConfigPermissionWebfetch `json:"webfetch"` + JSON configPermissionJSON `json:"-"` } // configPermissionJSON contains the JSON metadata for the struct @@ -790,6 +793,7 @@ type ConfigPermission struct { type configPermissionJSON struct { Bash apijson.Field Edit apijson.Field + Webfetch apijson.Field raw string ExtraFields map[string]apijson.Field } @@ -876,6 +880,22 @@ func (r ConfigPermissionEdit) IsKnown() bool { return false } +type ConfigPermissionWebfetch string + +const ( + ConfigPermissionWebfetchAsk ConfigPermissionWebfetch = "ask" + ConfigPermissionWebfetchAllow ConfigPermissionWebfetch = "allow" + ConfigPermissionWebfetchDeny ConfigPermissionWebfetch = "deny" +) + +func (r ConfigPermissionWebfetch) IsKnown() bool { + switch r { + case ConfigPermissionWebfetchAsk, ConfigPermissionWebfetchAllow, ConfigPermissionWebfetchDeny: + return true + } + return false +} + type ConfigProvider struct { Models map[string]ConfigProviderModel `json:"models,required"` ID string `json:"id"` diff --git a/packages/sdk/go/scripts/mock b/packages/sdk/go/scripts/mock index d2814ae6a..0b28f6ea2 100755 --- a/packages/sdk/go/scripts/mock +++ b/packages/sdk/go/scripts/mock @@ -21,7 +21,7 @@ echo "==> Starting mock server with URL ${URL}" # Run prism mock on the given spec if [ "$1" == "--daemon" ]; then - npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & # Wait for server to come online echo -n "Waiting for server" @@ -37,5 +37,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" fi diff --git a/packages/sdk/go/scripts/test b/packages/sdk/go/scripts/test index efebceaee..c26b12228 100755 --- a/packages/sdk/go/scripts/test +++ b/packages/sdk/go/scripts/test @@ -43,7 +43,7 @@ elif ! prism_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the prism command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock path/to/your.openapi.yml${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" echo exit 1 diff --git a/packages/sdk/go/session.go b/packages/sdk/go/session.go index bae17cf2a..bb5cecf57 100644 --- a/packages/sdk/go/session.go +++ b/packages/sdk/go/session.go @@ -962,18 +962,20 @@ type Part struct { Cost float64 `json:"cost"` Filename string `json:"filename"` // This field can have the runtime type of [[]string]. - Files interface{} `json:"files"` - Hash string `json:"hash"` - Mime string `json:"mime"` - Name string `json:"name"` - Snapshot string `json:"snapshot"` + Files interface{} `json:"files"` + Hash string `json:"hash"` + Mime string `json:"mime"` + Name string `json:"name"` + // This field can have the runtime type of [map[string]interface{}]. + ProviderMetadata interface{} `json:"providerMetadata"` + Snapshot string `json:"snapshot"` // This field can have the runtime type of [FilePartSource], [AgentPartSource]. Source interface{} `json:"source"` // This field can have the runtime type of [ToolPartState]. State interface{} `json:"state"` Synthetic bool `json:"synthetic"` Text string `json:"text"` - // This field can have the runtime type of [TextPartTime]. + // This field can have the runtime type of [TextPartTime], [ReasoningPartTime]. Time interface{} `json:"time"` // This field can have the runtime type of [StepFinishPartTokens]. Tokens interface{} `json:"tokens"` @@ -985,28 +987,29 @@ type Part struct { // partJSON contains the JSON metadata for the struct [Part] type partJSON struct { - ID apijson.Field - MessageID apijson.Field - SessionID apijson.Field - Type apijson.Field - CallID apijson.Field - Cost apijson.Field - Filename apijson.Field - Files apijson.Field - Hash apijson.Field - Mime apijson.Field - Name apijson.Field - Snapshot apijson.Field - Source apijson.Field - State apijson.Field - Synthetic apijson.Field - Text apijson.Field - Time apijson.Field - Tokens apijson.Field - Tool apijson.Field - URL apijson.Field - raw string - ExtraFields map[string]apijson.Field + ID apijson.Field + MessageID apijson.Field + SessionID apijson.Field + Type apijson.Field + CallID apijson.Field + Cost apijson.Field + Filename apijson.Field + Files apijson.Field + Hash apijson.Field + Mime apijson.Field + Name apijson.Field + ProviderMetadata apijson.Field + Snapshot apijson.Field + Source apijson.Field + State apijson.Field + Synthetic apijson.Field + Text apijson.Field + Time apijson.Field + Tokens apijson.Field + Tool apijson.Field + URL apijson.Field + raw string + ExtraFields map[string]apijson.Field } func (r partJSON) RawJSON() string { @@ -1025,14 +1028,16 @@ func (r *Part) UnmarshalJSON(data []byte) (err error) { // AsUnion returns a [PartUnion] interface which you can cast to the specific types // for more type safety. // -// Possible runtime types of the union are [TextPart], [FilePart], [ToolPart], -// [StepStartPart], [StepFinishPart], [SnapshotPart], [PartPatchPart], [AgentPart]. +// Possible runtime types of the union are [TextPart], [ReasoningPart], [FilePart], +// [ToolPart], [StepStartPart], [StepFinishPart], [SnapshotPart], [PartPatchPart], +// [AgentPart]. func (r Part) AsUnion() PartUnion { return r.union } -// Union satisfied by [TextPart], [FilePart], [ToolPart], [StepStartPart], -// [StepFinishPart], [SnapshotPart], [PartPatchPart] or [AgentPart]. +// Union satisfied by [TextPart], [ReasoningPart], [FilePart], [ToolPart], +// [StepStartPart], [StepFinishPart], [SnapshotPart], [PartPatchPart] or +// [AgentPart]. type PartUnion interface { implementsPart() } @@ -1046,6 +1051,11 @@ func init() { Type: reflect.TypeOf(TextPart{}), DiscriminatorValue: "text", }, + apijson.UnionVariant{ + TypeFilter: gjson.JSON, + Type: reflect.TypeOf(ReasoningPart{}), + DiscriminatorValue: "reasoning", + }, apijson.UnionVariant{ TypeFilter: gjson.JSON, Type: reflect.TypeOf(FilePart{}), @@ -1134,6 +1144,7 @@ type PartType string const ( PartTypeText PartType = "text" + PartTypeReasoning PartType = "reasoning" PartTypeFile PartType = "file" PartTypeTool PartType = "tool" PartTypeStepStart PartType = "step-start" @@ -1145,12 +1156,83 @@ const ( func (r PartType) IsKnown() bool { switch r { - case PartTypeText, PartTypeFile, PartTypeTool, PartTypeStepStart, PartTypeStepFinish, PartTypeSnapshot, PartTypePatch, PartTypeAgent: + case PartTypeText, PartTypeReasoning, PartTypeFile, PartTypeTool, PartTypeStepStart, PartTypeStepFinish, PartTypeSnapshot, PartTypePatch, PartTypeAgent: return true } return false } +type ReasoningPart struct { + ID string `json:"id,required"` + MessageID string `json:"messageID,required"` + SessionID string `json:"sessionID,required"` + Text string `json:"text,required"` + Type ReasoningPartType `json:"type,required"` + ProviderMetadata map[string]interface{} `json:"providerMetadata"` + Time ReasoningPartTime `json:"time"` + JSON reasoningPartJSON `json:"-"` +} + +// reasoningPartJSON contains the JSON metadata for the struct [ReasoningPart] +type reasoningPartJSON struct { + ID apijson.Field + MessageID apijson.Field + SessionID apijson.Field + Text apijson.Field + Type apijson.Field + ProviderMetadata apijson.Field + Time apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *ReasoningPart) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r reasoningPartJSON) RawJSON() string { + return r.raw +} + +func (r ReasoningPart) implementsPart() {} + +type ReasoningPartType string + +const ( + ReasoningPartTypeReasoning ReasoningPartType = "reasoning" +) + +func (r ReasoningPartType) IsKnown() bool { + switch r { + case ReasoningPartTypeReasoning: + return true + } + return false +} + +type ReasoningPartTime struct { + Start float64 `json:"start,required"` + End float64 `json:"end"` + JSON reasoningPartTimeJSON `json:"-"` +} + +// reasoningPartTimeJSON contains the JSON metadata for the struct +// [ReasoningPartTime] +type reasoningPartTimeJSON struct { + Start apijson.Field + End apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *ReasoningPartTime) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r reasoningPartTimeJSON) RawJSON() string { + return r.raw +} + type Session struct { ID string `json:"id,required"` Time SessionTime `json:"time,required"` diff --git a/packages/sdk/stainless/stainless.yml b/packages/sdk/stainless/stainless.yml index 3a8da4061..0c85a2cef 100644 --- a/packages/sdk/stainless/stainless.yml +++ b/packages/sdk/stainless/stainless.yml @@ -101,6 +101,7 @@ resources: toolPart: ToolPart agentPart: AgentPart agentPartInput: AgentPartInput + reasoningPart: ReasoningPart stepStartPart: StepStartPart stepFinishPart: StepFinishPart snapshotPart: SnapshotPart diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index 5f4dc9612..66f8d728e 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -208,6 +208,7 @@ func renderText( showToolDetails bool, width int, extra string, + isThinking bool, fileParts []opencode.FilePart, agentParts []opencode.AgentPart, toolCalls ...opencode.ToolPart, @@ -219,8 +220,15 @@ func renderText( var content string switch casted := message.(type) { case opencode.AssistantMessage: + bg := t.Background() + if isThinking { + bg = t.BackgroundPanel() + } ts = time.UnixMilli(int64(casted.Time.Created)) - content = util.ToMarkdown(text, width, t.Background()) + content = util.ToMarkdown(text, width, bg) + if isThinking { + content = styles.NewStyle().Background(bg).Foreground(t.TextMuted()).Render("Thinking") + "\n\n" + content + } case opencode.UserMessage: ts = time.UnixMilli(int64(casted.Time.Created)) base := styles.NewStyle().Foreground(t.Text()).Background(backgroundColor) @@ -385,6 +393,16 @@ func renderText( WithBorderColor(t.Secondary()), ) case opencode.AssistantMessage: + if isThinking { + return renderContentBlock( + app, + content, + width, + WithTextColor(t.Text()), + WithBackgroundColor(t.BackgroundPanel()), + WithBorderColor(t.BackgroundPanel()), + ) + } return renderContentBlock( app, content, diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go index 22cb97fb5..ff279821f 100644 --- a/packages/tui/internal/components/chat/messages.go +++ b/packages/tui/internal/components/chat/messages.go @@ -369,6 +369,7 @@ func (m *messagesComponent) renderView() tea.Cmd { m.showToolDetails, width, files, + false, fileParts, agentParts, ) @@ -448,6 +449,7 @@ func (m *messagesComponent) renderView() tea.Cmd { m.showToolDetails, width, "", + false, []opencode.FilePart{}, []opencode.AgentPart{}, toolCallParts..., @@ -469,6 +471,7 @@ func (m *messagesComponent) renderView() tea.Cmd { m.showToolDetails, width, "", + false, []opencode.FilePart{}, []opencode.AgentPart{}, toolCallParts..., @@ -546,6 +549,35 @@ func (m *messagesComponent) renderView() tea.Cmd { lineCount += lipgloss.Height(content) + 1 blocks = append(blocks, content) } + case opencode.ReasoningPart: + if reverted { + continue + } + text := "..." + if part.Text != "" { + text = part.Text + } + content = renderText( + m.app, + message.Info, + text, + casted.ModelID, + m.showToolDetails, + width, + "", + true, + []opencode.FilePart{}, + []opencode.AgentPart{}, + ) + content = lipgloss.PlaceHorizontal( + m.width, + lipgloss.Center, + content, + styles.WhitespaceStyle(t.Background()), + ) + partCount++ + lineCount += lipgloss.Height(content) + 1 + blocks = append(blocks, content) } } } diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 5f178e15a..639d15d04 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -423,6 +423,8 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch casted := p.(type) { case opencode.TextPart: return casted.ID == msg.Properties.Part.ID + case opencode.ReasoningPart: + return casted.ID == msg.Properties.Part.ID case opencode.FilePart: return casted.ID == msg.Properties.Part.ID case opencode.ToolPart: @@ -461,6 +463,8 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch casted := p.(type) { case opencode.TextPart: return casted.ID == msg.Properties.PartID + case opencode.ReasoningPart: + return casted.ID == msg.Properties.PartID case opencode.FilePart: return casted.ID == msg.Properties.PartID case opencode.ToolPart: diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index 4a75f737a..47632492d 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -61,7 +61,7 @@ export default function Share(props: { const [store, setStore] = createStore<{ info?: Session.Info messages: Record - }>({ info: props.info, messages: mapValues(props.messages, (x: any) => "metadata" in x ? fromV1(x) : x) }) + }>({ info: props.info, messages: mapValues(props.messages, (x: any) => ("metadata" in x ? fromV1(x) : x)) }) const messages = createMemo(() => Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id))) const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>(["disconnected", "Disconnected"]) createEffect(() => { @@ -128,12 +128,10 @@ export default function Share(props: { setStore("messages", messageID, reconcile(d.content)) } if (type === "part") { - setStore("messages", d.content.messageID, "parts", arr => { + setStore("messages", d.content.messageID, "parts", (arr) => { const index = arr.findIndex((x) => x.id === d.content.id) - if (index === -1) - arr.push(d.content) - if (index > -1) - arr[index] = d.content + if (index === -1) arr.push(d.content) + if (index > -1) arr[index] = d.content return [...arr] }) } @@ -350,7 +348,7 @@ export default function Share(props: { if (x.type === "tool" && (x.state.status === "pending" || x.state.status === "running")) return false return true - }) + }), ) return ( diff --git a/packages/web/src/components/icons/custom.tsx b/packages/web/src/components/icons/custom.tsx index ba06ddfb3..8023032e5 100644 --- a/packages/web/src/components/icons/custom.tsx +++ b/packages/web/src/components/icons/custom.tsx @@ -54,7 +54,10 @@ export function IconOpencode(props: JSX.SvgSVGAttributes) { export function IconMeta(props: JSX.SvgSVGAttributes) { return ( - + ) } @@ -63,6 +66,22 @@ export function IconMeta(props: JSX.SvgSVGAttributes) { export function IconRobot(props: JSX.SvgSVGAttributes) { return ( - + + + ) +} + +// https://icones.js.org/collection/ri?s=brain&icon=ri:brain-2-line +export function IconBrain(props: JSX.SvgSVGAttributes) { + return ( + + + ) } diff --git a/packages/web/src/components/share/part.module.css b/packages/web/src/components/share/part.module.css index ffae0c3b7..3dd321425 100644 --- a/packages/web/src/components/share/part.module.css +++ b/packages/web/src/components/share/part.module.css @@ -128,6 +128,29 @@ max-width: var(--md-tool-width); } + [data-component="assistant-reasoning"] { + min-width: 0; + display: flex; + flex-direction: column; + gap: 1rem; + flex-grow: 1; + max-width: var(--md-tool-width); + + & > [data-component="assistant-reasoning-markdown"] { + align-self: flex-start; + font-size: 0.875rem; + border: 1px solid var(--sl-color-blue-high); + padding: 0.5rem calc(0.5rem + 3px); + border-radius: 0.25rem; + position: relative; + + [data-component="copy-button"] { + top: 0.5rem; + right: calc(0.5rem - 1px); + } + } + } + [data-component="assistant-text"] { min-width: 0; display: flex; diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index 772a80dc6..30f927fc4 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -19,7 +19,7 @@ import { IconMagnifyingGlass, IconDocumentMagnifyingGlass, } from "../icons" -import { IconMeta, IconRobot, IconOpenAI, IconGemini, IconAnthropic } from "../icons/custom" +import { IconMeta, IconRobot, IconOpenAI, IconGemini, IconAnthropic, IconBrain } from "../icons/custom" import { ContentCode } from "./content-code" import { ContentDiff } from "./content-diff" import { ContentText } from "./content-text" @@ -83,6 +83,9 @@ export function Part(props: PartProps) { > {(model) => } + + + @@ -157,6 +160,13 @@ export function Part(props: PartProps) { )}
)} + {props.message.role === "assistant" && props.part.type === "reasoning" && ( +
+
+ +
+
+ )} {props.message.role === "user" && props.part.type === "file" && (
Attachment
From 3ab4f42ebb2bee72f6111b5dc253545f75a9c466 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 10 Aug 2025 20:30:25 -0400 Subject: [PATCH 040/178] support agent options --- packages/opencode/src/agent/agent.ts | 10 ++++++++++ packages/opencode/src/config/config.ts | 1 + packages/opencode/src/session/index.ts | 25 +++++++++++++------------ packages/plugin/src/index.ts | 2 +- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index 6fee2fdd5..ede8fbf7e 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -14,6 +14,7 @@ export namespace Agent { mode: z.union([z.literal("subagent"), z.literal("primary"), z.literal("all")]), topP: z.number().optional(), temperature: z.number().optional(), + options: z.record(z.any()), model: z .object({ modelID: z.string(), @@ -39,15 +40,18 @@ export namespace Agent { todoread: false, todowrite: false, }, + options: {}, mode: "subagent", }, build: { name: "build", tools: {}, + options: {}, mode: "primary", }, plan: { name: "plan", + options: {}, tools: { write: false, edit: false, @@ -66,6 +70,7 @@ export namespace Agent { item = result[key] = { name: key, mode: "all", + options: {}, tools: {}, } if (value.model) item.model = Provider.parseModel(value.model) @@ -79,6 +84,11 @@ export namespace Agent { if (value.temperature != undefined) item.temperature = value.temperature if (value.top_p != undefined) item.topP = value.top_p if (value.mode) item.mode = value.mode + if (value.options) + item.options = { + ...item.options, + ...value.options, + } } return result }) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 2ba82904f..96a662b29 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -173,6 +173,7 @@ export namespace Config { tools: z.record(z.string(), z.boolean()).optional(), disable: z.boolean().optional(), description: z.string().optional().describe("Description of when to use the agent"), + options: z.record(z.string(), z.any()).optional().describe("Additional model options passed through to provider"), mode: z.union([z.literal("subagent"), z.literal("primary"), z.literal("all")]).optional(), }) .openapi({ diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 583f3df7f..3d6bf98c6 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -852,20 +852,24 @@ export namespace Session { tools[key] = item } - const params = { - temperature: model.info.temperature - ? (agent.temperature ?? ProviderTransform.temperature(input.providerID, input.modelID)) - : undefined, - topP: agent.topP ?? ProviderTransform.topP(input.providerID, input.modelID), - } - await Plugin.trigger( + const params = await Plugin.trigger( "chat.params", { model: model.info, provider: await Provider.getProvider(input.providerID), message: userMsg, }, - params, + { + temperature: model.info.temperature + ? (agent.temperature ?? ProviderTransform.temperature(input.providerID, input.modelID)) + : undefined, + topP: agent.topP ?? ProviderTransform.topP(input.providerID, input.modelID), + options: { + ...ProviderTransform.options(input.providerID, input.modelID), + ...model.info.options, + ...agent.options, + }, + }, ) const stream = streamText({ onError(e) { @@ -946,10 +950,7 @@ export namespace Session { return false }, providerOptions: { - [input.providerID]: { - ...ProviderTransform.options(input.providerID, input.modelID), - ...model.info.options, - }, + [input.providerID]: params.options, }, temperature: params.temperature, topP: params.topP, diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index d0d484a00..7ea82d2aa 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -19,7 +19,7 @@ export interface Hooks { */ "chat.params"?: ( input: { model: Model; provider: Provider; message: UserMessage }, - output: { temperature: number; topP: number }, + output: { temperature: number; topP: number; options: Record }, ) => Promise "permission.ask"?: (input: Permission, output: { status: "ask" | "deny" | "allow" }) => Promise "tool.execute.before"?: ( From 13d3fba86bfe7c582df9297fa0a941d56aeb6a61 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 10 Aug 2025 20:47:11 -0400 Subject: [PATCH 041/178] switch gpt-5 to codex prompt --- packages/opencode/src/plugin/index.ts | 5 +++-- packages/opencode/src/session/index.ts | 8 +++++--- packages/opencode/src/session/message-v2.ts | 12 +++++------- packages/opencode/src/session/system.ts | 2 ++ 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/opencode/src/plugin/index.ts b/packages/opencode/src/plugin/index.ts index 3ffa30191..bb6cc07bc 100644 --- a/packages/opencode/src/plugin/index.ts +++ b/packages/opencode/src/plugin/index.ts @@ -4,7 +4,8 @@ import { Config } from "../config/config" import { Bus } from "../bus" import { Log } from "../util/log" import { createOpencodeClient } from "@opencode-ai/sdk" -import { Server } from "../server/server" +// Lazy import to avoid circular dependency with session/tool registry +// import { Server } from "../server/server" import { BunProc } from "../bun" export namespace Plugin { @@ -13,7 +14,7 @@ export namespace Plugin { const state = App.state("plugin", async (app) => { const client = createOpencodeClient({ baseUrl: "http://localhost:4096", - fetch: async (...args) => Server.app().fetch(...args), + fetch: async (...args) => (await import("../server/server")).Server.app().fetch(...args), }) const config = await Config.get() const hooks = [] diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 3d6bf98c6..594663f91 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -1007,7 +1007,7 @@ export namespace Session { async process(stream: StreamTextResult, never>) { try { let currentText: MessageV2.TextPart | undefined - let reasoningMap: Record = {} + // let reasoningMap: Record = {} for await (const value of stream.fullStream) { log.info("part", { @@ -1017,6 +1017,7 @@ export namespace Session { case "start": break + /* case "reasoning-start": if (value.id in reasoningMap) { continue @@ -1045,15 +1046,16 @@ export namespace Session { if (value.id in reasoningMap) { const part = reasoningMap[value.id] part.text = part.text.trimEnd() - part.providerMetadata = value.providerMetadata + part.metadata = value.providerMetadata part.time = { - start: Date.now(), + ...part.time, end: Date.now(), } await updatePart(part) delete reasoningMap[value.id] } break + */ case "tool-input-start": const part = await updatePart({ diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index a1daf9b9a..6fabd5219 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -121,13 +121,11 @@ export namespace MessageV2 { export const ReasoningPart = PartBase.extend({ type: z.literal("reasoning"), text: z.string(), - providerMetadata: z.record(z.any()).optional(), - time: z - .object({ - start: z.number(), - end: z.number().optional(), - }) - .optional(), + metadata: z.record(z.any()).optional(), + time: z.object({ + start: z.number(), + end: z.number().optional(), + }), }).openapi({ ref: "ReasoningPart", }) diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index a9b167be4..ec27dc627 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -13,6 +13,7 @@ import PROMPT_GEMINI from "./prompt/gemini.txt" import PROMPT_ANTHROPIC_SPOOF from "./prompt/anthropic_spoof.txt" import PROMPT_SUMMARIZE from "./prompt/summarize.txt" import PROMPT_TITLE from "./prompt/title.txt" +import PROMPT_CODEX from "./prompt/codex.txt" export namespace SystemPrompt { export function header(providerID: string) { @@ -20,6 +21,7 @@ export namespace SystemPrompt { return [] } export function provider(modelID: string) { + if (modelID.includes("gpt-5")) return [PROMPT_CODEX] if (modelID.includes("gpt-") || modelID.includes("o1") || modelID.includes("o3")) return [PROMPT_BEAST] if (modelID.includes("gemini-")) return [PROMPT_GEMINI] if (modelID.includes("claude")) return [PROMPT_ANTHROPIC] From 95e410db88769f0cfdf64aeee9ec2ea3c42d11bc Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 11 Aug 2025 00:53:06 +0000 Subject: [PATCH 042/178] release: v0.4.3 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/sdk/js/src/gen/types.gen.ts | 68 ++++++++++++++++++++-------- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 10 files changed, 57 insertions(+), 29 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 9fc71f056..87de52eb2 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.0.0", + "version": "0.4.3", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index b48bcd741..1b68421ae 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.3.130", + "version": "0.4.3", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index b39a77723..2e8f7a803 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.0.0", + "version": "0.4.3", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 99474496c..08e288a5f 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.1", + "version": "0.4.3", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index bfd674876..e66f303d6 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.1", + "version": "0.4.3", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 15f7c3d4c..add69f1e7 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.1", + "version": "0.4.3", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 1a11d9ccd..fd4d1df80 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.1", + "version": "0.4.3", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 3a97c2835..31917e4ca 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -22,18 +22,15 @@ export type Event = | ({ type: "storage.write" } & EventStorageWrite) - | ({ - type: "file.edited" - } & EventFileEdited) - | ({ - type: "server.connected" - } & EventServerConnected) | ({ type: "permission.updated" } & EventPermissionUpdated) | ({ type: "permission.replied" } & EventPermissionReplied) + | ({ + type: "file.edited" + } & EventFileEdited) | ({ type: "session.updated" } & EventSessionUpdated) @@ -46,6 +43,9 @@ export type Event = | ({ type: "session.error" } & EventSessionError) + | ({ + type: "server.connected" + } & EventServerConnected) | ({ type: "file.watcher.updated" } & EventFileWatcherUpdated) @@ -182,6 +182,9 @@ export type Part = | ({ type: "text" } & TextPart) + | ({ + type: "reasoning" + } & ReasoningPart) | ({ type: "file" } & FilePart) @@ -217,6 +220,21 @@ export type TextPart = { } } +export type ReasoningPart = { + id: string + sessionID: string + messageID: string + type: string + text: string + metadata?: { + [key: string]: unknown + } + time: { + start: number + end?: number + } +} + export type FilePart = { id: string sessionID: string @@ -407,20 +425,6 @@ export type EventStorageWrite = { } } -export type EventFileEdited = { - type: string - properties: { - file: string - } -} - -export type EventServerConnected = { - type: string - properties: { - [key: string]: unknown - } -} - export type EventPermissionUpdated = { type: string properties: Permission @@ -451,6 +455,13 @@ export type EventPermissionReplied = { } } +export type EventFileEdited = { + type: string + properties: { + file: string + } +} + export type EventSessionUpdated = { type: string properties: { @@ -512,6 +523,13 @@ export type EventSessionError = { } } +export type EventServerConnected = { + type: string + properties: { + [key: string]: unknown + } +} + export type EventFileWatcherUpdated = { type: string properties: { @@ -691,6 +709,7 @@ export type Config = { | { [key: string]: string } + webfetch?: string } experimental?: { hook?: { @@ -888,6 +907,12 @@ export type AgentConfig = { * Description of when to use the agent */ description?: string + /** + * Additional model options passed through to provider + */ + options?: { + [key: string]: unknown + } mode?: string } @@ -1028,6 +1053,9 @@ export type Agent = { mode: string topP?: number temperature?: number + options: { + [key: string]: unknown + } model?: { modelID: string providerID: string diff --git a/packages/web/package.json b/packages/web/package.json index 8d5b82fe5..7e0eef391 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.1", + "version": "0.4.3", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 2b34f8d83..e5d83ec2d 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.1", + "version": "0.4.3", "publisher": "sst-dev", "repository": { "type": "git", From 1c83ef75a2f0bdff400f5f70c7b4780126283f36 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 11 Aug 2025 08:31:15 +0700 Subject: [PATCH 043/178] fix(plugin): prevent compiled binary hang by removing lazy dynamic import (#1794) Co-authored-by: opencode --- packages/opencode/src/plugin/index.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/plugin/index.ts b/packages/opencode/src/plugin/index.ts index bb6cc07bc..3ffa30191 100644 --- a/packages/opencode/src/plugin/index.ts +++ b/packages/opencode/src/plugin/index.ts @@ -4,8 +4,7 @@ import { Config } from "../config/config" import { Bus } from "../bus" import { Log } from "../util/log" import { createOpencodeClient } from "@opencode-ai/sdk" -// Lazy import to avoid circular dependency with session/tool registry -// import { Server } from "../server/server" +import { Server } from "../server/server" import { BunProc } from "../bun" export namespace Plugin { @@ -14,7 +13,7 @@ export namespace Plugin { const state = App.state("plugin", async (app) => { const client = createOpencodeClient({ baseUrl: "http://localhost:4096", - fetch: async (...args) => (await import("../server/server")).Server.app().fetch(...args), + fetch: async (...args) => Server.app().fetch(...args), }) const config = await Config.get() const hooks = [] From d8dc23bde9dfe5d7cba911253e51fa258d11b4b9 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 10 Aug 2025 21:34:28 -0400 Subject: [PATCH 044/178] pass through additional agent options to the provider --- packages/opencode/src/agent/agent.ts | 12 +++---- packages/opencode/src/config/config.ts | 2 +- packages/web/src/content/docs/docs/agents.mdx | 35 +++++++++++++++++++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index ede8fbf7e..b363e0e9b 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -14,7 +14,6 @@ export namespace Agent { mode: z.union([z.literal("subagent"), z.literal("primary"), z.literal("all")]), topP: z.number().optional(), temperature: z.number().optional(), - options: z.record(z.any()), model: z .object({ modelID: z.string(), @@ -23,6 +22,7 @@ export namespace Agent { .optional(), prompt: z.string().optional(), tools: z.record(z.boolean()), + options: z.record(z.string(), z.any()), }) .openapi({ ref: "Agent", @@ -73,6 +73,11 @@ export namespace Agent { options: {}, tools: {}, } + const { model, prompt, tools, description, temperature, top_p, mode, ...extra } = value + item.options = { + ...item.options, + ...extra, + } if (value.model) item.model = Provider.parseModel(value.model) if (value.prompt) item.prompt = value.prompt if (value.tools) @@ -84,11 +89,6 @@ export namespace Agent { if (value.temperature != undefined) item.temperature = value.temperature if (value.top_p != undefined) item.topP = value.top_p if (value.mode) item.mode = value.mode - if (value.options) - item.options = { - ...item.options, - ...value.options, - } } return result }) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 96a662b29..62f425cb5 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -173,9 +173,9 @@ export namespace Config { tools: z.record(z.string(), z.boolean()).optional(), disable: z.boolean().optional(), description: z.string().optional().describe("Description of when to use the agent"), - options: z.record(z.string(), z.any()).optional().describe("Additional model options passed through to provider"), mode: z.union([z.literal("subagent"), z.literal("primary"), z.literal("all")]).optional(), }) + .catchall(z.any()) .openapi({ ref: "AgentConfig", }) diff --git a/packages/web/src/content/docs/docs/agents.mdx b/packages/web/src/content/docs/docs/agents.mdx index 659c8ee5f..52bb8ed14 100644 --- a/packages/web/src/content/docs/docs/agents.mdx +++ b/packages/web/src/content/docs/docs/agents.mdx @@ -360,6 +360,41 @@ The `mode` option can be set to `primary`, `subagent`, or `all`. If no `mode` is --- +### Additional options + +Any other options you specify in your agent configuration will be passed through directly to the provider as model options. This allows you to use provider-specific features and parameters. + +```json title="opencode.json" +{ + "agent": { + "reasoning": { + "model": "openai/gpt-5-turbo", + "reasoningEffort": "high", + "textVerbosity": "medium" + } + } +} +``` + +For example, with OpenAI's reasoning models, you can control the reasoning effort: + +```json title="opencode.json" +{ + "agent": { + "deep-thinker": { + "description": "Agent that uses high reasoning effort for complex problems", + "model": "openai/gpt-5-turbo", + "reasoningEffort": "high", + "textVerbosity": "low" + } + } +} +``` + +These additional options are model and provider-specific. Check your provider's documentation for available parameters. + +--- + ## Create agents You can create new agents using the following command: From 8fde77295772c39e368df59dc7b2951db32d04de Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 10 Aug 2025 21:37:48 -0400 Subject: [PATCH 045/178] ci: smoke test --- packages/opencode/script/publish.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts index c38148b4b..cfb98fffb 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -39,6 +39,11 @@ for (const [os, arch] of targets) { "../tui", ) await $`bun build --define OPENCODE_TUI_PATH="'../../../dist/${name}/bin/tui'" --define OPENCODE_VERSION="'${version}'" --compile --target=bun-${os}-${arch} --outfile=dist/${name}/bin/opencode ./src/index.ts` + // Run the binary only if it matches current OS/arch + if ((process.platform === (os === "windows" ? "win32" : os)) && (process.arch === arch || (process.arch === "x64" && arch === "x64-baseline"))) { + console.log(`smoke test: running dist/${name}/bin/opencode --version`) + await $`./dist/${name}/bin/opencode --version` + } await $`rm -rf ./dist/${name}/bin/tui` await Bun.file(`dist/${name}/package.json`).write( JSON.stringify( From 8cc427daba2cdb689062eb412455af25ff621794 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 10 Aug 2025 21:40:49 -0400 Subject: [PATCH 046/178] docs: docs agent --- .opencode/agent/docs.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.opencode/agent/docs.md b/.opencode/agent/docs.md index 8ba4b52d4..39cb4743a 100644 --- a/.opencode/agent/docs.md +++ b/.opencode/agent/docs.md @@ -1,4 +1,6 @@ --- +model: openai/gpt-5 +reasoningEffort: medium description: ALWAYS use this when writing docs --- From 70db372466bdf1037f78bb839ff2018db19f30e1 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 10 Aug 2025 21:52:52 -0400 Subject: [PATCH 047/178] add OPENCODE_DISABLE_AUTOUPDATE flag --- packages/opencode/src/cli/cmd/tui.ts | 3 ++- packages/opencode/src/flag/flag.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui.ts b/packages/opencode/src/cli/cmd/tui.ts index 7a9c30655..d9bba2028 100644 --- a/packages/opencode/src/cli/cmd/tui.ts +++ b/packages/opencode/src/cli/cmd/tui.ts @@ -13,6 +13,7 @@ import { Log } from "../../util/log" import { FileWatcher } from "../../file/watch" import { Ide } from "../../ide" import { Agent } from "../../agent/agent" +import { Flag } from "../../flag/flag" declare global { const OPENCODE_TUI_PATH: string @@ -126,7 +127,7 @@ export const TuiCommand = cmd({ if (Installation.isDev()) return if (Installation.isSnapshot()) return const config = await Config.global() - if (config.autoupdate === false) return + if (config.autoupdate === false || Flag.OPENCODE_DISABLE_AUTOUPDATE) return const latest = await Installation.latest().catch(() => {}) if (!latest) return if (Installation.VERSION === latest) return diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts index afc610b66..b39ce6a9e 100644 --- a/packages/opencode/src/flag/flag.ts +++ b/packages/opencode/src/flag/flag.ts @@ -2,6 +2,7 @@ export namespace Flag { export const OPENCODE_AUTO_SHARE = truthy("OPENCODE_AUTO_SHARE") export const OPENCODE_DISABLE_WATCHER = truthy("OPENCODE_DISABLE_WATCHER") export const OPENCODE_CONFIG = process.env["OPENCODE_CONFIG"] + export const OPENCODE_DISABLE_AUTOUPDATE = truthy("OPENCODE_DISABLE_AUTOUPDATE") function truthy(key: string) { const value = process.env[key]?.toLowerCase() From 55c294c013ae1569dba96b41d1950f5a1a29b6e5 Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 11 Aug 2025 01:59:27 +0000 Subject: [PATCH 048/178] release: v0.4.6 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/sdk/js/src/gen/types.gen.ts | 62 +++++++++++++++------------- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 10 files changed, 42 insertions(+), 38 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 87de52eb2..51a149560 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.3", + "version": "0.4.6", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 1b68421ae..41d0a9e30 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.3", + "version": "0.4.6", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 2e8f7a803..87408d49b 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.3", + "version": "0.4.6", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 08e288a5f..84f183dec 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.3", + "version": "0.4.6", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index e66f303d6..1b90ec517 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.3", + "version": "0.4.6", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index add69f1e7..c9fff7f3e 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.3", + "version": "0.4.6", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index fd4d1df80..dae5ccadc 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.3", + "version": "0.4.6", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 31917e4ca..5eb197954 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -22,15 +22,18 @@ export type Event = | ({ type: "storage.write" } & EventStorageWrite) + | ({ + type: "file.edited" + } & EventFileEdited) + | ({ + type: "server.connected" + } & EventServerConnected) | ({ type: "permission.updated" } & EventPermissionUpdated) | ({ type: "permission.replied" } & EventPermissionReplied) - | ({ - type: "file.edited" - } & EventFileEdited) | ({ type: "session.updated" } & EventSessionUpdated) @@ -43,9 +46,6 @@ export type Event = | ({ type: "session.error" } & EventSessionError) - | ({ - type: "server.connected" - } & EventServerConnected) | ({ type: "file.watcher.updated" } & EventFileWatcherUpdated) @@ -425,6 +425,20 @@ export type EventStorageWrite = { } } +export type EventFileEdited = { + type: string + properties: { + file: string + } +} + +export type EventServerConnected = { + type: string + properties: { + [key: string]: unknown + } +} + export type EventPermissionUpdated = { type: string properties: Permission @@ -455,13 +469,6 @@ export type EventPermissionReplied = { } } -export type EventFileEdited = { - type: string - properties: { - file: string - } -} - export type EventSessionUpdated = { type: string properties: { @@ -523,13 +530,6 @@ export type EventSessionError = { } } -export type EventServerConnected = { - type: string - properties: { - [key: string]: unknown - } -} - export type EventFileWatcherUpdated = { type: string properties: { @@ -907,13 +907,17 @@ export type AgentConfig = { * Description of when to use the agent */ description?: string - /** - * Additional model options passed through to provider - */ - options?: { - [key: string]: unknown - } mode?: string + [key: string]: + | unknown + | string + | number + | { + [key: string]: boolean + } + | boolean + | string + | undefined } export type Provider = { @@ -1053,9 +1057,6 @@ export type Agent = { mode: string topP?: number temperature?: number - options: { - [key: string]: unknown - } model?: { modelID: string providerID: string @@ -1064,6 +1065,9 @@ export type Agent = { tools: { [key: string]: boolean } + options: { + [key: string]: unknown + } } export type EventSubscribeData = { diff --git a/packages/web/package.json b/packages/web/package.json index 7e0eef391..fe6e8ab15 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.3", + "version": "0.4.6", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index e5d83ec2d..a062a39aa 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.3", + "version": "0.4.6", "publisher": "sst-dev", "repository": { "type": "git", From 409a6f93b2b79bf52c66889c653a1312ee0cccc5 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sun, 10 Aug 2025 21:17:12 -0500 Subject: [PATCH 049/178] fix: enforce field requirement for cli cmds (#1796) --- packages/opencode/src/cli/cmd/agent.ts | 2 +- packages/opencode/src/cli/cmd/auth.ts | 8 ++++---- packages/opencode/src/cli/cmd/mcp.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/opencode/src/cli/cmd/agent.ts b/packages/opencode/src/cli/cmd/agent.ts index f96da6cfd..0dd6311d7 100644 --- a/packages/opencode/src/cli/cmd/agent.ts +++ b/packages/opencode/src/cli/cmd/agent.ts @@ -39,7 +39,7 @@ const AgentCreateCommand = cmd({ const query = await prompts.text({ message: "Description", placeholder: "What should this agent do?", - validate: (x) => x && (x.length > 0 ? undefined : "Required"), + validate: (x) => (x && x.length > 0 ? undefined : "Required"), }) if (prompts.isCancel(query)) throw new UI.CancelledError() diff --git a/packages/opencode/src/cli/cmd/auth.ts b/packages/opencode/src/cli/cmd/auth.ts index ff99089cc..dab0bfd50 100644 --- a/packages/opencode/src/cli/cmd/auth.ts +++ b/packages/opencode/src/cli/cmd/auth.ts @@ -139,7 +139,7 @@ export const AuthLoginCommand = cmd({ if (provider === "other") { provider = await prompts.text({ message: "Enter provider id", - validate: (x) => x && (x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only"), + validate: (x) => (x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only"), }) if (prompts.isCancel(provider)) throw new UI.CancelledError() provider = provider.replace(/^@ai-sdk\//, "") @@ -193,7 +193,7 @@ export const AuthLoginCommand = cmd({ const code = await prompts.text({ message: "Paste the authorization code here: ", - validate: (x) => x && (x.length > 0 ? undefined : "Required"), + validate: (x) => (x && x.length > 0 ? undefined : "Required"), }) if (prompts.isCancel(code)) throw new UI.CancelledError() @@ -229,7 +229,7 @@ export const AuthLoginCommand = cmd({ const code = await prompts.text({ message: "Paste the authorization code here: ", - validate: (x) => x && (x.length > 0 ? undefined : "Required"), + validate: (x) => (x && x.length > 0 ? undefined : "Required"), }) if (prompts.isCancel(code)) throw new UI.CancelledError() @@ -302,7 +302,7 @@ export const AuthLoginCommand = cmd({ const key = await prompts.password({ message: "Enter your API key", - validate: (x) => x && (x.length > 0 ? undefined : "Required"), + validate: (x) => (x && x.length > 0 ? undefined : "Required"), }) if (prompts.isCancel(key)) throw new UI.CancelledError() await Auth.set(provider, { diff --git a/packages/opencode/src/cli/cmd/mcp.ts b/packages/opencode/src/cli/cmd/mcp.ts index 6e2d11fdc..df0046b23 100644 --- a/packages/opencode/src/cli/cmd/mcp.ts +++ b/packages/opencode/src/cli/cmd/mcp.ts @@ -19,7 +19,7 @@ export const McpAddCommand = cmd({ const name = await prompts.text({ message: "Enter MCP server name", - validate: (x) => x && (x.length > 0 ? undefined : "Required"), + validate: (x) => (x && x.length > 0 ? undefined : "Required"), }) if (prompts.isCancel(name)) throw new UI.CancelledError() @@ -44,7 +44,7 @@ export const McpAddCommand = cmd({ const command = await prompts.text({ message: "Enter command to run", placeholder: "e.g., opencode x @modelcontextprotocol/server-filesystem", - validate: (x) => x && (x.length > 0 ? undefined : "Required"), + validate: (x) => (x && x.length > 0 ? undefined : "Required"), }) if (prompts.isCancel(command)) throw new UI.CancelledError() From 81534ab387c9cf7d3e3c1e06d796554f2c81668c Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 10 Aug 2025 22:15:07 -0400 Subject: [PATCH 050/178] ci: tweaks --- packages/opencode/script/publish.ts | 39 +++-------------------------- script/publish.ts | 37 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts index cfb98fffb..b0520fb5a 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -40,7 +40,10 @@ for (const [os, arch] of targets) { ) await $`bun build --define OPENCODE_TUI_PATH="'../../../dist/${name}/bin/tui'" --define OPENCODE_VERSION="'${version}'" --compile --target=bun-${os}-${arch} --outfile=dist/${name}/bin/opencode ./src/index.ts` // Run the binary only if it matches current OS/arch - if ((process.platform === (os === "windows" ? "win32" : os)) && (process.arch === arch || (process.arch === "x64" && arch === "x64-baseline"))) { + if ( + process.platform === (os === "windows" ? "win32" : os) && + (process.arch === arch || (process.arch === "x64" && arch === "x64-baseline")) + ) { console.log(`smoke test: running dist/${name}/bin/opencode --version`) await $`./dist/${name}/bin/opencode --version` } @@ -84,44 +87,10 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write( if (!dry) await $`cd ./dist/${pkg.name} && bun publish --access public --tag ${npmTag}` if (!snapshot) { - // Github Release for (const key of Object.keys(optionalDependencies)) { await $`cd dist/${key}/bin && zip -r ../../${key}.zip *` } - const previous = await fetch("https://api.github.com/repos/sst/opencode/releases/latest") - .then((res) => { - if (!res.ok) throw new Error(res.statusText) - return res.json() - }) - .then((data) => data.tag_name) - - console.log("finding commits between", previous, "and", "HEAD") - const commits = await fetch(`https://api.github.com/repos/sst/opencode/compare/${previous}...HEAD`) - .then((res) => res.json()) - .then((data) => data.commits || []) - - const raw = commits.map((commit: any) => `- ${commit.commit.message.split("\n").join(" ")}`) - console.log(raw) - - const notes = - raw - .filter((x: string) => { - const lower = x.toLowerCase() - return ( - !lower.includes("release:") && - !lower.includes("ignore:") && - !lower.includes("chore:") && - !lower.includes("ci:") && - !lower.includes("wip:") && - !lower.includes("docs:") && - !lower.includes("doc:") - ) - }) - .join("\n") || "No notable changes" - - if (!dry) await $`gh release create v${version} --title "v${version}" --notes ${notes} ./dist/*.zip` - // Calculate SHA values const arm64Sha = await $`sha256sum ./dist/opencode-linux-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) const x64Sha = await $`sha256sum ./dist/opencode-linux-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) diff --git a/script/publish.ts b/script/publish.ts index 7a48749d1..7a1ec0601 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -1,6 +1,7 @@ #!/usr/bin/env bun import { $ } from "bun" +import path from "path" console.log("=== publishing ===\n") @@ -38,10 +39,46 @@ await import(`../packages/sdk/js/script/publish.ts`) console.log("\n=== plugin ===\n") await import(`../packages/plugin/script/publish.ts`) +const dir = new URL("..", import.meta.url).pathname +process.chdir(dir) + if (!snapshot) { await $`git commit -am "release: v${version}"` await $`git tag v${version}` await $`git push origin HEAD --tags --no-verify` + + const previous = await fetch("https://api.github.com/repos/sst/opencode/releases/latest") + .then((res) => { + if (!res.ok) throw new Error(res.statusText) + return res.json() + }) + .then((data) => data.tag_name) + + console.log("finding commits between", previous, "and", "HEAD") + const commits = await fetch(`https://api.github.com/repos/sst/opencode/compare/${previous}...HEAD`) + .then((res) => res.json()) + .then((data) => data.commits || []) + + const raw = commits.map((commit: any) => `- ${commit.commit.message.split("\n").join(" ")}`) + console.log(raw) + + const notes = + raw + .filter((x: string) => { + const lower = x.toLowerCase() + return ( + !lower.includes("release:") && + !lower.includes("ignore:") && + !lower.includes("chore:") && + !lower.includes("ci:") && + !lower.includes("wip:") && + !lower.includes("docs:") && + !lower.includes("doc:") + ) + }) + .join("\n") || "No notable changes" + + await $`gh release create v${version} --title "v${version}" --notes ${notes} ./packages/opencode/dist/*.zip` } if (snapshot) { await $`git checkout -b snapshot-${version}` From fce04dc48b87b35ee66814632329ab9859947c99 Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 11 Aug 2025 02:30:21 +0000 Subject: [PATCH 051/178] release: v0.4.11 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 51a149560..e9e957cf5 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.6", + "version": "0.4.11", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 41d0a9e30..212596bb9 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.6", + "version": "0.4.11", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 87408d49b..f3875a006 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.6", + "version": "0.4.11", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 84f183dec..6e9e07306 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.6", + "version": "0.4.11", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 1b90ec517..12c89d69e 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.6", + "version": "0.4.11", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index c9fff7f3e..765d8dfda 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.6", + "version": "0.4.11", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index dae5ccadc..00b7e576f 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.6", + "version": "0.4.11", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index fe6e8ab15..a6f4bf05e 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.6", + "version": "0.4.11", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index a062a39aa..6e3496054 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.6", + "version": "0.4.11", "publisher": "sst-dev", "repository": { "type": "git", From 457386ad08c8add2b8f9c80434582785ca07c557 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 01:14:46 -0400 Subject: [PATCH 052/178] fix plan mode bash tool making changes --- packages/opencode/src/session/index.ts | 22 +++++++++---------- packages/opencode/src/session/prompt/plan.txt | 7 +++++- packages/opencode/src/session/system.ts | 1 + 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 594663f91..5565f3e0a 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -15,6 +15,7 @@ import { } from "ai" import PROMPT_INITIALIZE from "../session/prompt/initialize.txt" +import PROMPT_PLAN from "../session/prompt/plan.txt" import { App } from "../app/app" import { Bus } from "../bus" @@ -607,17 +608,6 @@ export namespace Session { ] }), ).then((x) => x.flat()) - /* - if (inputAgent === "plan") - userParts.push({ - id: Identifier.ascending("part"), - messageID: userMsg.id, - sessionID: input.sessionID, - type: "text", - text: PROMPT_PLAN, - synthetic: true, - }) - */ await Plugin.trigger( "chat.message", {}, @@ -720,6 +710,16 @@ export namespace Session { } const agent = await Agent.get(inputAgent) + if (agent.name === "plan") { + msgs.at(-1)?.parts.push({ + id: Identifier.ascending("part"), + messageID: userMsg.id, + sessionID: input.sessionID, + type: "text", + text: PROMPT_PLAN, + synthetic: true, + }) + } let system = SystemPrompt.header(input.providerID) system.push( ...(() => { diff --git a/packages/opencode/src/session/prompt/plan.txt b/packages/opencode/src/session/prompt/plan.txt index f0e02d266..b17ca1d86 100644 --- a/packages/opencode/src/session/prompt/plan.txt +++ b/packages/opencode/src/session/prompt/plan.txt @@ -1,3 +1,8 @@ -Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supersedes any other instructions you have received (for example, to make edits). +Plan mode is active. The user indicated that they do not want you to execute yet +-- you MUST NOT make any edits, run any non-readonly tools (including changing +configs or making commits), or otherwise make any changes to the system. This +supersedes any other instructions you have received (for example, to make +edits). Bash tool must only run readonly commands + diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index ec27dc627..2f767c120 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -20,6 +20,7 @@ export namespace SystemPrompt { if (providerID.includes("anthropic")) return [PROMPT_ANTHROPIC_SPOOF.trim()] return [] } + export function provider(modelID: string) { if (modelID.includes("gpt-5")) return [PROMPT_CODEX] if (modelID.includes("gpt-") || modelID.includes("o1") || modelID.includes("o3")) return [PROMPT_BEAST] From 061ba65d20e0c7a7c536fbce0cc5c5bd16dd5ae1 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 01:23:00 -0400 Subject: [PATCH 053/178] show combined output of bash tool progressively --- packages/opencode/src/tool/bash.ts | 47 +++++++++++++++---- packages/opencode/test/tool/bash.test.ts | 2 +- .../tui/internal/components/chat/message.go | 10 ++-- packages/web/src/components/share/part.tsx | 2 +- 4 files changed, 44 insertions(+), 17 deletions(-) diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index de1eedaa8..f6368ea14 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -1,6 +1,6 @@ import { z } from "zod" import { exec } from "child_process" -import { text } from "stream/consumers" + import { Tool } from "./tool" import DESCRIPTION from "./bash.txt" import { App } from "../app/app" @@ -130,8 +130,35 @@ export const BashTool = Tool.define("bash", { timeout, }) - const stdoutPromise = text(process.stdout!) - const stderrPromise = text(process.stderr!) + let output = "" + + // Initialize metadata with empty output + ctx.metadata({ + metadata: { + output: "", + description: params.description, + }, + }) + + process.stdout?.on("data", (chunk) => { + output += chunk.toString() + ctx.metadata({ + metadata: { + output: output, + description: params.description, + }, + }) + }) + + process.stderr?.on("data", (chunk) => { + output += chunk.toString() + ctx.metadata({ + metadata: { + output: output, + description: params.description, + }, + }) + }) await new Promise((resolve) => { process.on("close", () => { @@ -139,18 +166,22 @@ export const BashTool = Tool.define("bash", { }) }) - const stdout = await stdoutPromise - const stderr = await stderrPromise + ctx.metadata({ + metadata: { + output: output, + exit: process.exitCode, + description: params.description, + }, + }) return { title: params.command, metadata: { - stderr, - stdout, + output, exit: process.exitCode, description: params.description, }, - output: [``, stdout ?? "", ``, ``, stderr ?? "", ``].join("\n"), + output, } }, }) diff --git a/packages/opencode/test/tool/bash.test.ts b/packages/opencode/test/tool/bash.test.ts index 016a6fe9e..e19949d6a 100644 --- a/packages/opencode/test/tool/bash.test.ts +++ b/packages/opencode/test/tool/bash.test.ts @@ -27,7 +27,7 @@ describe("tool.bash", () => { ctx, ) expect(result.metadata.exit).toBe(0) - expect(result.metadata.stdout).toContain("test") + expect(result.metadata.output).toContain("test") }) }) diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index 66f8d728e..ee3535ad8 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -569,13 +569,9 @@ func renderToolDetails( case "bash": command := toolInputMap["command"].(string) body = fmt.Sprintf("```console\n$ %s\n", command) - stdout := metadata["stdout"] - if stdout != nil { - body += ansi.Strip(fmt.Sprintf("%s", stdout)) - } - stderr := metadata["stderr"] - if stderr != nil { - body += ansi.Strip(fmt.Sprintf("%s", stderr)) + output := metadata["output"] + if output != nil { + body += ansi.Strip(fmt.Sprintf("%s", output)) } body += "```" body = util.ToMarkdown(body, width, backgroundColor) diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index 30f927fc4..5cfd3a045 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -605,7 +605,7 @@ export function BashTool(props: ToolProps) { return ( ) From 4580c88c0b38519e8187d2df1035e9538b51ec2a Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 11 Aug 2025 05:28:22 +0000 Subject: [PATCH 054/178] release: v0.4.12 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index e9e957cf5..a83174b6e 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.11", + "version": "0.4.12", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 212596bb9..7660ba221 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.11", + "version": "0.4.12", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index f3875a006..5b7973f92 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.11", + "version": "0.4.12", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 6e9e07306..9bd5b5725 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.11", + "version": "0.4.12", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 12c89d69e..c907d6062 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.11", + "version": "0.4.12", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 765d8dfda..f62c6e401 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.11", + "version": "0.4.12", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 00b7e576f..b33eed638 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.11", + "version": "0.4.12", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index a6f4bf05e..ff5531563 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.11", + "version": "0.4.12", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 6e3496054..07d02effb 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.11", + "version": "0.4.12", "publisher": "sst-dev", "repository": { "type": "git", From 6145dfcca0b6c611abef992610fd57370f9e3886 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 01:44:30 -0400 Subject: [PATCH 055/178] fix run command to be less messy --- packages/opencode/src/cli/cmd/run.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index 931940881..fc8df2ab8 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -84,10 +84,6 @@ export const RunCommand = cmd({ return } - UI.empty() - UI.println(UI.logo()) - UI.empty() - const cfg = await Config.get() if (cfg.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share) { try { @@ -101,7 +97,6 @@ export const RunCommand = cmd({ } } } - UI.empty() const agent = await (async () => { if (args.agent) return Agent.get(args.agent) @@ -116,9 +111,6 @@ export const RunCommand = cmd({ return Provider.defaultModel() })() - UI.println(UI.Style.TEXT_NORMAL_BOLD + "@ ", UI.Style.TEXT_NORMAL + `${providerID}/${modelID}`) - UI.empty() - function printEvent(color: string, type: string, title: string) { UI.println( color + `|`, @@ -137,7 +129,8 @@ export const RunCommand = cmd({ if (part.type === "tool" && part.state.status === "completed") { const [tool, color] = TOOL[part.tool] ?? [part.tool, UI.Style.TEXT_INFO_BOLD] const title = - part.state.title || Object.keys(part.state.input).length > 0 ? JSON.stringify(part.state.input) : "Unknown" + part.state.title || + (Object.keys(part.state.input).length > 0 ? JSON.stringify(part.state.input) : "Unknown") printEvent(color, tool, title) } From 62b8c7aee0cb4da28b3dbb99ed1aed4be4360127 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Mon, 11 Aug 2025 00:46:38 -0500 Subject: [PATCH 056/178] feat (tui): agents dialog (#1802) --- packages/tui/internal/app/app.go | 3 + packages/tui/internal/commands/command.go | 10 +- .../tui/internal/components/dialog/agents.go | 305 ++++++++++++++++++ packages/tui/internal/tui/tui.go | 29 ++ 4 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 packages/tui/internal/components/dialog/agents.go diff --git a/packages/tui/internal/app/app.go b/packages/tui/internal/app/app.go index 9794d0489..517cafd23 100644 --- a/packages/tui/internal/app/app.go +++ b/packages/tui/internal/app/app.go @@ -70,6 +70,9 @@ type ModelSelectedMsg struct { Provider opencode.Provider Model opencode.Model } +type AgentSelectedMsg struct { + Agent opencode.Agent +} type SessionClearedMsg struct{} type CompactSessionMsg struct{} type SendPrompt = Prompt diff --git a/packages/tui/internal/commands/command.go b/packages/tui/internal/commands/command.go index cb54bd09d..55f118aaa 100644 --- a/packages/tui/internal/commands/command.go +++ b/packages/tui/internal/commands/command.go @@ -64,12 +64,13 @@ func (r CommandRegistry) Sorted() []Command { commands = append(commands, command) } slices.SortFunc(commands, func(a, b Command) int { - // Priority order: session_new, session_share, model_list, app_help first, app_exit last + // Priority order: session_new, session_share, model_list, agent_list, app_help first, app_exit last priorityOrder := map[CommandName]int{ SessionNewCommand: 0, AppHelpCommand: 1, SessionShareCommand: 2, ModelListCommand: 3, + AgentListCommand: 4, } aPriority, aHasPriority := priorityOrder[a.Name] @@ -119,6 +120,7 @@ const ( SessionExportCommand CommandName = "session_export" ToolDetailsCommand CommandName = "tool_details" ModelListCommand CommandName = "model_list" + AgentListCommand CommandName = "agent_list" ThemeListCommand CommandName = "theme_list" FileListCommand CommandName = "file_list" FileCloseCommand CommandName = "file_close" @@ -248,6 +250,12 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry { Keybindings: parseBindings("m"), Trigger: []string{"models"}, }, + { + Name: AgentListCommand, + Description: "list agents", + Keybindings: parseBindings("a"), + Trigger: []string{"agents"}, + }, { Name: ThemeListCommand, Description: "list themes", diff --git a/packages/tui/internal/components/dialog/agents.go b/packages/tui/internal/components/dialog/agents.go new file mode 100644 index 000000000..49e3e025b --- /dev/null +++ b/packages/tui/internal/components/dialog/agents.go @@ -0,0 +1,305 @@ +package dialog + +import ( + "fmt" + "sort" + + "github.com/charmbracelet/bubbles/v2/key" + tea "github.com/charmbracelet/bubbletea/v2" + "github.com/lithammer/fuzzysearch/fuzzy" + "github.com/sst/opencode-sdk-go" + "github.com/sst/opencode/internal/app" + "github.com/sst/opencode/internal/components/list" + "github.com/sst/opencode/internal/components/modal" + "github.com/sst/opencode/internal/layout" + "github.com/sst/opencode/internal/styles" + "github.com/sst/opencode/internal/theme" + "github.com/sst/opencode/internal/util" +) + +const ( + numVisibleAgents = 10 + minAgentDialogWidth = 54 + maxAgentDialogWidth = 108 + maxDescriptionLength = 80 +) + +// AgentDialog interface for the agent selection dialog +type AgentDialog interface { + layout.Modal +} + +type agentDialog struct { + app *app.App + allAgents []opencode.Agent + width int + height int + modal *modal.Modal + searchDialog *SearchDialog + dialogWidth int +} + +// agentItem is a custom list item for agent selections +type agentItem struct { + agent opencode.Agent +} + +func (a agentItem) Render( + selected bool, + width int, + baseStyle styles.Style, +) string { + t := theme.CurrentTheme() + + itemStyle := baseStyle. + Background(t.BackgroundPanel()). + Foreground(t.Text()) + + if selected { + itemStyle = itemStyle.Foreground(t.Primary()) + } + + descStyle := baseStyle. + Foreground(t.TextMuted()). + Background(t.BackgroundPanel()) + + // Calculate available width (accounting for padding and margins) + availableWidth := width - 2 // Account for left padding + + agentName := a.agent.Name + description := a.agent.Description + if description == "" { + description = fmt.Sprintf("(%s)", a.agent.Mode) + } + + separator := " - " + + // Calculate how much space we have for the description + nameAndSeparatorLength := len(agentName) + len(separator) + descriptionMaxLength := availableWidth - nameAndSeparatorLength + + // Truncate description if it's too long + if len(description) > descriptionMaxLength && descriptionMaxLength > 3 { + description = description[:descriptionMaxLength-3] + "..." + } + + namePart := itemStyle.Render(agentName) + descPart := descStyle.Render(separator + description) + combinedText := namePart + descPart + + return baseStyle. + Background(t.BackgroundPanel()). + PaddingLeft(1). + Width(width). + Render(combinedText) +} + +func (a agentItem) Selectable() bool { + // All agents in the dialog are selectable (subagents are filtered out) + return true +} + +type agentKeyMap struct { + Enter key.Binding + Escape key.Binding +} + +var agentKeys = agentKeyMap{ + Enter: key.NewBinding( + key.WithKeys("enter"), + key.WithHelp("enter", "select agent"), + ), + Escape: key.NewBinding( + key.WithKeys("esc"), + key.WithHelp("esc", "close"), + ), +} + +func (a *agentDialog) Init() tea.Cmd { + a.setupAllAgents() + return a.searchDialog.Init() +} + +func (a *agentDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case SearchSelectionMsg: + // Handle selection from search dialog + if item, ok := msg.Item.(agentItem); ok { + return a, tea.Sequence( + util.CmdHandler(modal.CloseModalMsg{}), + util.CmdHandler( + app.AgentSelectedMsg{ + Agent: item.agent, + }), + ) + } + return a, util.CmdHandler(modal.CloseModalMsg{}) + case SearchCancelledMsg: + return a, util.CmdHandler(modal.CloseModalMsg{}) + + case SearchQueryChangedMsg: + // Update the list based on search query + items := a.buildDisplayList(msg.Query) + a.searchDialog.SetItems(items) + return a, nil + + case tea.WindowSizeMsg: + a.width = msg.Width + a.height = msg.Height + a.searchDialog.SetWidth(a.dialogWidth) + a.searchDialog.SetHeight(msg.Height) + } + + updatedDialog, cmd := a.searchDialog.Update(msg) + a.searchDialog = updatedDialog.(*SearchDialog) + return a, cmd +} + +func (a *agentDialog) View() string { + return a.searchDialog.View() +} + +func (a *agentDialog) calculateOptimalWidth(agents []opencode.Agent) int { + maxWidth := minAgentDialogWidth + + for _, agent := range agents { + // Calculate the width needed for this item: "AgentName - Description" + itemWidth := len(agent.Name) + if agent.Description != "" { + itemWidth += len(agent.Description) + 3 // " - " + } else { + itemWidth += len(string(agent.Mode)) + 3 // " (mode)" + } + + if itemWidth > maxWidth { + maxWidth = itemWidth + } + } + + maxWidth = min(maxWidth, maxAgentDialogWidth) + + return maxWidth +} + +func (a *agentDialog) setupAllAgents() { + // Get agents from the app, filtering out subagents + a.allAgents = []opencode.Agent{} + for _, agent := range a.app.Agents { + if agent.Mode != "subagent" { + a.allAgents = append(a.allAgents, agent) + } + } + + a.sortAgents() + + // Calculate optimal width based on all agents + a.dialogWidth = a.calculateOptimalWidth(a.allAgents) + + // Ensure minimum width to prevent textinput issues + a.dialogWidth = max(a.dialogWidth, minAgentDialogWidth) + + a.searchDialog = NewSearchDialog("Search agents...", numVisibleAgents) + a.searchDialog.SetWidth(a.dialogWidth) + + items := a.buildDisplayList("") + a.searchDialog.SetItems(items) +} + +func (a *agentDialog) sortAgents() { + sort.Slice(a.allAgents, func(i, j int) bool { + agentA := a.allAgents[i] + agentB := a.allAgents[j] + + // Current agent goes first + if agentA.Name == a.app.Agent().Name { + return true + } + if agentB.Name == a.app.Agent().Name { + return false + } + + // Alphabetical order for all other agents + return agentA.Name < agentB.Name + }) +} + +func (a *agentDialog) buildDisplayList(query string) []list.Item { + if query != "" { + return a.buildSearchResults(query) + } + return a.buildGroupedResults() +} + +func (a *agentDialog) buildSearchResults(query string) []list.Item { + agentNames := []string{} + agentMap := make(map[string]opencode.Agent) + + for _, agent := range a.allAgents { + // Search by name + searchStr := agent.Name + agentNames = append(agentNames, searchStr) + agentMap[searchStr] = agent + + // Search by description if available + if agent.Description != "" { + searchStr = fmt.Sprintf("%s %s", agent.Name, agent.Description) + agentNames = append(agentNames, searchStr) + agentMap[searchStr] = agent + } + } + + matches := fuzzy.RankFindFold(query, agentNames) + sort.Sort(matches) + + items := []list.Item{} + seenAgents := make(map[string]bool) + + for _, match := range matches { + agent := agentMap[match.Target] + // Create a unique key to avoid duplicates + key := agent.Name + if seenAgents[key] { + continue + } + seenAgents[key] = true + items = append(items, agentItem{agent: agent}) + } + + return items +} + +func (a *agentDialog) buildGroupedResults() []list.Item { + var items []list.Item + + items = append(items, list.HeaderItem("Agents")) + + // Add all agents (subagents are already filtered out) + for _, agent := range a.allAgents { + items = append(items, agentItem{agent: agent}) + } + + return items +} + +func (a *agentDialog) Render(background string) string { + return a.modal.Render(a.View(), background) +} + +func (s *agentDialog) Close() tea.Cmd { + return nil +} + +func NewAgentDialog(app *app.App) AgentDialog { + dialog := &agentDialog{ + app: app, + } + + dialog.setupAllAgents() + + dialog.modal = modal.New( + modal.WithTitle("Select Agent"), + modal.WithMaxWidth(dialog.dialogWidth+4), + ) + + return dialog +} diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 639d15d04..ea9f0560b 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -599,6 +599,32 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } a.app.State.UpdateModelUsage(msg.Provider.ID, msg.Model.ID) cmds = append(cmds, a.app.SaveState()) + case app.AgentSelectedMsg: + // Find the agent index + for i, agent := range a.app.Agents { + if agent.Name == msg.Agent.Name { + a.app.AgentIndex = i + break + } + } + a.app.State.Agent = msg.Agent.Name + + // Switch to the agent's preferred model if available + if model, ok := a.app.State.AgentModel[msg.Agent.Name]; ok { + for _, provider := range a.app.Providers { + if provider.ID == model.ProviderID { + a.app.Provider = &provider + for _, m := range provider.Models { + if m.ID == model.ModelID { + a.app.Model = &m + break + } + } + break + } + } + } + cmds = append(cmds, a.app.SaveState()) case dialog.ThemeSelectedMsg: a.app.State.Theme = msg.ThemeName cmds = append(cmds, a.app.SaveState()) @@ -1119,6 +1145,9 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) { case commands.ModelListCommand: modelDialog := dialog.NewModelDialog(a.app) a.modal = modelDialog + case commands.AgentListCommand: + agentDialog := dialog.NewAgentDialog(a.app) + a.modal = agentDialog case commands.ThemeListCommand: themeDialog := dialog.NewThemeDialog() a.modal = themeDialog From 8ed72ae0877884fb61bf346eb55e040f45c5cfe8 Mon Sep 17 00:00:00 2001 From: Stibbs Date: Mon, 11 Aug 2025 15:56:42 +1000 Subject: [PATCH 057/178] chore: add OPENCODE env var (#1780) --- packages/opencode/src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index d2d52d0b5..007de6509 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -58,6 +58,8 @@ const cli = yargs(hideBin(process.argv)) })(), }) + process.env["OPENCODE"] = "1" + Log.Default.info("opencode", { version: Installation.VERSION, args: process.argv.slice(2), From b223a29603f489063acced737b3004c32c1cf606 Mon Sep 17 00:00:00 2001 From: spoons-and-mirrors <212802214+spoons-and-mirrors@users.noreply.github.com> Date: Mon, 11 Aug 2025 07:59:50 +0200 Subject: [PATCH 058/178] Fix: Sanitize MCP Tool Names for Consistency in User Expectations (#1769) --- packages/opencode/src/mcp/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index 8f614b63a..664111fb2 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -149,7 +149,8 @@ export namespace MCP { for (const [clientName, client] of Object.entries(await clients())) { for (const [toolName, tool] of Object.entries(await client.tools())) { const sanitizedClientName = clientName.replace(/\s+/g, "_") - result[sanitizedClientName + "_" + toolName] = tool + const sanitizedToolName = toolName.replace(/[-\s]+/g, "_") + result[sanitizedClientName + "_" + sanitizedToolName] = tool } } return result From 6c30565d40023d001f072d76a80ed3b14149499c Mon Sep 17 00:00:00 2001 From: Jake Date: Mon, 11 Aug 2025 13:48:46 +0200 Subject: [PATCH 059/178] Add support for biome.jsonc config file (#1791) --- packages/opencode/src/format/formatter.ts | 12 +++++++++--- packages/web/src/content/docs/docs/formatters.mdx | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/format/formatter.ts b/packages/opencode/src/format/formatter.ts index 8a8bbc9aa..0869ef505 100644 --- a/packages/opencode/src/format/formatter.ts +++ b/packages/opencode/src/format/formatter.ts @@ -76,7 +76,7 @@ export const prettier: Info = { export const biome: Info = { name: "biome", - command: [BunProc.which(), "x", "biome", "format", "--write", "$FILE"], + command: [BunProc.which(), "x", "@biomejs/biome", "format", "--write", "$FILE"], environment: { BUN_BE_BUN: "1", }, @@ -110,8 +110,14 @@ export const biome: Info = { ], async enabled() { const app = App.info() - const items = await Filesystem.findUp("biome.json", app.path.cwd, app.path.root) - return items.length > 0 + const configs = ["biome.json", "biome.jsonc"] + for (const config of configs) { + const found = await Filesystem.findUp(config, app.path.cwd, app.path.root) + if (found.length > 0) { + return true + } + } + return false }, } diff --git a/packages/web/src/content/docs/docs/formatters.mdx b/packages/web/src/content/docs/docs/formatters.mdx index 720f3c1af..9c9411aeb 100644 --- a/packages/web/src/content/docs/docs/formatters.mdx +++ b/packages/web/src/content/docs/docs/formatters.mdx @@ -16,7 +16,7 @@ opencode comes with several built-in formatters for popular languages and framew | gofmt | .go | `gofmt` command available | | mix | .ex, .exs, .eex, .heex, .leex, .neex, .sface | `mix` command available | | prettier | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://prettier.io/docs/en/index.html) | `prettier` dependency in `package.json` | -| biome | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://biomejs.dev/) | `biome.json` config file | +| biome | .js, .jsx, .ts, .tsx, .html, .css, .md, .json, .yaml, and [more](https://biomejs.dev/) | `biome.json(c)` config file | | zig | .zig, .zon | `zig` command available | | clang-format | .c, .cpp, .h, .hpp, .ino, and [more](https://clang.llvm.org/docs/ClangFormat.html) | `.clang-format` config file | | ktlint | .kt, .kts | `ktlint` command available | From 8db75266d0da48bfd1257d528373549dc0814584 Mon Sep 17 00:00:00 2001 From: cvzakharchenko Date: Mon, 11 Aug 2025 13:55:45 +0200 Subject: [PATCH 060/178] Issue 1676: Don't eat up the last newline in a multi-line replacement (#1777) --- packages/opencode/src/tool/edit.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts index d3883b539..1b31b0aa4 100644 --- a/packages/opencode/src/tool/edit.ts +++ b/packages/opencode/src/tool/edit.ts @@ -188,7 +188,10 @@ export const LineTrimmedReplacer: Replacer = function* (content, find) { let matchEndIndex = matchStartIndex for (let k = 0; k < searchLines.length; k++) { - matchEndIndex += originalLines[i + k].length + 1 + matchEndIndex += originalLines[i + k].length + if (k < searchLines.length - 1) { + matchEndIndex += 1 // Add newline character except for the last line + } } yield content.substring(matchStartIndex, matchEndIndex) From 73a8356b10d537f7d2b31a9fb777192a8522ab54 Mon Sep 17 00:00:00 2001 From: spoons-and-mirrors <212802214+spoons-and-mirrors@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:00:32 +0200 Subject: [PATCH 061/178] Feat: Add F2 Keybind to Cycle Through the 5 Most Recent Models (#1778) --- packages/tui/internal/app/app.go | 33 +++++++++++++++++++++++ packages/tui/internal/commands/command.go | 6 +++++ packages/tui/internal/tui/tui.go | 5 ++++ 3 files changed, 44 insertions(+) diff --git a/packages/tui/internal/app/app.go b/packages/tui/internal/app/app.go index 517cafd23..48f4684fe 100644 --- a/packages/tui/internal/app/app.go +++ b/packages/tui/internal/app/app.go @@ -280,6 +280,39 @@ func (a *App) SwitchAgentReverse() (*App, tea.Cmd) { return a.cycleMode(false) } +func (a *App) CycleRecentModel() (*App, tea.Cmd) { + recentModels := a.State.RecentlyUsedModels + if len(recentModels) > 5 { + recentModels = recentModels[:5] + } + if len(recentModels) < 2 { + return a, toast.NewInfoToast("Need at least 2 recent models to cycle") + } + nextIndex := 0 + for i, recentModel := range recentModels { + if a.Provider != nil && a.Model != nil && recentModel.ProviderID == a.Provider.ID && recentModel.ModelID == a.Model.ID { + nextIndex = (i + 1) % len(recentModels) + break + } + } + for range recentModels { + currentRecentModel := recentModels[nextIndex%len(recentModels)] + provider, model := findModelByProviderAndModelID(a.Providers, currentRecentModel.ProviderID, currentRecentModel.ModelID) + if provider != nil && model != nil { + a.Provider, a.Model = provider, model + a.State.AgentModel[a.Agent().Name] = AgentModel{ProviderID: provider.ID, ModelID: model.ID} + return a, tea.Sequence(a.SaveState(), toast.NewSuccessToast(fmt.Sprintf("Switched to %s (%s)", model.Name, provider.Name))) + } + recentModels = append(recentModels[:nextIndex%len(recentModels)], recentModels[nextIndex%len(recentModels)+1:]...) + if len(recentModels) < 2 { + a.State.RecentlyUsedModels = recentModels + return a, tea.Sequence(a.SaveState(), toast.NewInfoToast("Not enough valid recent models to cycle")) + } + } + a.State.RecentlyUsedModels = recentModels + return a, toast.NewErrorToast("Recent model not found") +} + // findModelByFullID finds a model by its full ID in the format "provider/model" func findModelByFullID( providers []opencode.Provider, diff --git a/packages/tui/internal/commands/command.go b/packages/tui/internal/commands/command.go index 55f118aaa..516caab79 100644 --- a/packages/tui/internal/commands/command.go +++ b/packages/tui/internal/commands/command.go @@ -121,6 +121,7 @@ const ( ToolDetailsCommand CommandName = "tool_details" ModelListCommand CommandName = "model_list" AgentListCommand CommandName = "agent_list" + ModelCycleRecentCommand CommandName = "model_cycle_recent" ThemeListCommand CommandName = "theme_list" FileListCommand CommandName = "file_list" FileCloseCommand CommandName = "file_close" @@ -256,6 +257,11 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry { Keybindings: parseBindings("a"), Trigger: []string{"agents"}, }, + { + Name: ModelCycleRecentCommand, + Description: "cycle recent models", + Keybindings: parseBindings("f2"), + }, { Name: ThemeListCommand, Description: "list themes", diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index ea9f0560b..499d67c65 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -1148,6 +1148,11 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) { case commands.AgentListCommand: agentDialog := dialog.NewAgentDialog(a.app) a.modal = agentDialog + case commands.ModelCycleRecentCommand: + slog.Debug("ModelCycleRecentCommand triggered") + updated, cmd := a.app.CycleRecentModel() + a.app = updated + cmds = append(cmds, cmd) case commands.ThemeListCommand: themeDialog := dialog.NewThemeDialog() a.modal = themeDialog From 09561254a83a67dd5ad373f96e1a5e3b0d9f436d Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 11 Aug 2025 12:04:33 +0000 Subject: [PATCH 062/178] ignore: update download stats 2025-08-11 --- STATS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/STATS.md b/STATS.md index 733a7df49..057e5f2ed 100644 --- a/STATS.md +++ b/STATS.md @@ -44,3 +44,4 @@ | 2025-08-08 | 158,187 (+5,596) | 163,448 (+2,559) | 321,635 (+8,155) | | 2025-08-09 | 162,770 (+4,583) | 165,721 (+2,273) | 328,491 (+6,856) | | 2025-08-10 | 165,695 (+2,925) | 167,109 (+1,388) | 332,804 (+4,313) | +| 2025-08-11 | 169,297 (+3,602) | 167,953 (+844) | 337,250 (+4,446) | From ddacb04f99ebba52e76bc76b21dcababc9db94c8 Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 11 Aug 2025 12:49:52 +0000 Subject: [PATCH 063/178] release: v0.4.15 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index a83174b6e..dede5b56e 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.12", + "version": "0.4.15", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 7660ba221..b2737f0b6 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.12", + "version": "0.4.15", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 5b7973f92..dd2c2fa61 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.12", + "version": "0.4.15", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 9bd5b5725..2bd3254e2 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.12", + "version": "0.4.15", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index c907d6062..5f353963d 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.12", + "version": "0.4.15", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index f62c6e401..2e2d83cd7 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.12", + "version": "0.4.15", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index b33eed638..46819e5b9 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.12", + "version": "0.4.15", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index ff5531563..bd6bde085 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.12", + "version": "0.4.15", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 07d02effb..fbde580c3 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.12", + "version": "0.4.15", "publisher": "sst-dev", "repository": { "type": "git", From eea45a22fa3c2dad557f6ff87a824d02572a1105 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 08:41:33 -0400 Subject: [PATCH 064/178] ci: tweak --- script/publish.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/script/publish.ts b/script/publish.ts index 7a1ec0601..64afcf341 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -45,7 +45,9 @@ process.chdir(dir) if (!snapshot) { await $`git commit -am "release: v${version}"` await $`git tag v${version}` - await $`git push origin HEAD --tags --no-verify` + await $`git fetch origin` + await $`git cherry-pick HEAD..origin/dev`.nothrow() + await $`git push origin HEAD --tags --no-verify --force` const previous = await fetch("https://api.github.com/repos/sst/opencode/releases/latest") .then((res) => { From 776276d5a48f13b50d240fe66458db68aa6710df Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 11 Aug 2025 12:59:20 +0000 Subject: [PATCH 065/178] release: v0.4.16 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index dede5b56e..266f6c922 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.15", + "version": "0.4.16", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index b2737f0b6..e6c5083c7 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.15", + "version": "0.4.16", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index dd2c2fa61..f8aaae017 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.15", + "version": "0.4.16", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 2bd3254e2..edd93e55e 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.15", + "version": "0.4.16", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 5f353963d..13b6721e5 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.15", + "version": "0.4.16", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 2e2d83cd7..ca960ccf6 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.15", + "version": "0.4.16", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 46819e5b9..c2ed5d629 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.15", + "version": "0.4.16", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index bd6bde085..b39e8a8a9 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.15", + "version": "0.4.16", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index fbde580c3..b891158f8 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.15", + "version": "0.4.16", "publisher": "sst-dev", "repository": { "type": "git", From df7296cfe16d3c2dfffb01941354173fb5f4a812 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Mon, 11 Aug 2025 08:23:41 -0500 Subject: [PATCH 066/178] fix: instructions should be able to handle absolute paths (#1762) --- packages/opencode/src/session/system.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index 2f767c120..7e5c75e67 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -74,8 +74,22 @@ export namespace SystemPrompt { paths.add(path.join(os.homedir(), ".claude", "CLAUDE.md")) if (config.instructions) { - for (const instruction of config.instructions) { - const matches = await Filesystem.globUp(instruction, cwd, root).catch(() => []) + for (let instruction of config.instructions) { + if (instruction.startsWith("~/")) { + instruction = path.join(os.homedir(), instruction.slice(2)) + } + let matches: string[] = [] + if (path.isAbsolute(instruction)) { + matches = await Array.fromAsync( + new Bun.Glob(path.basename(instruction)).scan({ + cwd: path.dirname(instruction), + absolute: true, + onlyFiles: true, + }), + ).catch(() => []) + } else { + matches = await Filesystem.globUp(instruction, cwd, root).catch(() => []) + } matches.forEach((path) => paths.add(path)) } } From 23757f3ac09b540f31d5df2088e65e5399d92f7f Mon Sep 17 00:00:00 2001 From: Timo Clasen Date: Mon, 11 Aug 2025 15:28:03 +0200 Subject: [PATCH 067/178] fix: only load the first local and global rule file (#1761) --- packages/opencode/src/session/system.ts | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index 7e5c75e67..b2f156f14 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -54,24 +54,35 @@ export namespace SystemPrompt { ] } - const CUSTOM_FILES = [ + const LOCAL_RULE_FILES = [ "AGENTS.md", "CLAUDE.md", "CONTEXT.md", // deprecated ] + const GLOBAL_RULE_FILES = [ + path.join(Global.Path.config, "AGENTS.md"), + path.join(os.homedir(), ".claude", "CLAUDE.md"), + ] export async function custom() { const { cwd, root } = App.info().path const config = await Config.get() const paths = new Set() - for (const item of CUSTOM_FILES) { - const matches = await Filesystem.findUp(item, cwd, root) - matches.forEach((path) => paths.add(path)) + for (const localRuleFile of LOCAL_RULE_FILES) { + const matches = await Filesystem.findUp(localRuleFile, cwd, root) + if (matches.length > 0) { + matches.forEach((path) => paths.add(path)) + break + } } - paths.add(path.join(Global.Path.config, "AGENTS.md")) - paths.add(path.join(os.homedir(), ".claude", "CLAUDE.md")) + for (const globalRuleFile of GLOBAL_RULE_FILES) { + if (await Bun.file(globalRuleFile).exists()) { + paths.add(globalRuleFile) + break + } + } if (config.instructions) { for (let instruction of config.instructions) { From ab2df0ae3356d9a034d432b53f96c8cf0b83bf1b Mon Sep 17 00:00:00 2001 From: spoons-and-mirrors <212802214+spoons-and-mirrors@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:47:51 +0200 Subject: [PATCH 068/178] Feat: Implement Wrap-Around Navigation for List Selection (for Models and Tools modal) (#1768) --- packages/tui/internal/components/list/list.go | 29 ++++++----- .../tui/internal/components/list/list_test.go | 51 ++++++++++++++++--- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/packages/tui/internal/components/list/list.go b/packages/tui/internal/components/list/list.go index fd2d7d93f..a9823d0ab 100644 --- a/packages/tui/internal/components/list/list.go +++ b/packages/tui/internal/components/list/list.go @@ -173,7 +173,13 @@ func (c *listComponent[T]) moveUp() { } } - // If no selectable item found above, stay at current position + // If no selectable item found above, wrap to the bottom + for i := len(c.items) - 1; i > c.selectedIdx; i-- { + if c.isSelectable(c.items[i]) { + c.selectedIdx = i + return + } + } } // moveDown moves the selection down, skipping non-selectable items @@ -183,20 +189,19 @@ func (c *listComponent[T]) moveDown() { } originalIdx := c.selectedIdx - for { - if c.selectedIdx < len(c.items)-1 { - c.selectedIdx++ - } else { - break - } - - if c.isSelectable(c.items[c.selectedIdx]) { + // First try moving down from current position + for i := c.selectedIdx + 1; i < len(c.items); i++ { + if c.isSelectable(c.items[i]) { + c.selectedIdx = i return } + } - // Prevent infinite loop - if c.selectedIdx == originalIdx { - break + // If no selectable item found below, wrap to the top + for i := 0; i < originalIdx; i++ { + if c.isSelectable(c.items[i]) { + c.selectedIdx = i + return } } } diff --git a/packages/tui/internal/components/list/list_test.go b/packages/tui/internal/components/list/list_test.go index 663503a4a..25cca8cf4 100644 --- a/packages/tui/internal/components/list/list_test.go +++ b/packages/tui/internal/components/list/list_test.go @@ -138,15 +138,18 @@ func TestCtrlNavigation(t *testing.T) { func TestNavigationBoundaries(t *testing.T) { list := createTestList() - // Test up arrow at first item (should stay at 0) + // Test up arrow at first item (should wrap to last item) upKey := tea.KeyPressMsg{Code: tea.KeyUp} updatedModel, _ := list.Update(upKey) list = updatedModel.(*listComponent[testItem]) _, idx := list.GetSelectedItem() - if idx != 0 { - t.Errorf("Expected to stay at index 0 when pressing up at first item, got %d", idx) + if idx != 2 { + t.Errorf("Expected to wrap to index 2 when pressing up at first item, got %d", idx) } + // Move to first item + list.SetSelectedIndex(0) + // Move to last item downKey := tea.KeyPressMsg{Code: tea.KeyDown} updatedModel, _ = list.Update(downKey) @@ -158,12 +161,12 @@ func TestNavigationBoundaries(t *testing.T) { t.Errorf("Expected to be at index 2, got %d", idx) } - // Test down arrow at last item (should stay at 2) + // Test down arrow at last item (should wrap to first item) updatedModel, _ = list.Update(downKey) list = updatedModel.(*listComponent[testItem]) _, idx = list.GetSelectedItem() - if idx != 2 { - t.Errorf("Expected to stay at index 2 when pressing down at last item, got %d", idx) + if idx != 0 { + t.Errorf("Expected to wrap to index 0 when pressing down at last item, got %d", idx) } } @@ -208,3 +211,39 @@ func TestEmptyList(t *testing.T) { t.Error("Expected IsEmpty() to return true for empty list") } } + +func TestWrapAroundNavigation(t *testing.T) { + list := createTestList() + + // Start at first item (index 0) + _, idx := list.GetSelectedItem() + if idx != 0 { + t.Errorf("Expected to start at index 0, got %d", idx) + } + + // Press up arrow - should wrap to last item (index 2) + upKey := tea.KeyPressMsg{Code: tea.KeyUp} + updatedModel, _ := list.Update(upKey) + list = updatedModel.(*listComponent[testItem]) + _, idx = list.GetSelectedItem() + if idx != 2 { + t.Errorf("Expected to wrap to index 2 when pressing up from first item, got %d", idx) + } + + // Press down arrow - should wrap to first item (index 0) + downKey := tea.KeyPressMsg{Code: tea.KeyDown} + updatedModel, _ = list.Update(downKey) + list = updatedModel.(*listComponent[testItem]) + _, idx = list.GetSelectedItem() + if idx != 0 { + t.Errorf("Expected to wrap to index 0 when pressing down from last item, got %d", idx) + } + + // Navigate to middle and verify normal navigation still works + updatedModel, _ = list.Update(downKey) + list = updatedModel.(*listComponent[testItem]) + _, idx = list.GetSelectedItem() + if idx != 1 { + t.Errorf("Expected to move to index 1, got %d", idx) + } +} From 8780fa6ccfa37377f822591dbd2dacd15e627d21 Mon Sep 17 00:00:00 2001 From: spoons-and-mirrors <212802214+spoons-and-mirrors@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:51:35 +0200 Subject: [PATCH 069/178] Fix: Respect agent's preferred model at TUI startup (#1683) Co-authored-by: opencode --- packages/tui/internal/app/app.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/tui/internal/app/app.go b/packages/tui/internal/app/app.go index 48f4684fe..a07637794 100644 --- a/packages/tui/internal/app/app.go +++ b/packages/tui/internal/app/app.go @@ -417,7 +417,18 @@ func (a *App) InitializeProvider() tea.Cmd { } } - // Priority 3: Recent model usage (most recently used model) + // Priority 3: Current agent's preferred model + if selectedProvider == nil && a.Agent().Model.ModelID != "" { + if provider, model := findModelByProviderAndModelID(providers, a.Agent().Model.ProviderID, a.Agent().Model.ModelID); provider != nil && model != nil { + selectedProvider = provider + selectedModel = model + slog.Debug("Selected model from current agent", "provider", provider.ID, "model", model.ID, "agent", a.Agent().Name) + } else { + slog.Debug("Agent model not found", "provider", a.Agent().Model.ProviderID, "model", a.Agent().Model.ModelID, "agent", a.Agent().Name) + } + } + + // Priority 4: Recent model usage (most recently used model) if selectedProvider == nil && len(a.State.RecentlyUsedModels) > 0 { recentUsage := a.State.RecentlyUsedModels[0] // Most recent is first if provider, model := findModelByProviderAndModelID(providers, recentUsage.ProviderID, recentUsage.ModelID); provider != nil && @@ -436,7 +447,7 @@ func (a *App) InitializeProvider() tea.Cmd { } } - // Priority 4: State-based model (backwards compatibility) + // Priority 5: State-based model (backwards compatibility) if selectedProvider == nil && a.State.Provider != "" && a.State.Model != "" { if provider, model := findModelByProviderAndModelID(providers, a.State.Provider, a.State.Model); provider != nil && model != nil { @@ -448,7 +459,7 @@ func (a *App) InitializeProvider() tea.Cmd { } } - // Priority 5: Internal priority fallback (Anthropic preferred, then first available) + // Priority 6: Internal priority fallback (Anthropic preferred, then first available) if selectedProvider == nil { // Try Anthropic first as internal priority if provider := findProviderByID(providers, "anthropic"); provider != nil { From fa7cae59c090a2151bccdd51ec4dbf637b07c918 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 09:19:45 -0500 Subject: [PATCH 070/178] fix: re-render messages on session error --- packages/tui/internal/components/chat/messages.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go index ff279821f..630f715eb 100644 --- a/packages/tui/internal/components/chat/messages.go +++ b/packages/tui/internal/components/chat/messages.go @@ -187,6 +187,10 @@ func (m *messagesComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if msg.Properties.Info.SessionID == m.app.Session.ID { cmds = append(cmds, m.renderView()) } + case opencode.EventListResponseEventSessionError: + if msg.Properties.SessionID == m.app.Session.ID { + cmds = append(cmds, m.renderView()) + } case opencode.EventListResponseEventMessagePartUpdated: if msg.Properties.Part.SessionID == m.app.Session.ID { cmds = append(cmds, m.renderView()) From a760e8364ff70e0fdefcbd2ff17077a5b9b2a5ea Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 09:29:44 -0500 Subject: [PATCH 071/178] feat: placeholder on pending assistant message --- .../tui/internal/components/chat/messages.go | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go index 630f715eb..9d9c8011b 100644 --- a/packages/tui/internal/components/chat/messages.go +++ b/packages/tui/internal/components/chat/messages.go @@ -400,6 +400,7 @@ func (m *messagesComponent) renderView() tea.Cmd { revertedToolCount = 0 } hasTextPart := false + hasContent := false for partIndex, p := range message.Parts { switch part := p.(type) { case opencode.TextPart: @@ -491,6 +492,7 @@ func (m *messagesComponent) renderView() tea.Cmd { partCount++ lineCount += lipgloss.Height(content) + 1 blocks = append(blocks, content) + hasContent = true } case opencode.ToolPart: if reverted { @@ -552,6 +554,7 @@ func (m *messagesComponent) renderView() tea.Cmd { partCount++ lineCount += lipgloss.Height(content) + 1 blocks = append(blocks, content) + hasContent = true } case opencode.ReasoningPart: if reverted { @@ -582,8 +585,33 @@ func (m *messagesComponent) renderView() tea.Cmd { partCount++ lineCount += lipgloss.Height(content) + 1 blocks = append(blocks, content) + hasContent = true } } + + if !hasContent { + content = renderText( + m.app, + message.Info, + "Generating...", + casted.ModelID, + m.showToolDetails, + width, + "", + false, + []opencode.FilePart{}, + []opencode.AgentPart{}, + ) + content = lipgloss.PlaceHorizontal( + m.width, + lipgloss.Center, + content, + styles.WhitespaceStyle(t.Background()), + ) + partCount++ + lineCount += lipgloss.Height(content) + 1 + blocks = append(blocks, content) + } } error := "" From e8d144d2a21f884483eb47713dd52a83df7c859e Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 09:38:52 -0500 Subject: [PATCH 072/178] fix: reformat assistant message footer --- packages/tui/internal/components/chat/message.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index ee3535ad8..b5579883f 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -225,6 +225,9 @@ func renderText( bg = t.BackgroundPanel() } ts = time.UnixMilli(int64(casted.Time.Created)) + if casted.Time.Completed > 0 { + ts = time.UnixMilli(int64(casted.Time.Completed)) + } content = util.ToMarkdown(text, width, bg) if isThinking { content = styles.NewStyle().Background(bg).Foreground(t.TextMuted()).Render("Thinking") + "\n\n" + content @@ -333,7 +336,7 @@ func renderText( timestamp = timestamp[12:] } - // Check if this is an assistant message with mode (agent) information + // Check if this is an assistant message with agent information var modelAndAgentSuffix string if assistantMsg, ok := message.(opencode.AssistantMessage); ok && assistantMsg.Mode != "" { // Find the agent index by name to get the correct color @@ -349,17 +352,15 @@ func renderText( agentColor := util.GetAgentColor(agentIndex) // Style the agent name with the same color as status bar - agentName := strings.Title(assistantMsg.Mode) - styledAgentName := styles.NewStyle().Foreground(agentColor).Bold(true).Render(agentName) - modelAndAgentSuffix = fmt.Sprintf(" | %s | %s", assistantMsg.ModelID, styledAgentName) + agentName := cases.Title(language.Und).String(assistantMsg.Mode) + styledAgentName := styles.NewStyle().Foreground(agentColor).Render(agentName) + modelAndAgentSuffix = fmt.Sprintf("%s %s", styledAgentName, assistantMsg.ModelID) } var info string if modelAndAgentSuffix != "" { - // For assistant messages: "timestamp | modelID | agentName" - info = fmt.Sprintf("%s%s", timestamp, modelAndAgentSuffix) + info = fmt.Sprintf("%s (%s)", modelAndAgentSuffix, timestamp) } else { - // For user messages: "author (timestamp)" info = fmt.Sprintf("%s (%s)", author, timestamp) } info = styles.NewStyle().Foreground(t.TextMuted()).Render(info) From c51a34bf4b81407fb80091166b89dbe654fb7c57 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 10:53:09 -0400 Subject: [PATCH 073/178] make models key optional in config --- packages/opencode/src/config/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 62f425cb5..0eaae2b79 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -294,7 +294,7 @@ export namespace Config { .record( ModelsDev.Provider.partial() .extend({ - models: z.record(ModelsDev.Model.partial()), + models: z.record(ModelsDev.Model.partial()).optional(), options: z .object({ apiKey: z.string().optional(), From 279edb6f244b178cae76b8e79fe9b80b7555713a Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 10:54:51 -0400 Subject: [PATCH 074/178] fix azure gpt config --- packages/opencode/src/provider/transform.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 933df3dd4..a5a8c2f4c 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -82,8 +82,14 @@ export namespace ProviderTransform { return undefined } - export function options(_providerID: string, modelID: string) { + export function options(providerID: string, modelID: string): Record | undefined { if (modelID.includes("gpt-5")) { + if (providerID === "azure") { + return { + reasoning_effort: "minimal", + text_verbosity: "verbose", + } + } return { reasoningEffort: "minimal", textVerbosity: "low", From eb15b2ba752dc67d90f0697423f5a9ef8af621ff Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 11 Aug 2025 15:15:24 +0000 Subject: [PATCH 075/178] release: v0.4.17 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/sdk/js/src/gen/types.gen.ts | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 266f6c922..231d1ab78 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.16", + "version": "0.4.17", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index e6c5083c7..3c1cf63b8 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.16", + "version": "0.4.17", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index f8aaae017..95f5c5fb8 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.16", + "version": "0.4.17", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index edd93e55e..e692b5b64 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.16", + "version": "0.4.17", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 13b6721e5..ffa0adff5 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.16", + "version": "0.4.17", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index ca960ccf6..f6bb4e20e 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.16", + "version": "0.4.17", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index c2ed5d629..6064f13bc 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.16", + "version": "0.4.17", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 5eb197954..e6d085d83 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -627,7 +627,7 @@ export type Config = { env?: Array id?: string npm?: string - models: { + models?: { [key: string]: { id?: string name?: string diff --git a/packages/web/package.json b/packages/web/package.json index b39e8a8a9..cc49971fa 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.16", + "version": "0.4.17", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index b891158f8..74efa7f26 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.16", + "version": "0.4.17", "publisher": "sst-dev", "repository": { "type": "git", From c78cb57c41c5ba29ba7097528bc5441167f874a5 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 10:50:00 -0500 Subject: [PATCH 076/178] fix: assistant message footer styles --- .../tui/internal/components/chat/message.go | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index b5579883f..9a85e89e1 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -335,6 +335,10 @@ func renderText( if time.Now().Format("02 Jan 2006") == timestamp[:11] { timestamp = timestamp[12:] } + timestamp = styles.NewStyle(). + Background(backgroundColor). + Foreground(t.TextMuted()). + Render(" (" + timestamp + ")") // Check if this is an assistant message with agent information var modelAndAgentSuffix string @@ -353,17 +357,23 @@ func renderText( // Style the agent name with the same color as status bar agentName := cases.Title(language.Und).String(assistantMsg.Mode) - styledAgentName := styles.NewStyle().Foreground(agentColor).Render(agentName) - modelAndAgentSuffix = fmt.Sprintf("%s %s", styledAgentName, assistantMsg.ModelID) + styledAgentName := styles.NewStyle(). + Background(backgroundColor). + Foreground(agentColor). + Render(agentName + " ") + styledModelID := styles.NewStyle(). + Background(backgroundColor). + Foreground(t.TextMuted()). + Render(assistantMsg.ModelID) + modelAndAgentSuffix = styledAgentName + styledModelID } var info string if modelAndAgentSuffix != "" { - info = fmt.Sprintf("%s (%s)", modelAndAgentSuffix, timestamp) + info = modelAndAgentSuffix + timestamp } else { - info = fmt.Sprintf("%s (%s)", author, timestamp) + info = author + timestamp } - info = styles.NewStyle().Foreground(t.TextMuted()).Render(info) if !showToolDetails && toolCalls != nil && len(toolCalls) > 0 { content = content + "\n\n" for _, toolCall := range toolCalls { @@ -378,10 +388,11 @@ func renderText( } } - sections := []string{content, info} + sections := []string{content} if extra != "" { - sections = append(sections, "\n"+extra) + sections = append(sections, "\n"+extra+"\n") } + sections = append(sections, info) content = strings.Join(sections, "\n") switch message.(type) { From 9c8e56fc96df1f680671f8a16a202fcd9e9c4b1e Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 10:52:49 -0500 Subject: [PATCH 077/178] fix: assistant message footer styles --- packages/tui/internal/components/chat/message.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index 9a85e89e1..12156ea91 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -220,17 +220,17 @@ func renderText( var content string switch casted := message.(type) { case opencode.AssistantMessage: - bg := t.Background() + backgroundColor = t.Background() if isThinking { - bg = t.BackgroundPanel() + backgroundColor = t.BackgroundPanel() } ts = time.UnixMilli(int64(casted.Time.Created)) if casted.Time.Completed > 0 { ts = time.UnixMilli(int64(casted.Time.Completed)) } - content = util.ToMarkdown(text, width, bg) + content = util.ToMarkdown(text, width, backgroundColor) if isThinking { - content = styles.NewStyle().Background(bg).Foreground(t.TextMuted()).Render("Thinking") + "\n\n" + content + content = styles.NewStyle().Background(backgroundColor).Foreground(t.TextMuted()).Render("Thinking") + "\n\n" + content } case opencode.UserMessage: ts = time.UnixMilli(int64(casted.Time.Created)) From a4113acd157cf4192aac4161b76c2aa1d55aefc6 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 10:57:18 -0500 Subject: [PATCH 078/178] fix: assistant message footer styles --- packages/tui/internal/components/chat/message.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index 12156ea91..c7f8b0ee2 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -375,7 +375,7 @@ func renderText( info = author + timestamp } if !showToolDetails && toolCalls != nil && len(toolCalls) > 0 { - content = content + "\n\n" + content = content + "\n" for _, toolCall := range toolCalls { title := renderToolTitle(toolCall, width-2) style := styles.NewStyle() @@ -383,16 +383,16 @@ func renderText( style = style.Foreground(t.Error()) } title = style.Render(title) - title = "∟ " + title + "\n" + title = "\n∟ " + title content = content + title } } sections := []string{content} if extra != "" { - sections = append(sections, "\n"+extra+"\n") + sections = append(sections, "\n"+extra) } - sections = append(sections, info) + sections = append(sections, "\n"+info) content = strings.Join(sections, "\n") switch message.(type) { From e9de7f95a7f09975f65241552ee9a5a3cade4089 Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 11 Aug 2025 16:04:07 +0000 Subject: [PATCH 079/178] release: v0.4.18 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 231d1ab78..886c583b4 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.17", + "version": "0.4.18", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 3c1cf63b8..9bd9e9e62 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.17", + "version": "0.4.18", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 95f5c5fb8..f0630a22e 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.17", + "version": "0.4.18", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index e692b5b64..65f322ef1 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.17", + "version": "0.4.18", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index ffa0adff5..9fd5bfd97 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.17", + "version": "0.4.18", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index f6bb4e20e..032ccc69c 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.17", + "version": "0.4.18", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 6064f13bc..2a3b0ee62 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.17", + "version": "0.4.18", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index cc49971fa..52a4468de 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.17", + "version": "0.4.18", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 74efa7f26..18ca80411 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.17", + "version": "0.4.18", "publisher": "sst-dev", "repository": { "type": "git", From 10f3983f0bf490cbe1c5db623706bcb5fc0bee3f Mon Sep 17 00:00:00 2001 From: Jay V Date: Mon, 11 Aug 2025 12:41:13 -0400 Subject: [PATCH 080/178] docs: edits --- packages/web/src/content/docs/docs/agents.mdx | 20 ++++--------------- packages/web/src/content/docs/docs/config.mdx | 19 ------------------ 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/packages/web/src/content/docs/docs/agents.mdx b/packages/web/src/content/docs/docs/agents.mdx index 52bb8ed14..80e1867d7 100644 --- a/packages/web/src/content/docs/docs/agents.mdx +++ b/packages/web/src/content/docs/docs/agents.mdx @@ -1,6 +1,6 @@ --- title: Agents -description: Configure and use specialized agents in opencode. +description: Configure and use specialized agents. --- Agents are specialized AI assistants that can be configured for specific tasks and workflows. They allow you to create focused tools with custom prompts, models, and tool access. @@ -360,25 +360,13 @@ The `mode` option can be set to `primary`, `subagent`, or `all`. If no `mode` is --- -### Additional options +### Additional -Any other options you specify in your agent configuration will be passed through directly to the provider as model options. This allows you to use provider-specific features and parameters. - -```json title="opencode.json" -{ - "agent": { - "reasoning": { - "model": "openai/gpt-5-turbo", - "reasoningEffort": "high", - "textVerbosity": "medium" - } - } -} -``` +Any other options you specify in your agent configuration will be **passed through directly** to the provider as model options. This allows you to use provider-specific features and parameters. For example, with OpenAI's reasoning models, you can control the reasoning effort: -```json title="opencode.json" +```json title="opencode.json" {6,7} { "agent": { "deep-thinker": { diff --git a/packages/web/src/content/docs/docs/config.mdx b/packages/web/src/content/docs/docs/config.mdx index 2119be5fa..61a89cb1f 100644 --- a/packages/web/src/content/docs/docs/config.mdx +++ b/packages/web/src/content/docs/docs/config.mdx @@ -71,25 +71,6 @@ Your editor should be able to validate and autocomplete based on the schema. --- -### Modes - -opencode comes with two built-in modes: _build_, the default with all tools enabled. And _plan_, restricted mode with file modification tools disabled. You can override these built-in modes or define your own custom modes with the `mode` option. - -```json title="opencode.json" -{ - "$schema": "https://opencode.ai/config.json", - "mode": { - "build": {}, - "plan": {}, - "my-custom-mode": {} - } -} -``` - -[Learn more here](/docs/modes). - ---- - ### Models You can configure the providers and models you want to use in your opencode config through the `provider`, `model` and `small_model` options. From 42329a038a2f9b6721ff9fea4299c17f7ec4c54e Mon Sep 17 00:00:00 2001 From: Jay V Date: Mon, 11 Aug 2025 12:51:19 -0400 Subject: [PATCH 081/178] docs: share page fix --- packages/web/src/components/Share.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index 47632492d..3848924df 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -64,9 +64,9 @@ export default function Share(props: { }>({ info: props.info, messages: mapValues(props.messages, (x: any) => ("metadata" in x ? fromV1(x) : x)) }) const messages = createMemo(() => Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id))) const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>(["disconnected", "Disconnected"]) - createEffect(() => { - console.log(unwrap(store)) - }) + // createEffect(() => { + // console.log(unwrap(store)) + // }) onMount(() => { const apiUrl = props.api From 3c71fda6481bcadcf62278478f0678002ff9029a Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 11:09:28 -0500 Subject: [PATCH 082/178] fix: don't display placeholder on error --- .../tui/internal/components/chat/messages.go | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go index 9d9c8011b..84c3b9830 100644 --- a/packages/tui/internal/components/chat/messages.go +++ b/packages/tui/internal/components/chat/messages.go @@ -289,6 +289,7 @@ func (m *messagesComponent) renderView() tea.Cmd { for _, message := range m.app.Messages { var content string var cached bool + error := "" switch casted := message.Info.(type) { case opencode.UserMessage: @@ -589,7 +590,19 @@ func (m *messagesComponent) renderView() tea.Cmd { } } - if !hasContent { + switch err := casted.Error.AsUnion().(type) { + case nil: + case opencode.AssistantMessageErrorMessageOutputLengthError: + error = "Message output length exceeded" + case opencode.ProviderAuthError: + error = err.Data.Message + case opencode.MessageAbortedError: + error = "Request was aborted" + case opencode.UnknownError: + error = err.Data.Message + } + + if !hasContent && error == "" && !reverted { content = renderText( m.app, message.Info, @@ -614,21 +627,6 @@ func (m *messagesComponent) renderView() tea.Cmd { } } - error := "" - if assistant, ok := message.Info.(opencode.AssistantMessage); ok { - switch err := assistant.Error.AsUnion().(type) { - case nil: - case opencode.AssistantMessageErrorMessageOutputLengthError: - error = "Message output length exceeded" - case opencode.ProviderAuthError: - error = err.Data.Message - case opencode.MessageAbortedError: - error = "Request was aborted" - case opencode.UnknownError: - error = err.Data.Message - } - } - if error != "" && !reverted { error = styles.NewStyle().Width(width - 6).Render(error) error = renderContentBlock( From 5e777fd2a2d821003bfc7aa39bf5c5cd158b6c5b Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 11:58:40 -0500 Subject: [PATCH 083/178] feat: toggle tool details visible --- packages/opencode/src/config/config.ts | 1 + packages/opencode/src/session/index.ts | 4 +- packages/tui/internal/app/state.go | 2 + packages/tui/internal/commands/command.go | 7 ++ .../tui/internal/components/chat/messages.go | 114 +++++++++++------- packages/tui/internal/tui/tui.go | 7 ++ .../web/src/components/share/part.module.css | 3 +- packages/web/src/components/share/part.tsx | 21 ++-- 8 files changed, 102 insertions(+), 57 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 0eaae2b79..40ec22b57 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -202,6 +202,7 @@ export namespace Config { session_interrupt: z.string().optional().default("esc").describe("Interrupt current session"), session_compact: z.string().optional().default("c").describe("Compact the session"), tool_details: z.string().optional().default("d").describe("Toggle tool details"), + thinking_blocks: z.string().optional().default("b").describe("Toggle thinking blocks"), model_list: z.string().optional().default("m").describe("List available models"), theme_list: z.string().optional().default("t").describe("List available themes"), file_list: z.string().optional().default("f").describe("List files"), diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 5565f3e0a..5f7bf000d 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -1007,7 +1007,7 @@ export namespace Session { async process(stream: StreamTextResult, never>) { try { let currentText: MessageV2.TextPart | undefined - // let reasoningMap: Record = {} + let reasoningMap: Record = {} for await (const value of stream.fullStream) { log.info("part", { @@ -1017,7 +1017,6 @@ export namespace Session { case "start": break - /* case "reasoning-start": if (value.id in reasoningMap) { continue @@ -1055,7 +1054,6 @@ export namespace Session { delete reasoningMap[value.id] } break - */ case "tool-input-start": const part = await updatePart({ diff --git a/packages/tui/internal/app/state.go b/packages/tui/internal/app/state.go index 478022351..283cbd15e 100644 --- a/packages/tui/internal/app/state.go +++ b/packages/tui/internal/app/state.go @@ -32,6 +32,8 @@ type State struct { MessagesRight bool `toml:"messages_right"` SplitDiff bool `toml:"split_diff"` MessageHistory []Prompt `toml:"message_history"` + ShowToolDetails *bool `toml:"show_tool_details"` + ShowThinkingBlocks *bool `toml:"show_thinking_blocks"` } func NewState() *State { diff --git a/packages/tui/internal/commands/command.go b/packages/tui/internal/commands/command.go index 516caab79..fff547543 100644 --- a/packages/tui/internal/commands/command.go +++ b/packages/tui/internal/commands/command.go @@ -119,6 +119,7 @@ const ( SessionCompactCommand CommandName = "session_compact" SessionExportCommand CommandName = "session_export" ToolDetailsCommand CommandName = "tool_details" + ThinkingBlocksCommand CommandName = "thinking_blocks" ModelListCommand CommandName = "model_list" AgentListCommand CommandName = "agent_list" ModelCycleRecentCommand CommandName = "model_cycle_recent" @@ -245,6 +246,12 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry { Keybindings: parseBindings("d"), Trigger: []string{"details"}, }, + { + Name: ThinkingBlocksCommand, + Description: "toggle thinking blocks", + Keybindings: parseBindings("b"), + Trigger: []string{"thinking"}, + }, { Name: ModelListCommand, Description: "list models", diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go index 84c3b9830..bbfd557a6 100644 --- a/packages/tui/internal/components/chat/messages.go +++ b/packages/tui/internal/components/chat/messages.go @@ -33,6 +33,7 @@ type MessagesComponent interface { HalfPageUp() (tea.Model, tea.Cmd) HalfPageDown() (tea.Model, tea.Cmd) ToolDetailsVisible() bool + ThinkingBlocksVisible() bool GotoTop() (tea.Model, tea.Cmd) GotoBottom() (tea.Model, tea.Cmd) CopyLastMessage() (tea.Model, tea.Cmd) @@ -41,20 +42,21 @@ type MessagesComponent interface { } type messagesComponent struct { - width, height int - app *app.App - header string - viewport viewport.Model - clipboard []string - cache *PartCache - loading bool - showToolDetails bool - rendering bool - dirty bool - tail bool - partCount int - lineCount int - selection *selection + width, height int + app *app.App + header string + viewport viewport.Model + clipboard []string + cache *PartCache + loading bool + showToolDetails bool + showThinkingBlocks bool + rendering bool + dirty bool + tail bool + partCount int + lineCount int + selection *selection } type selection struct { @@ -94,6 +96,7 @@ func (s selection) coords(offset int) *selection { } type ToggleToolDetailsMsg struct{} +type ToggleThinkingBlocksMsg struct{} func (m *messagesComponent) Init() tea.Cmd { return tea.Batch(m.viewport.Init()) @@ -160,7 +163,12 @@ func (m *messagesComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, m.renderView() case ToggleToolDetailsMsg: m.showToolDetails = !m.showToolDetails - return m, m.renderView() + m.app.State.ShowToolDetails = &m.showToolDetails + return m, tea.Batch(m.renderView(), m.app.SaveState()) + case ToggleThinkingBlocksMsg: + m.showThinkingBlocks = !m.showThinkingBlocks + m.app.State.ShowThinkingBlocks = &m.showThinkingBlocks + return m, tea.Batch(m.renderView(), m.app.SaveState()) case app.SessionLoadedMsg, app.SessionClearedMsg: m.cache.Clear() m.tail = true @@ -561,32 +569,34 @@ func (m *messagesComponent) renderView() tea.Cmd { if reverted { continue } - text := "..." + if !m.showThinkingBlocks { + continue + } if part.Text != "" { - text = part.Text + text := part.Text + content = renderText( + m.app, + message.Info, + text, + casted.ModelID, + m.showToolDetails, + width, + "", + true, + []opencode.FilePart{}, + []opencode.AgentPart{}, + ) + content = lipgloss.PlaceHorizontal( + m.width, + lipgloss.Center, + content, + styles.WhitespaceStyle(t.Background()), + ) + partCount++ + lineCount += lipgloss.Height(content) + 1 + blocks = append(blocks, content) + hasContent = true } - content = renderText( - m.app, - message.Info, - text, - casted.ModelID, - m.showToolDetails, - width, - "", - true, - []opencode.FilePart{}, - []opencode.AgentPart{}, - ) - content = lipgloss.PlaceHorizontal( - m.width, - lipgloss.Center, - content, - styles.WhitespaceStyle(t.Background()), - ) - partCount++ - lineCount += lipgloss.Height(content) + 1 - blocks = append(blocks, content) - hasContent = true } } @@ -1006,6 +1016,10 @@ func (m *messagesComponent) ToolDetailsVisible() bool { return m.showToolDetails } +func (m *messagesComponent) ThinkingBlocksVisible() bool { + return m.showThinkingBlocks +} + func (m *messagesComponent) GotoTop() (tea.Model, tea.Cmd) { m.viewport.GotoTop() return m, nil @@ -1202,11 +1216,23 @@ func NewMessagesComponent(app *app.App) MessagesComponent { vp.MouseWheelDelta = 4 } + // Default to showing tool details, hidden thinking blocks + showToolDetails := true + if app.State.ShowToolDetails != nil { + showToolDetails = *app.State.ShowToolDetails + } + + showThinkingBlocks := false + if app.State.ShowThinkingBlocks != nil { + showThinkingBlocks = *app.State.ShowThinkingBlocks + } + return &messagesComponent{ - app: app, - viewport: vp, - showToolDetails: true, - cache: NewPartCache(), - tail: true, + app: app, + viewport: vp, + showToolDetails: showToolDetails, + showThinkingBlocks: showThinkingBlocks, + cache: NewPartCache(), + tail: true, } } diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 499d67c65..1899df351 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -1142,6 +1142,13 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) { } cmds = append(cmds, util.CmdHandler(chat.ToggleToolDetailsMsg{})) cmds = append(cmds, toast.NewInfoToast(message)) + case commands.ThinkingBlocksCommand: + message := "Thinking blocks are now visible" + if a.messages.ThinkingBlocksVisible() { + message = "Thinking blocks are now hidden" + } + cmds = append(cmds, util.CmdHandler(chat.ToggleThinkingBlocksMsg{})) + cmds = append(cmds, toast.NewInfoToast(message)) case commands.ModelListCommand: modelDialog := dialog.NewModelDialog(a.app) a.modal = modelDialog diff --git a/packages/web/src/components/share/part.module.css b/packages/web/src/components/share/part.module.css index 3dd321425..15a43da36 100644 --- a/packages/web/src/components/share/part.module.css +++ b/packages/web/src/components/share/part.module.css @@ -136,13 +136,14 @@ flex-grow: 1; max-width: var(--md-tool-width); - & > [data-component="assistant-reasoning-markdown"] { + [data-component="assistant-reasoning-markdown"] { align-self: flex-start; font-size: 0.875rem; border: 1px solid var(--sl-color-blue-high); padding: 0.5rem calc(0.5rem + 3px); border-radius: 0.25rem; position: relative; + margin-top: 0.5rem; [data-component="copy-button"] { top: 0.5rem; diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index 5cfd3a045..597b67d67 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -152,18 +152,23 @@ export function Part(props: PartProps) { )} {` | ${props.message.modelID}`} {props.message.mode && ( - - {` | ${props.message.mode}`} - + {` | ${props.message.mode}`} )} )}
)} {props.message.role === "assistant" && props.part.type === "reasoning" && ( -
-
- +
+
+ Thinking +
+
+ +
+ +
+
)} @@ -182,9 +187,7 @@ export function Part(props: PartProps) { )} {` | ${props.message.modelID}`} {props.message.mode && ( - - {` | ${props.message.mode}`} - + {` | ${props.message.mode}`} )}
From 038689847698725a746fc8c88b4dc655f0a9d6a6 Mon Sep 17 00:00:00 2001 From: Jay V Date: Mon, 11 Aug 2025 13:21:59 -0400 Subject: [PATCH 084/178] docs: comment out thinking blocks for share page --- packages/web/src/components/icons/custom.tsx | 4 +- .../web/src/components/share/part.module.css | 4 +- packages/web/src/components/share/part.tsx | 78 ++++++++++--------- 3 files changed, 47 insertions(+), 39 deletions(-) diff --git a/packages/web/src/components/icons/custom.tsx b/packages/web/src/components/icons/custom.tsx index 8023032e5..ffa4fea82 100644 --- a/packages/web/src/components/icons/custom.tsx +++ b/packages/web/src/components/icons/custom.tsx @@ -75,7 +75,7 @@ export function IconRobot(props: JSX.SvgSVGAttributes) { } // https://icones.js.org/collection/ri?s=brain&icon=ri:brain-2-line -export function IconBrain(props: JSX.SvgSVGAttributes) { +/* export function IconBrain(props: JSX.SvgSVGAttributes) { return ( ) { /> ) -} +} */ diff --git a/packages/web/src/components/share/part.module.css b/packages/web/src/components/share/part.module.css index 15a43da36..5634f6102 100644 --- a/packages/web/src/components/share/part.module.css +++ b/packages/web/src/components/share/part.module.css @@ -128,7 +128,7 @@ max-width: var(--md-tool-width); } - [data-component="assistant-reasoning"] { + /* [data-component="assistant-reasoning"] { min-width: 0; display: flex; flex-direction: column; @@ -150,7 +150,7 @@ right: calc(0.5rem - 1px); } } - } + } */ [data-component="assistant-text"] { min-width: 0; diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index 597b67d67..2b87c6bcd 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -19,7 +19,7 @@ import { IconMagnifyingGlass, IconDocumentMagnifyingGlass, } from "../icons" -import { IconMeta, IconRobot, IconOpenAI, IconGemini, IconAnthropic, IconBrain } from "../icons/custom" +import { IconMeta, IconRobot, IconOpenAI, IconGemini, IconAnthropic /*, IconBrain */ } from "../icons/custom" import { ContentCode } from "./content-code" import { ContentDiff } from "./content-diff" import { ContentText } from "./content-text" @@ -83,9 +83,9 @@ export function Part(props: PartProps) { > {(model) => } - + {/* - + */} @@ -158,7 +158,7 @@ export function Part(props: PartProps) { )} )} - {props.message.role === "assistant" && props.part.type === "reasoning" && ( + {/* {props.message.role === "assistant" && props.part.type === "reasoning" && (
Thinking @@ -169,36 +169,43 @@ export function Part(props: PartProps) {
+
+ + )} */} + { + props.message.role === "user" && props.part.type === "file" && ( +
+
Attachment
+
{props.part.filename}
- - )} - {props.message.role === "user" && props.part.type === "file" && ( -
-
Attachment
-
{props.part.filename}
-
- )} - {props.part.type === "step-start" && props.message.role === "assistant" && ( -
-
{props.message.providerID}
-
- {DateTime.fromMillis(props.message.time.completed || props.message.time.created).toLocaleString( - DateTime.DATETIME_MED, - )} - {` | ${props.message.modelID}`} - {props.message.mode && ( - {` | ${props.message.mode}`} - )} + ) + } + { + props.part.type === "step-start" && props.message.role === "assistant" && ( +
+
{props.message.providerID}
+
+ {DateTime.fromMillis(props.message.time.completed || props.message.time.created).toLocaleString( + DateTime.DATETIME_MED, + )} + {` | ${props.message.modelID}`} + {props.message.mode && ( + {` | ${props.message.mode}`} + )} +
-
- )} - {props.part.type === "tool" && props.part.state.status === "error" && ( -
- {formatErrorString(props.part.state.error)} - -
- )} - {props.part.type === "tool" && + ) + } + { + props.part.type === "tool" && props.part.state.status === "error" && ( +
+ {formatErrorString(props.part.state.error)} + +
+ ) + } + { + props.part.type === "tool" && props.part.state.status === "completed" && props.message.role === "assistant" && ( <> @@ -300,9 +307,10 @@ export function Part(props: PartProps) { .toMillis()} /> - )} -
- + ) + } + + ) } From e83e8001da673fa1b2c4a0599f18a7b851b0eaff Mon Sep 17 00:00:00 2001 From: Jay V Date: Mon, 11 Aug 2025 13:38:03 -0400 Subject: [PATCH 085/178] docs: test share --- packages/web/src/pages/s/[id].astro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/pages/s/[id].astro b/packages/web/src/pages/s/[id].astro index 430d50cfa..ff9d15e66 100644 --- a/packages/web/src/pages/s/[id].astro +++ b/packages/web/src/pages/s/[id].astro @@ -1,6 +1,6 @@ --- import { Base64 } from "js-base64"; -import config from "virtual:starlight/user-config"; +// import config from "virtual:starlight/user-config"; import config from '../../../config.mjs' import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; From a6a633d5c16ab002c5d7f0fd7df63e3fb5338752 Mon Sep 17 00:00:00 2001 From: Jay V Date: Mon, 11 Aug 2025 13:45:55 -0400 Subject: [PATCH 086/178] docs: test share --- packages/web/src/components/Share.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index 3848924df..bf5d38abe 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -1,6 +1,6 @@ -import { For, Show, onMount, Suspense, onCleanup, createMemo, createSignal, SuspenseList, createEffect } from "solid-js" +import { For, Show, onMount, Suspense, onCleanup, createMemo, createSignal, SuspenseList } from "solid-js" import { DateTime } from "luxon" -import { createStore, reconcile, unwrap } from "solid-js/store" +import { createStore, reconcile } from "solid-js/store" import { mapValues } from "remeda" import { IconArrowDown } from "./icons" import { IconOpencode } from "./icons/custom" @@ -62,7 +62,9 @@ export default function Share(props: { info?: Session.Info messages: Record }>({ info: props.info, messages: mapValues(props.messages, (x: any) => ("metadata" in x ? fromV1(x) : x)) }) - const messages = createMemo(() => Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id))) + const messages = createMemo(() => + Object.values(store.messages).toSorted((a, b) => (a.id || "").localeCompare(b.id || "")), + ) const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>(["disconnected", "Disconnected"]) // createEffect(() => { // console.log(unwrap(store)) @@ -508,6 +510,7 @@ export function fromV1(v1: Message.Info): MessageWithParts { }, modelID: v1.metadata.assistant!.modelID, providerID: v1.metadata.assistant!.providerID, + mode: "build", system: v1.metadata.assistant!.system, error: v1.metadata.error, parts: v1.parts.flatMap((part, index): MessageV2.Part[] => { From b5d45fa9f5d292d0c72b4546bb7c982778981b51 Mon Sep 17 00:00:00 2001 From: Jay V Date: Mon, 11 Aug 2025 13:57:41 -0400 Subject: [PATCH 087/178] docs: share page --- packages/web/src/components/Share.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index bf5d38abe..0f60c8f3a 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -1,6 +1,6 @@ -import { For, Show, onMount, Suspense, onCleanup, createMemo, createSignal, SuspenseList } from "solid-js" +import { For, Show, onMount, Suspense, onCleanup, createMemo, createSignal, SuspenseList, createEffect } from "solid-js" import { DateTime } from "luxon" -import { createStore, reconcile } from "solid-js/store" +import { createStore, reconcile, unwrap } from "solid-js/store" import { mapValues } from "remeda" import { IconArrowDown } from "./icons" import { IconOpencode } from "./icons/custom" @@ -62,9 +62,7 @@ export default function Share(props: { info?: Session.Info messages: Record }>({ info: props.info, messages: mapValues(props.messages, (x: any) => ("metadata" in x ? fromV1(x) : x)) }) - const messages = createMemo(() => - Object.values(store.messages).toSorted((a, b) => (a.id || "").localeCompare(b.id || "")), - ) + const messages = createMemo(() => Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id))) const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>(["disconnected", "Disconnected"]) // createEffect(() => { // console.log(unwrap(store)) From 09bf0b86d8fc56cf1254624c62921a6a7d122582 Mon Sep 17 00:00:00 2001 From: Jay V Date: Mon, 11 Aug 2025 14:01:20 -0400 Subject: [PATCH 088/178] docs: share page --- packages/web/src/components/Share.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index 0f60c8f3a..ad1172731 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -356,8 +356,7 @@ export default function Share(props: { {(part, partIndex) => { const last = createMemo( - () => - data().messages.length === msgIndex() + 1 && filteredParts().length === partIndex() + 1, + () => messages().length === msgIndex() + 1 && filteredParts().length === partIndex() + 1, ) onMount(() => { From befb7509defeadae8f1950364b3399fbd80622d8 Mon Sep 17 00:00:00 2001 From: Jay V Date: Mon, 11 Aug 2025 14:19:22 -0400 Subject: [PATCH 089/178] docs: share --- packages/web/src/components/Share.tsx | 3 ++- packages/web/src/components/share/part.tsx | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index ad1172731..0f60c8f3a 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -356,7 +356,8 @@ export default function Share(props: { {(part, partIndex) => { const last = createMemo( - () => messages().length === msgIndex() + 1 && filteredParts().length === partIndex() + 1, + () => + data().messages.length === msgIndex() + 1 && filteredParts().length === partIndex() + 1, ) onMount(() => { diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index 2b87c6bcd..7c7220c37 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -147,13 +147,16 @@ export function Part(props: PartProps) { DateTime.DATETIME_FULL_WITH_SECONDS, )} > - {DateTime.fromMillis(props.message.time.completed || props.message.time.created).toLocaleString( + {DateTime.fromMillis(props.message.time.completed).toLocaleString(DateTime.DATETIME_MED)} + {/* + DateTime.fromMillis(props.message.time.completed || props.message.time.created).toLocaleString( DateTime.DATETIME_MED, )} {` | ${props.message.modelID}`} {props.message.mode && ( {` | ${props.message.mode}`} - )} + ) + */} )} @@ -184,6 +187,8 @@ export function Part(props: PartProps) { props.part.type === "step-start" && props.message.role === "assistant" && (
{props.message.providerID}
+
{props.message.modelID}
+ { /*
{DateTime.fromMillis(props.message.time.completed || props.message.time.created).toLocaleString( DateTime.DATETIME_MED, @@ -193,6 +198,7 @@ export function Part(props: PartProps) { {` | ${props.message.mode}`} )}
+ */ }
) } From 6eaa231587586fa7e3a1833405c5ca8c14f45708 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 14:28:38 -0400 Subject: [PATCH 090/178] Update GPT-5 system prompt to use copilot-specific prompt instead of codex prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [opencode](https://opencode.ai) Co-Authored-By: opencode --- .../src/session/prompt/copilot-gpt-5.txt | 126 ++++++++++++++++++ packages/opencode/src/session/system.ts | 4 +- script/publish.ts | 1 - 3 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 packages/opencode/src/session/prompt/copilot-gpt-5.txt diff --git a/packages/opencode/src/session/prompt/copilot-gpt-5.txt b/packages/opencode/src/session/prompt/copilot-gpt-5.txt new file mode 100644 index 000000000..cbcd52efb --- /dev/null +++ b/packages/opencode/src/session/prompt/copilot-gpt-5.txt @@ -0,0 +1,126 @@ +You are an expert AI programming assistant +When asked for your name, you must respond with "opencode". +Follow the user's requirements carefully & to the letter. +Keep your answers short and impersonal. + +You are a highly sophisticated automated coding agent with expert-level knowledge across many different programming languages and frameworks. +The user will ask a question, or ask you to perform a task, and it may require lots of research to answer correctly. There is a selection of tools that let you perform actions or retrieve helpful context to answer the user's question. +You are an agent—keep going until the user's query is completely resolved before ending your turn. ONLY stop if solved or genuinely blocked. +Take action when possible; the user expects you to do useful work without unnecessary questions. +After any parallel, read-only context gathering, give a concise progress update and what's next. +Avoid repetition across turns: don't restate unchanged plans or sections (like the todo list) verbatim; provide delta updates or only the parts that changed. +Tool batches: You MUST preface each batch with a one-sentence why/what/outcome preamble. +Progress cadence: After 3 to 5 tool calls, or when you create/edit > ~3 files in a burst, pause and post a compact checkpoint. +Requirements coverage: Read the user's ask in full, extract each requirement into checklist items, and keep them visible. Do not omit a requirement. If something cannot be done with available tools, note why briefly and propose a viable alternative. +Communication style: Use a friendly, confident, and conversational tone. Prefer short sentences, contractions, and concrete language. Keep it skimmable and encouraging, not formal or robotic. A tiny touch of personality is okay; avoid overusing exclamations or emoji. Avoid empty filler like "Sounds good!", "Great!", "Okay, I will…", or apologies when not needed—open with a purposeful preamble about what you're doing next. +You will be given some context and attachments along with the user prompt. You can use them if they are relevant to the task, and ignore them if not. +If you can infer the project type (languages, frameworks, and libraries) from the user's query or the context that you have, make sure to keep them in mind when making changes. +If you aren't sure which tool is relevant, you can call multiple tools. You can call tools repeatedly to take actions or gather as much context as needed until you have completed the task fully. Don't give up unless you are sure the request cannot be fulfilled with the tools you have. It's YOUR RESPONSIBILITY to make sure that you have done all you can to collect necessary context. +Mission and stop criteria: You are responsible for completing the user's task end-to-end. Continue working until the goal is satisfied or you are truly blocked by missing information. Do not defer actions back to the user if you can execute them yourself with available tools. Only ask a clarifying question when essential to proceed. +Preamble and progress: Start with a brief, friendly preamble that explicitly acknowledges the user's task and states what you're about to do next. Make it engaging and tailored to the repo/task; keep it to a single sentence. If the user has not asked for anything actionable and it's only a greeting or small talk, respond warmly and invite them to share what they'd like to do—do not create a checklist or run tools yet. Use the preamble only once per task; if the previous assistant message already included a preamble for this task, skip it this turn. Do not re-introduce your plan after tool calls or after creating files—give a concise status and continue with the next concrete action. For multi-step tasks, keep a lightweight checklist and weave progress updates into your narration. Batch independent, read-only operations together; after a batch, share a concise progress note and what's next. If you say you will do something, execute it in the same turn using tools. + +Always read the user's request in full before acting. Extract the explicit requirements and any reasonable implicit requirements. +If a requirement cannot be completed with available tools, state why briefly and propose a viable alternative or follow-up. + + +When reading files, prefer reading large meaningful chunks rather than consecutive small sections to minimize tool calls and gain better context. +Don't make assumptions about the situation- gather context first, then perform the task or answer the question. +Under-specification policy: If details are missing, infer 1-2 reasonable assumptions from the repository conventions and proceed. Note assumptions briefly and continue; ask only when truly blocked. +Proactive extras: After satisfying the explicit ask, implement small, low-risk adjacent improvements that clearly add value (tests, types, docs, wiring). If a follow-up is larger or risky, list it as next steps. +Anti-laziness: Avoid generic restatements and high-level advice. Prefer concrete edits, running tools, and verifying outcomes over suggesting what the user should do. + +Think like a software engineer—when relevant, prefer to: +- Outline a tiny “contract” in 2-4 bullets (inputs/outputs, data shapes, error modes, success criteria). +- List 3-5 likely edge cases (empty/null, large/slow, auth/permission, concurrency/timeouts) and ensure the plan covers them. +- Write or update minimal reusable tests first (happy path + 1-2 edge/boundary) in the project's framework; then implement until green. + + + +Before wrapping up, prefer a quick “quality gates” triage: Build, Lint/Typecheck, Unit tests, and a small smoke test. Ensure there are no syntax/type errors across the project; fix them or clearly call out any intentionally deferred ones. Report deltas only (PASS/FAIL). Include a brief “requirements coverage” line mapping each requirement to its status (Done/Deferred + reason). + + + +Choose response mode based on task complexity. Prefer a lightweight answer when it's a greeting, small talk, or a trivial/direct Q&A that doesn't require tools or edits: keep it short, skip todo lists and progress checkpoints, and avoid tool calls unless necessary. Use the full engineering workflow (checklist, phases, checkpoints) when the task is multi-step, requires edits/builds/tests, or has ambiguity/unknowns. Escalate from light to full only when needed; if you escalate, say so briefly and continue. + + +Validation and green-before-done: After any substantive change, run the relevant build/tests/linters automatically. For runnable code that you created or edited, immediately run a test to validate the code works (fast, minimal input) yourself using terminal tools. Prefer automated code-based tests where possible. Then provide optional fenced code blocks with commands for larger or platform-specific runs. Don't end a turn with a broken build if you can fix it. If failures occur, iterate up to three targeted fixes; if still failing, summarize the root cause, options, and exact failing output. For non-critical checks (e.g., a flaky health check), retry briefly (2-3 attempts with short backoff) and then proceed with the next step, noting the flake. +Never invent file paths, APIs, or commands. Verify with tools (search/read/list) before acting when uncertain. +Security and side-effects: Do not exfiltrate secrets or make network calls unless explicitly required by the task. Prefer local actions first. +Reproducibility and dependencies: Follow the project's package manager and configuration; prefer minimal, pinned, widely-used libraries and update manifests or lockfiles appropriately. Prefer adding or updating tests when you change public behavior. +Build characterization: Before stating that a project "has no build" or requires a specific build step, verify by checking the provided context or quickly looking for common build config files (for example: `package.json`, `pnpm-lock.yaml`, `requirements.txt`, `pyproject.toml`, `setup.py`, `Makefile`, `Dockerfile`, `build.gradle`, `pom.xml`). If uncertain, say what you know based on the available evidence and proceed with minimal setup instructions; note that you can adapt if additional build configs exist. +Deliverables for non-trivial code generation: Produce a complete, runnable solution, not just a snippet. Create the necessary source files plus a small runner or test/benchmark harness when relevant, a minimal `README.md` with usage and troubleshooting, and a dependency manifest (for example, `package.json`, `requirements.txt`, `pyproject.toml`) updated or added as appropriate. If you intentionally choose not to create one of these artifacts, briefly say why. +Don't repeat yourself after a tool call, pick up where you left off. +You don't need to read a file if it's already provided in context. + + +If the user is requesting a code sample, you can answer it directly without using any tools. +When using a tool, follow the JSON schema very carefully and make sure to include ALL required properties. +No need to ask permission before using a tool. +NEVER say the name of a tool to a user. For example, instead of saying that you'll use the run_in_terminal tool, say "I'll run the command in a terminal". +If you think running multiple tools can answer the user's question, prefer calling them in parallel whenever possible, but do not call semantic_search in parallel. +Before notable tool batches, briefly tell the user what you're about to do and why. After the results return, briefly interpret them and state what you'll do next. Don't narrate every trivial call. +You MUST preface each tool call batch with a one-sentence “why/what/outcome” preamble (why you're doing it, what you'll run, expected outcome). If you make many tool calls in a row, you MUST checkpoint progress after roughly every 3-5 calls: what you ran, key results, and what you'll do next. If you create or edit more than ~3 files in a burst, checkpoint immediately with a compact bullet summary. +If you think running multiple tools can answer the user's question, prefer calling them in parallel whenever possible, but do not call semantic_search in parallel. Parallelize read-only, independent operations only; do not parallelize edits or dependent steps. +Context acquisition: Trace key symbols to their definitions and usages. Read sufficiently large, meaningful chunks to avoid missing context. Prefer semantic or codebase search when you don't know the exact string; prefer exact search or direct reads when you do. Avoid redundant reads when the content is already attached and sufficient. +Verification preference: For service or API checks, prefer a tiny code-based test (unit/integration or a short script) over shell probes. Use shell probes (e.g., curl) only as optional documentation or quick one-off sanity checks, and mark them as optional. +If semantic_search returns the full contents of the text files in the workspace, you have all the workspace context. +You can use the grep_search to get an overview of a file by searching for a string within that one file, instead of using read_file many times. +If you don't know exactly the string or filename pattern you're looking for, use semantic_search to do a semantic search across the workspace. +When invoking a tool that takes a file path, always use the absolute file path. If the file has a scheme like untitled: or vscode-userdata:, then use a URI with the scheme. +You don't currently have any tools available for editing files. If the user asks you to edit a file, you can ask the user to enable editing tools or print a codeblock with the suggested changes. +You don't currently have any tools available for running terminal commands. If the user asks you to run a terminal command, you can ask the user to enable terminal tools or print a codeblock with the suggested command. +Tools can be disabled by the user. You may see tools used previously in the conversation that are not currently available. Be careful to only use the tools that are currently available to you. + + +These instructions only apply when the question is about the user's workspace. +First, analyze the developer's request to determine how complicated their task is. Leverage any of the tools available to you to gather the context needed to provided a complete and accurate response. Keep your search focused on the developer's request, and don't run extra tools if the developer's request clearly can be satisfied by just one. +If the developer wants to implement a feature and they have not specified the relevant files, first break down the developer's request into smaller concepts and think about the kinds of files you need to grasp each concept. +If you aren't sure which tool is relevant, you can call multiple tools. You can call tools repeatedly to take actions or gather as much context as needed. +Don't make assumptions about the situation. Gather enough context to address the developer's request without going overboard. +Think step by step: +1. Read the provided relevant workspace information (code excerpts, file names, and symbols) to understand the user's workspace. +2. Consider how to answer the user's prompt based on the provided information and your specialized coding knowledge. Always assume that the user is asking about the code in their workspace instead of asking a general programming question. Prefer using variables, functions, types, and classes from the workspace over those from the standard library. +3. Generate a response that clearly and accurately answers the user's question. In your response, add fully qualified links for referenced symbols (example: [`namespace.VariableName`](path/to/file.ts)) and links for files (example: [path/to/file](path/to/file.ts)) so that the user can open them. +Remember that you MUST add links for all referenced symbols from the workspace and fully qualify the symbol name in the link, for example: [`namespace.functionName`](path/to/util.ts). +Remember that you MUST add links for all workspace files, for example: [path/to/file.js](path/to/file.js) + + + +These instructions only apply when the question is about the user's workspace. +Unless it is clear that the user's question relates to the current workspace, you should avoid using the code search tools and instead prefer to answer the user's question directly. +Remember that you can call multiple tools in one response. +Use semantic_search to search for high level concepts or descriptions of functionality in the user's question. This is the best place to start if you don't know where to look or the exact strings found in the codebase. +Prefer search_workspace_symbols over grep_search when you have precise code identifiers to search for. +Prefer grep_search over semantic_search when you have precise keywords to search for. +The tools file_search, grep_search, and get_changed_files are deterministic and comprehensive, so do not repeatedly invoke them with the same arguments. + + +When suggesting code changes or new content, use Markdown code blocks. +To start a code block, use 4 backticks. +After the backticks, add the programming language name. +If the code modifies an existing file or should be placed at a specific location, add a line comment with 'filepath:' and the file path. +If you want the user to decide where to place the code, do not add the file path comment. +In the code block, use a line comment with '...existing code...' to indicate code that is already present in the file. +````languageId +// filepath: /path/to/file +// ...existing code... +{ changed code } +// ...existing code... +{ changed code } +// ...existing code... +```` + +Use proper Markdown formatting in your answers. When referring to a filename or symbol in the user's workspace, wrap it in backticks. +When sharing setup or run steps for the user to execute, render commands in fenced code blocks with an appropriate language tag (`bash`, `sh`, `powershell`, `python`, etc.). Keep one command per line; avoid prose-only representations of commands. +Keep responses conversational and fun—use a brief, friendly preamble that acknowledges the goal and states what you're about to do next. Avoid literal scaffold labels like "Plan:", "Task receipt:", or "Actions:"; instead, use short paragraphs and, when helpful, concise bullet lists. Do not start with filler acknowledgements (e.g., "Sounds good", "Great", "Okay, I will…"). For multi-step tasks, maintain a lightweight checklist implicitly and weave progress into your narration. +For section headers in your response, use level-2 Markdown headings (`##`) for top-level sections and level-3 (`###`) for subsections. Choose titles dynamically to match the task and content. Do not hard-code fixed section names; create only the sections that make sense and only when they have non-empty content. Keep headings short and descriptive (e.g., "actions taken", "files changed", "how to run", "performance", "notes"), and order them naturally (actions > artifacts > how to run > performance > notes) when applicable. You may add a tasteful emoji to a heading when it improves scannability; keep it minimal and professional. Headings must start at the beginning of the line with `## ` or `### `, have a blank line before and after, and must not be inside lists, block quotes, or code fences. +When listing files created/edited, include a one-line purpose for each file when helpful. In performance sections, base any metrics on actual runs from this session; note the hardware/OS context and mark estimates clearly—never fabricate numbers. In "Try it" sections, keep commands copyable; comments starting with `#` are okay, but put each command on its own line. +If platform-specific acceleration applies, include an optional speed-up fenced block with commands. Close with a concise completion summary describing what changed and how it was verified (build/tests/linters), plus any follow-ups. + +The class `Person` is in `src/models/person.ts`. + +Use KaTeX for math equations in your answers. +Wrap inline math equations in $. +Wrap more complex blocks of math equations in $$. + + diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index b2f156f14..15bb40c80 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -13,7 +13,7 @@ import PROMPT_GEMINI from "./prompt/gemini.txt" import PROMPT_ANTHROPIC_SPOOF from "./prompt/anthropic_spoof.txt" import PROMPT_SUMMARIZE from "./prompt/summarize.txt" import PROMPT_TITLE from "./prompt/title.txt" -import PROMPT_CODEX from "./prompt/codex.txt" +import PROMPT_COPILOT_GPT_5 from "./prompt/copilot-gpt-5.txt" export namespace SystemPrompt { export function header(providerID: string) { @@ -22,7 +22,7 @@ export namespace SystemPrompt { } export function provider(modelID: string) { - if (modelID.includes("gpt-5")) return [PROMPT_CODEX] + if (modelID.includes("gpt-5")) return [PROMPT_COPILOT_GPT_5] if (modelID.includes("gpt-") || modelID.includes("o1") || modelID.includes("o3")) return [PROMPT_BEAST] if (modelID.includes("gemini-")) return [PROMPT_GEMINI] if (modelID.includes("claude")) return [PROMPT_ANTHROPIC] diff --git a/script/publish.ts b/script/publish.ts index 64afcf341..c0f79bb9c 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -1,7 +1,6 @@ #!/usr/bin/env bun import { $ } from "bun" -import path from "path" console.log("=== publishing ===\n") From 85e0b53c33fff08d06561dd309382f7ff6f5bd27 Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 11 Aug 2025 18:36:06 +0000 Subject: [PATCH 091/178] release: v0.4.19 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/sdk/js/src/gen/types.gen.ts | 4 ++++ packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 10 files changed, 13 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 886c583b4..98e859053 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.18", + "version": "0.4.19", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 9bd9e9e62..9f2514adb 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.18", + "version": "0.4.19", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index f0630a22e..55ad09b38 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.18", + "version": "0.4.19", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 65f322ef1..c699f0683 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.18", + "version": "0.4.19", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 9fd5bfd97..4ab958b5c 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.18", + "version": "0.4.19", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 032ccc69c..2448f1779 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.18", + "version": "0.4.19", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 2a3b0ee62..9e73fa1f8 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.18", + "version": "0.4.19", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index e6d085d83..5bc67b1d8 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -792,6 +792,10 @@ export type KeybindsConfig = { * Toggle tool details */ tool_details: string + /** + * Toggle thinking blocks + */ + thinking_blocks: string /** * List available models */ diff --git a/packages/web/package.json b/packages/web/package.json index 52a4468de..a60f38dba 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.18", + "version": "0.4.19", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 18ca80411..bd2eb6c71 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.18", + "version": "0.4.19", "publisher": "sst-dev", "repository": { "type": "git", From 22023fa9e73513e0ff677646cc3fe18785c5635c Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 14:30:18 -0400 Subject: [PATCH 092/178] remove git bash tool coauthor message --- packages/opencode/src/tool/bash.txt | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/packages/opencode/src/tool/bash.txt b/packages/opencode/src/tool/bash.txt index caf2515ed..ce78347e0 100644 --- a/packages/opencode/src/tool/bash.txt +++ b/packages/opencode/src/tool/bash.txt @@ -59,10 +59,6 @@ When the user asks you to create a new git commit, follow these steps carefully: 3. You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. ALWAYS run the following commands in parallel: - Add relevant untracked files to the staging area. - - Create the commit with a message ending with: - 🤖 Generated with [opencode](https://opencode.ai) - - Co-Authored-By: opencode - Run git status to make sure the commit succeeded. 4. If the commit fails due to pre-commit hook changes, retry the commit ONCE to include these automated changes. If it fails again, it usually means a pre-commit hook is preventing the commit. If the commit succeeds but you notice that files were modified by the pre-commit hook, you MUST amend your commit to include them. @@ -76,17 +72,6 @@ Important notes: - If there are no changes to commit (i.e., no untracked files and no modifications), do not create an empty commit - Ensure your commit message is meaningful and concise. It should explain the purpose of the changes, not just describe them. - Return an empty response - the user will see the git output directly -- In order to ensure good formatting, ALWAYS pass the commit message via a HEREDOC, a la this example: - -git commit -m "$(cat <<'EOF' - Commit message here. - - 🤖 Generated with [opencode](https://opencode.ai) - - Co-Authored-By: opencode - EOF - )" - # Creating pull requests Use the gh command via the Bash tool for ALL GitHub-related tasks including working with issues, pull requests, checks, and releases. If given a Github URL use the gh command to get the information needed. @@ -125,14 +110,6 @@ gh pr create --title "the pr title" --body "$(cat <<'EOF' ## Summary <1-3 bullet points> -## Test plan -[Checklist of TODOs for testing the pull request...] - -🤖 Generated with [opencode](https://opencode.ai) -EOF -)" - - Important: - NEVER update the git config - Return the PR URL when you're done, so the user can see it From 4ff64c620900b3951d924736bc2de02a0b268e22 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 13:38:29 -0500 Subject: [PATCH 093/178] fix: take up less vertical space --- packages/tui/internal/components/chat/message.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index c7f8b0ee2..1bbf3c5ca 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -375,7 +375,6 @@ func renderText( info = author + timestamp } if !showToolDetails && toolCalls != nil && len(toolCalls) > 0 { - content = content + "\n" for _, toolCall := range toolCalls { title := renderToolTitle(toolCall, width-2) style := styles.NewStyle() @@ -390,9 +389,9 @@ func renderText( sections := []string{content} if extra != "" { - sections = append(sections, "\n"+extra) + sections = append(sections, "\n"+extra+"\n") } - sections = append(sections, "\n"+info) + sections = append(sections, info) content = strings.Join(sections, "\n") switch message.(type) { From 7633a951e69a608761bebc351cd35d883dad629e Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 14:43:29 -0400 Subject: [PATCH 094/178] ignore: test --- bun.lock | 27 +++++++++++---------------- packages/opencode/package.json | 1 - packages/web/src/components/Share.tsx | 2 +- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/bun.lock b/bun.lock index 08ea5be4b..7f8a8c01e 100644 --- a/bun.lock +++ b/bun.lock @@ -13,7 +13,7 @@ }, "cloud/core": { "name": "@opencode/cloud-core", - "version": "0.0.0", + "version": "0.4.18", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "drizzle-orm": "0.41.0", @@ -27,7 +27,7 @@ }, "cloud/function": { "name": "@opencode/cloud-function", - "version": "0.3.130", + "version": "0.4.18", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -47,7 +47,7 @@ }, "cloud/web": { "name": "@opencode/cloud-web", - "version": "0.0.0", + "version": "0.4.18", "dependencies": { "@kobalte/core": "0.13.9", "@openauthjs/solid": "0.0.0-20250322224806", @@ -66,7 +66,7 @@ }, "packages/function": { "name": "@opencode/function", - "version": "0.4.1", + "version": "0.4.18", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -81,7 +81,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "0.4.1", + "version": "0.4.18", "bin": { "opencode": "./bin/opencode", }, @@ -120,7 +120,6 @@ }, "devDependencies": { "@ai-sdk/amazon-bedrock": "2.2.10", - "@ai-sdk/anthropic": "1.2.12", "@octokit/webhooks-types": "7.6.1", "@standard-schema/spec": "1.0.0", "@tsconfig/bun": "1.0.7", @@ -134,7 +133,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "0.4.1", + "version": "0.4.18", "dependencies": { "@opencode-ai/sdk": "workspace:*", }, @@ -146,7 +145,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "0.4.1", + "version": "0.4.18", "devDependencies": { "@hey-api/openapi-ts": "0.80.1", "@tsconfig/node22": "catalog:", @@ -155,7 +154,7 @@ }, "packages/web": { "name": "@opencode/web", - "version": "0.4.1", + "version": "0.4.18", "dependencies": { "@astrojs/cloudflare": "^12.5.4", "@astrojs/markdown-remark": "6.3.1", @@ -908,7 +907,7 @@ "@types/babel__traverse": ["@types/babel__traverse@7.20.7", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng=="], - "@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="], + "@types/bun": ["@types/bun@1.2.20", "", { "dependencies": { "bun-types": "1.2.20" } }, "sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA=="], "@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="], @@ -2750,6 +2749,8 @@ "@tufjs/models/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "@types/bun/bun-types": ["bun-types@1.2.20", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-pxTnQYOrKvdOwyiyd/7sMt9yFOenN004Y6O4lCcCUoKVej48FS5cvTw9geRaEcB9TsDZaJKAxPTVvi8tFsVuXA=="], + "ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.1", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-/iP1sKc6UdJgGH98OCly7sWJKv+J9G47PnTjIj40IJMUQKwDrUMyf7zOOfRtPwSuNifYhSoJQ4s1WltI65gJ/g=="], "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -2828,8 +2829,6 @@ "nypm/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@1.2.12", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ=="], - "opencode/@openauthjs/openauth": ["@openauthjs/openauth@0.4.3", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-RlnjqvHzqcbFVymEwhlUEuac4utA5h4nhSK/i2szZuQmxTIqbGUxZ+nM+avM+VV4Ing+/ZaNLKILoXS3yrkOOw=="], "opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="], @@ -3060,10 +3059,6 @@ "local-pkg/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "opencode/@ai-sdk/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], - - "opencode/@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], - "opencode/@openauthjs/openauth/@standard-schema/spec": ["@standard-schema/spec@1.0.0-beta.3", "", {}, "sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw=="], "opencode/@openauthjs/openauth/aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 4ab958b5c..7fa3aa2f1 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -16,7 +16,6 @@ }, "devDependencies": { "@ai-sdk/amazon-bedrock": "2.2.10", - "@ai-sdk/anthropic": "1.2.12", "@octokit/webhooks-types": "7.6.1", "@standard-schema/spec": "1.0.0", "@tsconfig/bun": "1.0.7", diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index 0f60c8f3a..646ee7710 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -61,7 +61,7 @@ export default function Share(props: { const [store, setStore] = createStore<{ info?: Session.Info messages: Record - }>({ info: props.info, messages: mapValues(props.messages, (x: any) => ("metadata" in x ? fromV1(x) : x)) }) + }>({ info: props.info, messages: {} }) const messages = createMemo(() => Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id))) const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>(["disconnected", "Disconnected"]) // createEffect(() => { From 00b4670b8be185b894ef4b0f467d6f177cba91ee Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Mon, 11 Aug 2025 13:44:12 -0500 Subject: [PATCH 095/178] docs: fix instructions (#1827) --- packages/web/src/content/docs/docs/config.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/content/docs/docs/config.mdx b/packages/web/src/content/docs/docs/config.mdx index 61a89cb1f..06eb6ee7d 100644 --- a/packages/web/src/content/docs/docs/config.mdx +++ b/packages/web/src/content/docs/docs/config.mdx @@ -318,7 +318,7 @@ Use `{file:path/to/file}` to substitute the contents of a file: ```json title="opencode.json" { "$schema": "https://opencode.ai/config.json", - "instructions": ["{file:./custom-instructions.md}"], + "instructions": ["./custom-instructions.md"], "provider": { "openai": { "options": { From 7a67fe7dded32e206890e14a887da5dd803caa93 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 13:54:48 -0500 Subject: [PATCH 096/178] fix: collapsed tool calls hidden at times --- packages/tui/internal/components/chat/messages.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go index bbfd557a6..d965ed704 100644 --- a/packages/tui/internal/components/chat/messages.go +++ b/packages/tui/internal/components/chat/messages.go @@ -452,7 +452,7 @@ func (m *messagesComponent) renderView() tea.Cmd { } if finished { - key := m.cache.GenerateKey(casted.ID, part.Text, width, m.showToolDetails) + key := m.cache.GenerateKey(casted.ID, part.Text, width, m.showToolDetails, toolCallParts) content, cached = m.cache.Get(key) if !cached { content = renderText( From 0ce7d92a8b67c5660669b7850d758131d39b076d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 16:12:26 -0400 Subject: [PATCH 097/178] ignore: fix share page --- bun.lock | 28 +- packages/opencode/src/provider/provider.ts | 2 +- packages/web/package.json | 2 +- packages/web/src/components/Share.tsx | 397 +++++++++++---------- 4 files changed, 223 insertions(+), 206 deletions(-) diff --git a/bun.lock b/bun.lock index 7f8a8c01e..9dd752545 100644 --- a/bun.lock +++ b/bun.lock @@ -13,7 +13,7 @@ }, "cloud/core": { "name": "@opencode/cloud-core", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "drizzle-orm": "0.41.0", @@ -27,7 +27,7 @@ }, "cloud/function": { "name": "@opencode/cloud-function", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -47,7 +47,7 @@ }, "cloud/web": { "name": "@opencode/cloud-web", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { "@kobalte/core": "0.13.9", "@openauthjs/solid": "0.0.0-20250322224806", @@ -66,7 +66,7 @@ }, "packages/function": { "name": "@opencode/function", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -81,7 +81,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "0.4.18", + "version": "0.4.19", "bin": { "opencode": "./bin/opencode", }, @@ -133,7 +133,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { "@opencode-ai/sdk": "workspace:*", }, @@ -145,7 +145,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "0.4.18", + "version": "0.4.19", "devDependencies": { "@hey-api/openapi-ts": "0.80.1", "@tsconfig/node22": "catalog:", @@ -154,9 +154,9 @@ }, "packages/web": { "name": "@opencode/web", - "version": "0.4.18", + "version": "0.4.19", "dependencies": { - "@astrojs/cloudflare": "^12.5.4", + "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", "@astrojs/solid-js": "5.1.0", "@astrojs/starlight": "0.34.3", @@ -229,11 +229,11 @@ "@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@11.9.3", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ=="], - "@astrojs/cloudflare": ["@astrojs/cloudflare@12.6.0", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/underscore-redirects": "1.0.0", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-pQ8bokC59GEiXvyXpC4swBNoL7C/EknP+82KFzQwgR/Aeo5N1oPiAoPHgJbpPya/YF4E26WODdCQfBQDvLRfuw=="], + "@astrojs/cloudflare": ["@astrojs/cloudflare@12.6.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.1", "@astrojs/underscore-redirects": "1.0.0", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-xhJptF5tU2k5eo70nIMyL1Udma0CqmUEnGSlGyFflLqSY82CRQI6nWZ/xZt0ZvmXuErUjIx0YYQNfZsz5CNjLQ=="], "@astrojs/compiler": ["@astrojs/compiler@2.12.2", "", {}, "sha512-w2zfvhjNCkNMmMMOn5b0J8+OmUaBL1o40ipMvqcG6NRpdC+lKxmTi48DT8Xw0SzJ3AfmeFLB45zXZXtmbsjcgw=="], - "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.1", "", {}, "sha512-7dwEVigz9vUWDw3nRwLQ/yH/xYovlUA0ZD86xoeKEBmkz9O6iELG1yri67PgAPW6VLL/xInA4t7H0CK6VmtkKQ=="], "@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.1", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/prism": "3.2.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.1", "remark-smartypants": "^3.0.2", "shiki": "^3.0.0", "smol-toml": "^1.3.1", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-c5F5gGrkczUaTVgmMW9g1YMJGzOtRvjjhw6IfGuxarM6ct09MpwysP10US729dy07gg8y+ofVifezvP3BNsWZg=="], @@ -2569,6 +2569,8 @@ "@astrojs/cloudflare/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], + "@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + "@astrojs/mdx/@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.2.1", "smol-toml": "^1.3.4", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-DDRtD1sPvAuA7ms2btc9A7/7DApKqgLMNrE6kh5tmkfy8utD0Z738gqd3p5aViYYdUtHIyEJ1X4mCMxfCfu15w=="], "@astrojs/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], @@ -2757,6 +2759,8 @@ "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + "astro/diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="], "astro/esbuild": ["esbuild@0.25.8", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], @@ -2933,6 +2937,8 @@ "@astrojs/cloudflare/vite/esbuild": ["esbuild@0.25.8", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], + "@astrojs/mdx/@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], + "@astrojs/mdx/@astrojs/markdown-remark/@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], "@astrojs/solid-js/vite/esbuild": ["esbuild@0.25.8", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 184b938da..c72ae7ef3 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -448,7 +448,7 @@ export namespace Provider { } } - const priority = ["gemini-2.5-pro-preview", "codex-mini", "claude-sonnet-4"] + const priority = ["gemini-2.5-pro-preview", "gpt-5", "claude-sonnet-4"] export function sort(models: ModelsDev.Model[]) { return sortBy( models, diff --git a/packages/web/package.json b/packages/web/package.json index a60f38dba..d3c8baaab 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -11,7 +11,7 @@ "astro": "astro" }, "dependencies": { - "@astrojs/cloudflare": "^12.5.4", + "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", "@astrojs/solid-js": "5.1.0", "@astrojs/starlight": "0.34.3", diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index 646ee7710..2b0e52c1a 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -1,7 +1,6 @@ import { For, Show, onMount, Suspense, onCleanup, createMemo, createSignal, SuspenseList, createEffect } from "solid-js" import { DateTime } from "luxon" import { createStore, reconcile, unwrap } from "solid-js/store" -import { mapValues } from "remeda" import { IconArrowDown } from "./icons" import { IconOpencode } from "./icons/custom" import styles from "./share.module.css" @@ -42,7 +41,6 @@ export default function Share(props: { id: string api: string info: Session.Info - messages: Record }) { let lastScrollY = 0 let hasScrolledToAnchor = false @@ -50,7 +48,6 @@ export default function Share(props: { let scrollSentinel: HTMLElement | undefined let scrollObserver: IntersectionObserver | undefined - const id = props.id const params = new URLSearchParams(window.location.search) const debug = params.get("debug") === "true" @@ -61,17 +58,27 @@ export default function Share(props: { const [store, setStore] = createStore<{ info?: Session.Info messages: Record - }>({ info: props.info, messages: {} }) + }>({ + info: { + id: props.id, + title: props.info.title, + version: props.info.version, + time: { + created: props.info.time.created, + updated: props.info.time.updated, + }, + }, messages: {} + }) const messages = createMemo(() => Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id))) const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>(["disconnected", "Disconnected"]) - // createEffect(() => { - // console.log(unwrap(store)) - // }) + createEffect(() => { + console.log(unwrap(store)) + }) onMount(() => { const apiUrl = props.api - if (!id) { + if (!props.id) { setConnectionStatus(["error", "id not found"]) return } @@ -96,7 +103,7 @@ export default function Share(props: { // Always use secure WebSocket protocol (wss) const wsBaseUrl = apiUrl.replace(/^https?:\/\//, "wss://") - const wsUrl = `${wsBaseUrl}/share_poll?id=${id}` + const wsUrl = `${wsBaseUrl}/share_poll?id=${props.id}` console.log("Connecting to WebSocket URL:", wsUrl) // Create WebSocket connection @@ -261,7 +268,9 @@ export default function Share(props: { }, } - result.created = props.info.time.created + if (!store.info) return result + + result.created = store.info.time.created const msgs = messages() for (let i = 0; i < msgs.length; i++) { @@ -290,197 +299,199 @@ export default function Share(props: { }) return ( -
-
-

{store.info?.title}

-
-
    -
  • -
    - -
    - - v{store.info?.version} - -
  • - {Object.values(data().models).length > 0 ? ( - - {([provider, model]) => ( -
  • -
    - -
    - {model} -
  • - )} -
    - ) : ( -
  • - Models - + +
    +
    +

    {store.info?.title}

    +
    +
      +
    • +
      + +
      + + v{store.info?.version} +
    • - )} -
    -
    - {DateTime.fromMillis(data().created || 0).toLocaleString(DateTime.DATETIME_MED)} -
    -
    -
    - -
    - 0} fallback={

    Waiting for messages...

    }> -
    - - - {(msg, msgIndex) => { - const filteredParts = createMemo(() => - msg.parts.filter((x, index) => { - if (x.type === "step-start" && index > 0) return false - if (x.type === "snapshot") return false - if (x.type === "patch") return false - if (x.type === "step-finish") return false - if (x.type === "text" && x.synthetic === true) return false - if (x.type === "tool" && x.tool === "todoread") return false - if (x.type === "text" && !x.text) return false - if (x.type === "tool" && (x.state.status === "pending" || x.state.status === "running")) - return false - return true - }), - ) - - return ( - - - {(part, partIndex) => { - const last = createMemo( - () => - data().messages.length === msgIndex() + 1 && filteredParts().length === partIndex() + 1, - ) - - onMount(() => { - const hash = window.location.hash.slice(1) - // Wait till all parts are loaded - if ( - hash !== "" && - !hasScrolledToAnchor && - filteredParts().length === partIndex() + 1 && - data().messages.length === msgIndex() + 1 - ) { - hasScrolledToAnchor = true - scrollToAnchor(hash) - } - }) - - return - }} - - - ) - }} - - -
    -
    - -
    -
    -

    {getStatusText(connectionStatus())}

    -
      -
    • - Cost - {data().cost !== undefined ? ( - ${data().cost.toFixed(2)} - ) : ( - - )} -
    • -
    • - Input Tokens - {data().tokens.input ? {data().tokens.input} : } -
    • -
    • - Output Tokens - {data().tokens.output ? {data().tokens.output} : } -
    • -
    • - Reasoning Tokens - {data().tokens.reasoning ? ( - {data().tokens.reasoning} - ) : ( - - )} -
    • -
    -
    -
    -
    -
    -
    - - -
    -
    - 0} fallback={

    Waiting for messages...

    }> -
      - - {(msg) => ( -
    • -
      - Key: {msg.id} + {Object.values(data().models).length > 0 ? ( + + {([provider, model]) => ( +
    • +
      +
      -
      {JSON.stringify(msg, null, 2)}
      + {model}
    • )}
      -
    -
    + ) : ( +
  • + Models + +
  • + )} +
+
+ {DateTime.fromMillis(data().created || 0).toLocaleString(DateTime.DATETIME_MED)} +
- - - - -
+
+ 0} fallback={

Waiting for messages...

}> +
+ + + {(msg, msgIndex) => { + const filteredParts = createMemo(() => + msg.parts.filter((x, index) => { + if (x.type === "step-start" && index > 0) return false + if (x.type === "snapshot") return false + if (x.type === "patch") return false + if (x.type === "step-finish") return false + if (x.type === "text" && x.synthetic === true) return false + if (x.type === "tool" && x.tool === "todoread") return false + if (x.type === "text" && !x.text) return false + if (x.type === "tool" && (x.state.status === "pending" || x.state.status === "running")) + return false + return true + }), + ) + + return ( + + + {(part, partIndex) => { + const last = createMemo( + () => + data().messages.length === msgIndex() + 1 && filteredParts().length === partIndex() + 1, + ) + + onMount(() => { + const hash = window.location.hash.slice(1) + // Wait till all parts are loaded + if ( + hash !== "" && + !hasScrolledToAnchor && + filteredParts().length === partIndex() + 1 && + data().messages.length === msgIndex() + 1 + ) { + hasScrolledToAnchor = true + scrollToAnchor(hash) + } + }) + + return + }} + + + ) + }} + + +
+
+ +
+
+

{getStatusText(connectionStatus())}

+
    +
  • + Cost + {data().cost !== undefined ? ( + ${data().cost.toFixed(2)} + ) : ( + + )} +
  • +
  • + Input Tokens + {data().tokens.input ? {data().tokens.input} : } +
  • +
  • + Output Tokens + {data().tokens.output ? {data().tokens.output} : } +
  • +
  • + Reasoning Tokens + {data().tokens.reasoning ? ( + {data().tokens.reasoning} + ) : ( + + )} +
  • +
+
+
+
+
+
+ + +
+
+ 0} fallback={

Waiting for messages...

}> +
    + + {(msg) => ( +
  • +
    + Key: {msg.id} +
    +
    {JSON.stringify(msg, null, 2)}
    +
  • + )} +
    +
+
+
+
+
+ + + + + + ) } From b2a4f57d640655b78f25d4485d2cf137637c3c53 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:32:09 -0500 Subject: [PATCH 098/178] feat: add -c and -s args to tui command following run command pattern (#1835) --- packages/opencode/src/cli/cmd/tui.ts | 25 +++++++++++++ packages/tui/cmd/opencode/main.go | 3 +- packages/tui/internal/app/app.go | 53 ++++++++++++++++++++-------- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui.ts b/packages/opencode/src/cli/cmd/tui.ts index d9bba2028..3d8028af7 100644 --- a/packages/opencode/src/cli/cmd/tui.ts +++ b/packages/opencode/src/cli/cmd/tui.ts @@ -14,6 +14,7 @@ import { FileWatcher } from "../../file/watch" import { Ide } from "../../ide" import { Agent } from "../../agent/agent" import { Flag } from "../../flag/flag" +import { Session } from "../../session" declare global { const OPENCODE_TUI_PATH: string @@ -39,6 +40,16 @@ export const TuiCommand = cmd({ alias: ["m"], describe: "model to use in the format of provider/model", }) + .option("continue", { + alias: ["c"], + describe: "continue the last session", + type: "boolean", + }) + .option("session", { + alias: ["s"], + describe: "session id to continue", + type: "string", + }) .option("prompt", { alias: ["p"], type: "string", @@ -69,6 +80,19 @@ export const TuiCommand = cmd({ return } const result = await bootstrap({ cwd }, async (app) => { + const sessionID = await (async () => { + if (args.continue) { + const list = Session.list() + const first = await list.next() + await list.return() + if (first.done) return + return first.value.id + } + if (args.session) { + return args.session + } + return undefined + })() FileWatcher.init() const providers = await Provider.list() if (Object.keys(providers).length === 0) { @@ -106,6 +130,7 @@ export const TuiCommand = cmd({ ...(args.model ? ["--model", args.model] : []), ...(args.prompt ? ["--prompt", args.prompt] : []), ...(args.mode ? ["--mode", args.mode] : []), + ...(sessionID ? ["--session", sessionID] : []), ], cwd, stdout: "inherit", diff --git a/packages/tui/cmd/opencode/main.go b/packages/tui/cmd/opencode/main.go index 654eb0adc..c242a737a 100644 --- a/packages/tui/cmd/opencode/main.go +++ b/packages/tui/cmd/opencode/main.go @@ -32,6 +32,7 @@ func main() { var model *string = flag.String("model", "", "model to begin with") var prompt *string = flag.String("prompt", "", "prompt to begin with") var agent *string = flag.String("agent", "", "agent to begin with") + var sessionID *string = flag.String("session", "", "session ID") flag.Parse() url := os.Getenv("OPENCODE_SERVER") @@ -96,7 +97,7 @@ func main() { }() // Create main context for the application - app_, err := app.New(ctx, version, appInfo, agents, httpClient, model, prompt, agent) + app_, err := app.New(ctx, version, appInfo, agents, httpClient, model, prompt, agent, sessionID) if err != nil { panic(err) } diff --git a/packages/tui/internal/app/app.go b/packages/tui/internal/app/app.go index a07637794..47520257d 100644 --- a/packages/tui/internal/app/app.go +++ b/packages/tui/internal/app/app.go @@ -46,6 +46,7 @@ type App struct { InitialModel *string InitialPrompt *string InitialAgent *string + InitialSession *string compactCancel context.CancelFunc IsLeaderSequence bool } @@ -95,6 +96,7 @@ func New( initialModel *string, initialPrompt *string, initialAgent *string, + initialSession *string, ) (*App, error) { util.RootPath = appInfo.Path.Root util.CwdPath = appInfo.Path.Cwd @@ -175,20 +177,21 @@ func New( slog.Debug("Loaded config", "config", configInfo) app := &App{ - Info: appInfo, - Agents: agents, - Version: version, - StatePath: appStatePath, - Config: configInfo, - State: appState, - Client: httpClient, - AgentIndex: agentIndex, - Session: &opencode.Session{}, - Messages: []Message{}, - Commands: commands.LoadFromConfig(configInfo), - InitialModel: initialModel, - InitialPrompt: initialPrompt, - InitialAgent: initialAgent, + Info: appInfo, + Agents: agents, + Version: version, + StatePath: appStatePath, + Config: configInfo, + State: appState, + Client: httpClient, + AgentIndex: agentIndex, + Session: &opencode.Session{}, + Messages: []Message{}, + Commands: commands.LoadFromConfig(configInfo), + InitialModel: initialModel, + InitialPrompt: initialPrompt, + InitialAgent: initialAgent, + InitialSession: initialSession, } return app, nil @@ -504,6 +507,28 @@ func (a *App) InitializeProvider() tea.Cmd { Provider: *selectedProvider, Model: *selectedModel, })) + + // Load initial session if provided + if a.InitialSession != nil && *a.InitialSession != "" { + cmds = append(cmds, func() tea.Msg { + // Find the session by ID + sessions, err := a.ListSessions(context.Background()) + if err != nil { + slog.Error("Failed to list sessions for initial session", "error", err) + return toast.NewErrorToast("Failed to load initial session")() + } + + for _, session := range sessions { + if session.ID == *a.InitialSession { + return SessionSelectedMsg(&session) + } + } + + slog.Warn("Initial session not found", "sessionID", *a.InitialSession) + return toast.NewErrorToast("Session not found: " + *a.InitialSession)() + }) + } + if a.InitialPrompt != nil && *a.InitialPrompt != "" { cmds = append(cmds, util.CmdHandler(SendPrompt{Text: *a.InitialPrompt})) } From 04b51f261045a6621deb77a4a32399b93b66fff8 Mon Sep 17 00:00:00 2001 From: Jay V Date: Mon, 11 Aug 2025 19:36:17 -0400 Subject: [PATCH 099/178] ignore: share page thinking blocks --- packages/web/src/components/icons/custom.tsx | 4 +- .../web/src/components/share/part.module.css | 9 ++-- packages/web/src/components/share/part.tsx | 44 ++++++------------- packages/web/src/pages/s/[id].astro | 1 - 4 files changed, 19 insertions(+), 39 deletions(-) diff --git a/packages/web/src/components/icons/custom.tsx b/packages/web/src/components/icons/custom.tsx index ffa4fea82..8023032e5 100644 --- a/packages/web/src/components/icons/custom.tsx +++ b/packages/web/src/components/icons/custom.tsx @@ -75,7 +75,7 @@ export function IconRobot(props: JSX.SvgSVGAttributes) { } // https://icones.js.org/collection/ri?s=brain&icon=ri:brain-2-line -/* export function IconBrain(props: JSX.SvgSVGAttributes) { +export function IconBrain(props: JSX.SvgSVGAttributes) { return ( ) { /> ) -} */ +} diff --git a/packages/web/src/components/share/part.module.css b/packages/web/src/components/share/part.module.css index 5634f6102..85c3cc9b9 100644 --- a/packages/web/src/components/share/part.module.css +++ b/packages/web/src/components/share/part.module.css @@ -128,29 +128,28 @@ max-width: var(--md-tool-width); } - /* [data-component="assistant-reasoning"] { + [data-component="assistant-reasoning"] { min-width: 0; display: flex; flex-direction: column; - gap: 1rem; + gap: 0.5rem; flex-grow: 1; max-width: var(--md-tool-width); [data-component="assistant-reasoning-markdown"] { align-self: flex-start; - font-size: 0.875rem; + font-size: 0.75rem; border: 1px solid var(--sl-color-blue-high); padding: 0.5rem calc(0.5rem + 3px); border-radius: 0.25rem; position: relative; - margin-top: 0.5rem; [data-component="copy-button"] { top: 0.5rem; right: calc(0.5rem - 1px); } } - } */ + } [data-component="assistant-text"] { min-width: 0; diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index 7c7220c37..b27e4806a 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -19,7 +19,7 @@ import { IconMagnifyingGlass, IconDocumentMagnifyingGlass, } from "../icons" -import { IconMeta, IconRobot, IconOpenAI, IconGemini, IconAnthropic /*, IconBrain */ } from "../icons/custom" +import { IconMeta, IconRobot, IconOpenAI, IconGemini, IconAnthropic, IconBrain } from "../icons/custom" import { ContentCode } from "./content-code" import { ContentDiff } from "./content-diff" import { ContentText } from "./content-text" @@ -83,9 +83,9 @@ export function Part(props: PartProps) { > {(model) => } - {/* + - */} + @@ -148,33 +148,26 @@ export function Part(props: PartProps) { )} > {DateTime.fromMillis(props.message.time.completed).toLocaleString(DateTime.DATETIME_MED)} - {/* - DateTime.fromMillis(props.message.time.completed || props.message.time.created).toLocaleString( - DateTime.DATETIME_MED, - )} - {` | ${props.message.modelID}`} - {props.message.mode && ( - {` | ${props.message.mode}`} - ) - */} )} )} - {/* {props.message.role === "assistant" && props.part.type === "reasoning" && ( + {props.message.role === "assistant" && props.part.type === "reasoning" && (
Thinking
-
- -
- -
-
+ +
+ +
+ +
+
+
- )} */} + )} { props.message.role === "user" && props.part.type === "file" && (
@@ -188,17 +181,6 @@ export function Part(props: PartProps) {
{props.message.providerID}
{props.message.modelID}
- { /* -
- {DateTime.fromMillis(props.message.time.completed || props.message.time.created).toLocaleString( - DateTime.DATETIME_MED, - )} - {` | ${props.message.modelID}`} - {props.message.mode && ( - {` | ${props.message.mode}`} - )} -
- */ }
) } diff --git a/packages/web/src/pages/s/[id].astro b/packages/web/src/pages/s/[id].astro index ff9d15e66..df39f0070 100644 --- a/packages/web/src/pages/s/[id].astro +++ b/packages/web/src/pages/s/[id].astro @@ -1,6 +1,5 @@ --- import { Base64 } from "js-base64"; -// import config from "virtual:starlight/user-config"; import config from '../../../config.mjs' import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; From febf902dc4c20c52c95c05611bfa6086e713d464 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:36:15 -0500 Subject: [PATCH 100/178] Revert "feat: improve file attachment pasting (#1704)" This reverts commit 81a3e02474445bc4d22c7857d4c3b1f591c1cb56. --- .../tui/internal/components/chat/editor.go | 208 ++++--------- .../internal/components/chat/editor_test.go | 277 ------------------ packages/tui/internal/tui/tui.go | 3 - 3 files changed, 51 insertions(+), 437 deletions(-) delete mode 100644 packages/tui/internal/components/chat/editor_test.go diff --git a/packages/tui/internal/components/chat/editor.go b/packages/tui/internal/components/chat/editor.go index 1cfbbc064..509de6244 100644 --- a/packages/tui/internal/components/chat/editor.go +++ b/packages/tui/internal/components/chat/editor.go @@ -27,50 +27,6 @@ import ( "github.com/sst/opencode/internal/util" ) -type AttachmentInsertedMsg struct{} - -// unescapeClipboardText trims surrounding quotes from clipboard text and returns the inner content. -// It avoids interpreting backslash escape sequences unless the text is explicitly quoted. -func (m *editorComponent) unescapeClipboardText(s string) string { - t := strings.TrimSpace(s) - if len(t) >= 2 { - first := t[0] - last := t[len(t)-1] - if (first == '"' && last == '"') || (first == '\'' && last == '\'') { - if u, err := strconv.Unquote(t); err == nil { - return u - } - return t[1 : len(t)-1] - } - } - return t -} - -// pathExists checks if the given path exists. Relative paths are resolved against the app CWD. -// Supports expanding '~' to the user's home directory. -func (m *editorComponent) pathExists(p string) bool { - if p == "" { - return false - } - if strings.HasPrefix(p, "~") { - if home, err := os.UserHomeDir(); err == nil { - if p == "~" { - p = home - } else if strings.HasPrefix(p, "~/") { - p = filepath.Join(home, p[2:]) - } - } - } - check := p - if !filepath.IsAbs(check) { - check = filepath.Join(m.app.Info.Path.Cwd, check) - } - if _, err := os.Stat(check); err == nil { - return true - } - return false -} - type EditorComponent interface { tea.Model tea.ViewModel @@ -197,123 +153,60 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } case tea.PasteMsg: - // Normalize clipboard text first - textRaw := string(msg) - text := m.unescapeClipboardText(textRaw) + text := string(msg) - // Case 1: pasted content contains one or more inline @paths -> insert attachments inline - // We scan the raw pasted text to preserve original content around attachments. - if strings.Contains(textRaw, "@") { - last := 0 - idx := 0 - inserted := 0 - for idx < len(textRaw) { - r, size := utf8.DecodeRuneInString(textRaw[idx:]) - if r != '@' { - idx += size - continue - } - - // Insert preceding chunk before attempting to consume a path - if idx > last { - m.textarea.InsertRunesFromUserInput([]rune(textRaw[last:idx])) - } - - // Extract candidate path after '@' up to whitespace - start := idx + size - end := start - for end < len(textRaw) { - nr, ns := utf8.DecodeRuneInString(textRaw[end:]) - if nr == ' ' || nr == '\t' || nr == '\n' || nr == '\r' { - break - } - end += ns - } - - if end > start { - raw := textRaw[start:end] - // Trim common trailing punctuation that may follow paths in prose - trimmed := strings.TrimRight(raw, ",.;:)]}\\\"'?!") - suffix := raw[len(trimmed):] - p := filepath.Clean(trimmed) - if m.pathExists(p) { - att := m.createAttachmentFromPath(p) - if att != nil { - m.textarea.InsertAttachment(att) - if suffix != "" { - m.textarea.InsertRunesFromUserInput([]rune(suffix)) - } - // Insert a trailing space only if the next rune isn't already whitespace - insertSpace := true - if end < len(textRaw) { - nr, _ := utf8.DecodeRuneInString(textRaw[end:]) - if nr == ' ' || nr == '\t' || nr == '\n' || nr == '\r' { - insertSpace = false - } - } - if insertSpace { - m.textarea.InsertString(" ") - } - inserted++ - last = end - idx = end - continue - } - } - } - - // No valid path -> keep the '@' literally - m.textarea.InsertRune('@') - last = start - idx = start + if filePath := strings.TrimSpace(strings.TrimPrefix(text, "@")); strings.HasPrefix(text, "@") && filePath != "" { + statPath := filePath + if !filepath.IsAbs(filePath) { + statPath = filepath.Join(m.app.Info.Path.Cwd, filePath) } - // Insert any trailing content after the last processed segment - if last < len(textRaw) { - m.textarea.InsertRunesFromUserInput([]rune(textRaw[last:])) - } - if inserted > 0 { - return m, util.CmdHandler(AttachmentInsertedMsg{}) - } - } - - // Case 2: user typed '@' and then pasted a valid path -> replace '@' with attachment - at := m.textarea.LastRuneIndex('@') - if at != -1 && at == m.textarea.CursorColumn()-1 { - p := filepath.Clean(text) - if m.pathExists(p) { - cur := m.textarea.CursorColumn() - m.textarea.ReplaceRange(at, cur, "") - att := m.createAttachmentFromPath(p) - if att != nil { - m.textarea.InsertAttachment(att) + if _, err := os.Stat(statPath); err == nil { + attachment := m.createAttachmentFromPath(filePath) + if attachment != nil { + m.textarea.InsertAttachment(attachment) m.textarea.InsertString(" ") - return m, util.CmdHandler(AttachmentInsertedMsg{}) + return m, nil } } } - // Case 3: plain path pasted (e.g., drag-and-drop) -> attach if image or PDF - { - p := filepath.Clean(text) - if m.pathExists(p) { - mime := getMediaTypeFromExtension(strings.ToLower(filepath.Ext(p))) - if strings.HasPrefix(mime, "image/") || mime == "application/pdf" { - if att := m.createAttachmentFromFile(p); att != nil { - m.textarea.InsertAttachment(att) - m.textarea.InsertString(" ") - return m, util.CmdHandler(AttachmentInsertedMsg{}) - } - } + text = strings.ReplaceAll(text, "\\", "") + text, err := strconv.Unquote(`"` + text + `"`) + if err != nil { + slog.Error("Failed to unquote text", "error", err) + text := string(msg) + if m.shouldSummarizePastedText(text) { + m.handleLongPaste(text) + } else { + m.textarea.InsertRunesFromUserInput([]rune(msg)) } - } - - // Default: do not auto-convert. Insert raw text or summarize long pastes. - if m.shouldSummarizePastedText(textRaw) { - m.handleLongPaste(textRaw) return m, nil } - m.textarea.InsertRunesFromUserInput([]rune(textRaw)) - return m, nil + if _, err := os.Stat(text); err != nil { + slog.Error("Failed to paste file", "error", err) + text := string(msg) + if m.shouldSummarizePastedText(text) { + m.handleLongPaste(text) + } else { + m.textarea.InsertRunesFromUserInput([]rune(msg)) + } + return m, nil + } + + filePath := text + + attachment := m.createAttachmentFromFile(filePath) + if attachment == nil { + if m.shouldSummarizePastedText(text) { + m.handleLongPaste(text) + } else { + m.textarea.InsertRunesFromUserInput([]rune(msg)) + } + return m, nil + } + + m.textarea.InsertAttachment(attachment) + m.textarea.InsertString(" ") case tea.ClipboardMsg: text := string(msg) // Check if the pasted text is long and should be summarized @@ -340,7 +233,7 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if atIndex == -1 { // Should not happen, but as a fallback, just insert. m.textarea.InsertString(msg.Item.Value + " ") - return m, util.CmdHandler(AttachmentInsertedMsg{}) + return m, nil } // The range to replace is from the '@' up to the current cursor position. @@ -354,13 +247,13 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { attachment := m.createAttachmentFromPath(filePath) m.textarea.InsertAttachment(attachment) m.textarea.InsertString(" ") - return m, util.CmdHandler(AttachmentInsertedMsg{}) + return m, nil case "symbols": atIndex := m.textarea.LastRuneIndex('@') if atIndex == -1 { // Should not happen, but as a fallback, just insert. m.textarea.InsertString(msg.Item.Value + " ") - return m, util.CmdHandler(AttachmentInsertedMsg{}) + return m, nil } cursorCol := m.textarea.CursorColumn() @@ -394,13 +287,13 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } m.textarea.InsertAttachment(attachment) m.textarea.InsertString(" ") - return m, util.CmdHandler(AttachmentInsertedMsg{}) + return m, nil case "agents": atIndex := m.textarea.LastRuneIndex('@') if atIndex == -1 { // Should not happen, but as a fallback, just insert. m.textarea.InsertString(msg.Item.Value + " ") - return m, util.CmdHandler(AttachmentInsertedMsg{}) + return m, nil } cursorCol := m.textarea.CursorColumn() @@ -418,7 +311,8 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.textarea.InsertAttachment(attachment) m.textarea.InsertString(" ") - return m, util.CmdHandler(AttachmentInsertedMsg{}) + return m, nil + default: slog.Debug("Unknown provider", "provider", msg.Item.ProviderID) return m, nil diff --git a/packages/tui/internal/components/chat/editor_test.go b/packages/tui/internal/components/chat/editor_test.go deleted file mode 100644 index f43a4078c..000000000 --- a/packages/tui/internal/components/chat/editor_test.go +++ /dev/null @@ -1,277 +0,0 @@ -package chat - -import ( - "os" - "path/filepath" - "strings" - "testing" - - "github.com/charmbracelet/bubbles/v2/spinner" - tea "github.com/charmbracelet/bubbletea/v2" - "github.com/sst/opencode/internal/app" - "github.com/sst/opencode/internal/completions" - "github.com/sst/opencode/internal/components/dialog" - "github.com/sst/opencode/internal/components/textarea" - "github.com/sst/opencode/internal/styles" -) - -func newTestEditor() *editorComponent { - m := &editorComponent{ - app: &app.App{}, - textarea: textarea.New(), - spinner: spinner.New(), - } - return m -} - -func TestPasteAtPathWithTrailingComma_PreservesPunctuation_NoDoubleSpace(t *testing.T) { - m := newTestEditor() - p := createTempTextFile(t, "", "pc.txt", "x") - - paste := "See @" + p + ", next" - _, cmd := m.Update(tea.PasteMsg(paste)) - if cmd == nil { - t.Fatalf("expected command to be returned for comma punctuation paste") - } - if _, ok := cmd().(AttachmentInsertedMsg); !ok { - t.Fatalf("expected AttachmentInsertedMsg for comma punctuation paste") - } - if len(m.textarea.GetAttachments()) != 1 { - t.Fatalf("expected 1 attachment, got %d", len(m.textarea.GetAttachments())) - } - v := m.Value() - if !strings.Contains(v, ", next") { - t.Fatalf("expected comma and following text to be preserved, got: %q", v) - } - if strings.Contains(v, ", next") { - t.Fatalf("did not expect double space after comma, got: %q", v) - } -} - -func TestPasteAtPathWithTrailingQuestion_PreservesPunctuation_NoDoubleSpace(t *testing.T) { - m := newTestEditor() - p := createTempTextFile(t, "", "pq.txt", "x") - - paste := "Check @" + p + "? Done" - _, cmd := m.Update(tea.PasteMsg(paste)) - if cmd == nil { - t.Fatalf("expected command to be returned for question punctuation paste") - } - if _, ok := cmd().(AttachmentInsertedMsg); !ok { - t.Fatalf("expected AttachmentInsertedMsg for question punctuation paste") - } - if len(m.textarea.GetAttachments()) != 1 { - t.Fatalf("expected 1 attachment, got %d", len(m.textarea.GetAttachments())) - } - v := m.Value() - if !strings.Contains(v, "? Done") { - t.Fatalf("expected question mark and following text to be preserved, got: %q", v) - } - if strings.Contains(v, "? Done") { - t.Fatalf("did not expect double space after question mark, got: %q", v) - } -} - -func TestPasteMultipleInlineAtPaths_AttachesEach(t *testing.T) { - m := newTestEditor() - dir := t.TempDir() - p1 := createTempTextFile(t, dir, "m1.txt", "one") - p2 := createTempTextFile(t, dir, "m2.txt", "two") - - // Build a paste with text around, two @paths, and punctuation after the first - paste := "Please check @" + p1 + ", and also @" + p2 + " thanks" - - _, cmd := m.Update(tea.PasteMsg(paste)) - if cmd == nil { - t.Fatalf("expected command to be returned for multi inline paste") - } - if _, ok := cmd().(AttachmentInsertedMsg); !ok { - t.Fatalf("expected AttachmentInsertedMsg for multi inline paste") - } - - atts := m.textarea.GetAttachments() - if len(atts) != 2 { - t.Fatalf("expected 2 attachments, got %d", len(atts)) - } - v := m.Value() - if !strings.Contains(v, "Please check") || !strings.Contains(v, "and also") || !strings.Contains(v, "thanks") { - t.Fatalf("expected surrounding text to be preserved, got: %q", v) - } -} - -func createTempTextFile(t *testing.T, dir, name, content string) string { - t.Helper() - if dir == "" { - td, err := os.MkdirTemp("", "editor-test-*") - if err != nil { - t.Fatalf("failed to make temp dir: %v", err) - } - dir = td - } - p := filepath.Join(dir, name) - if err := os.WriteFile(p, []byte(content), 0o600); err != nil { - t.Fatalf("failed to write temp file: %v", err) - } - abs, err := filepath.Abs(p) - if err != nil { - t.Fatalf("failed to get abs path: %v", err) - } - return abs -} - -func createTempBinFile(t *testing.T, dir, name string, data []byte) string { - t.Helper() - if dir == "" { - td, err := os.MkdirTemp("", "editor-test-*") - if err != nil { - t.Fatalf("failed to make temp dir: %v", err) - } - dir = td - } - p := filepath.Join(dir, name) - if err := os.WriteFile(p, data, 0o600); err != nil { - t.Fatalf("failed to write temp bin file: %v", err) - } - abs, err := filepath.Abs(p) - if err != nil { - t.Fatalf("failed to get abs path: %v", err) - } - return abs -} - -func TestPasteStartsWithAt_AttachesAndEmitsMsg(t *testing.T) { - m := newTestEditor() - p := createTempTextFile(t, "", "a.txt", "hello") - - _, cmd := m.Update(tea.PasteMsg("@" + p)) - if cmd == nil { - t.Fatalf("expected command to be returned") - } - msg := cmd() - if _, ok := msg.(AttachmentInsertedMsg); !ok { - t.Fatalf("expected AttachmentInsertedMsg, got %T", msg) - } - - atts := m.textarea.GetAttachments() - if len(atts) != 1 { - t.Fatalf("expected 1 attachment, got %d", len(atts)) - } - if v := m.Value(); !strings.HasSuffix(v, " ") { - t.Fatalf("expected trailing space after attachment, got value: %q", v) - } -} - -func TestPasteAfterAt_ReplacesAtWithAttachment(t *testing.T) { - m := newTestEditor() - p := createTempTextFile(t, "", "b.txt", "hello") - - m.textarea.SetValue("@") - // Cursor should be at the end after SetValue; paste absolute path - _, cmd := m.Update(tea.PasteMsg(p)) - if cmd == nil { - t.Fatalf("expected command to be returned") - } - if _, ok := cmd().(AttachmentInsertedMsg); !ok { - t.Fatalf("expected AttachmentInsertedMsg from paste after '@'") - } - - // Ensure the raw '@' rune was removed (attachment inserted in its place) - if m.textarea.LastRuneIndex('@') != -1 { - t.Fatalf("'@' rune should have been removed from the text slice") - } - if len(m.textarea.GetAttachments()) != 1 { - t.Fatalf("expected 1 attachment inserted") - } - if v := m.Value(); !strings.HasSuffix(v, " ") { - t.Fatalf("expected trailing space after attachment, got value: %q", v) - } -} - -func TestPlainTextPaste_NoAttachment_NoMsg(t *testing.T) { - m := newTestEditor() - _, cmd := m.Update(tea.PasteMsg("hello")) - if cmd != nil { - t.Fatalf("expected no command for plain text paste") - } - if got := m.Value(); got != "hello" { - t.Fatalf("expected value 'hello', got %q", got) - } - if len(m.textarea.GetAttachments()) != 0 { - t.Fatalf("expected no attachments for plain text paste") - } -} - -func TestPlainPathPng_AttachesImage(t *testing.T) { - m := newTestEditor() - // Minimal bytes; content isn't validated, extension determines mime - p := createTempBinFile(t, "", "img.png", []byte{0x89, 'P', 'N', 'G'}) - - _, cmd := m.Update(tea.PasteMsg(p)) - if cmd == nil { - t.Fatalf("expected command to be returned for image path paste") - } - if _, ok := cmd().(AttachmentInsertedMsg); !ok { - t.Fatalf("expected AttachmentInsertedMsg for image path paste") - } - atts := m.textarea.GetAttachments() - if len(atts) != 1 { - t.Fatalf("expected 1 attachment, got %d", len(atts)) - } - if atts[0].MediaType != "image/png" { - t.Fatalf("expected image/png mime, got %q", atts[0].MediaType) - } - if v := m.Value(); !strings.HasSuffix(v, " ") { - t.Fatalf("expected trailing space after attachment, got value: %q", v) - } -} - -func TestPlainPathPdf_AttachesPDF(t *testing.T) { - m := newTestEditor() - p := createTempBinFile(t, "", "doc.pdf", []byte("%PDF-1.4")) - - _, cmd := m.Update(tea.PasteMsg(p)) - if cmd == nil { - t.Fatalf("expected command to be returned for pdf path paste") - } - if _, ok := cmd().(AttachmentInsertedMsg); !ok { - t.Fatalf("expected AttachmentInsertedMsg for pdf path paste") - } - atts := m.textarea.GetAttachments() - if len(atts) != 1 { - t.Fatalf("expected 1 attachment, got %d", len(atts)) - } - if atts[0].MediaType != "application/pdf" { - t.Fatalf("expected application/pdf mime, got %q", atts[0].MediaType) - } - if v := m.Value(); !strings.HasSuffix(v, " ") { - t.Fatalf("expected trailing space after attachment, got value: %q", v) - } -} - -func TestCompletionFiles_InsertsAttachment_EmitsMsg(t *testing.T) { - m := newTestEditor() - p := createTempTextFile(t, "", "c.txt", "hello") - m.textarea.SetValue("@") - - item := completions.CompletionSuggestion{ - ProviderID: "files", - Value: p, - Display: func(_ styles.Style) string { return p }, - } - // Build the completion selected message as if the user selected from the dialog - msg := dialog.CompletionSelectedMsg{Item: item, SearchString: "@"} - - _, cmd := m.Update(msg) - if cmd == nil { - t.Fatalf("expected command to be returned") - } - if _, ok := cmd().(AttachmentInsertedMsg); !ok { - t.Fatalf("expected AttachmentInsertedMsg from files completion selection") - } - if len(m.textarea.GetAttachments()) != 1 { - t.Fatalf("expected 1 attachment inserted from completion selection") - } - if v := m.Value(); !strings.HasSuffix(v, " ") { - t.Fatalf("expected trailing space after attachment, got value: %q", v) - } -} diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 1899df351..6a56e2012 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -382,9 +382,6 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { a.app.Messages = []app.Message{} case dialog.CompletionDialogCloseMsg: a.showCompletionDialog = false - case chat.AttachmentInsertedMsg: - // Close completion dialog when the editor inserts an attachment - a.showCompletionDialog = false case opencode.EventListResponseEventInstallationUpdated: return a, toast.NewSuccessToast( "opencode updated to "+msg.Properties.Version+", restart to apply.", From 7e8b5749fa9b6b58d7115beffa98456f1ff9f074 Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 11 Aug 2025 23:43:43 +0000 Subject: [PATCH 101/178] release: v0.4.20 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 98e859053..9f8a44918 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.19", + "version": "0.4.20", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 9f2514adb..70705d875 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.19", + "version": "0.4.20", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 55ad09b38..188932416 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.19", + "version": "0.4.20", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index c699f0683..2073ed9de 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.19", + "version": "0.4.20", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 7fa3aa2f1..403ece8e5 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.19", + "version": "0.4.20", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 2448f1779..971896721 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.19", + "version": "0.4.20", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 9e73fa1f8..f1d1d1228 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.19", + "version": "0.4.20", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index d3c8baaab..046c7084b 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.19", + "version": "0.4.20", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index bd2eb6c71..19da5895c 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.19", + "version": "0.4.20", "publisher": "sst-dev", "repository": { "type": "git", From f54e9007160d3f4cd1d459447e1313e510f3df4a Mon Sep 17 00:00:00 2001 From: Carl Brugger Date: Mon, 11 Aug 2025 18:40:56 -0500 Subject: [PATCH 102/178] Fix plugin file name (#1837) --- packages/web/src/content/docs/docs/plugins.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/content/docs/docs/plugins.mdx b/packages/web/src/content/docs/docs/plugins.mdx index 910ee4cdc..08c697a62 100644 --- a/packages/web/src/content/docs/docs/plugins.mdx +++ b/packages/web/src/content/docs/docs/plugins.mdx @@ -90,7 +90,7 @@ We are using `osascript` to run AppleScript on macOS. Here we are using it to se Prevent opencode from reading `.env` files: -```javascript title=".opencode/plugin/slack.js" +```javascript title=".opencode/plugin/env-protection.js" export const EnvProtection = async ({ client, $ }) => { return { tool: { From dec1e3fddabe253a2aa22f4bcfcee8ddfbcb9766 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:58:32 -0500 Subject: [PATCH 103/178] fix: complete item on space --- .../tui/internal/components/dialog/complete.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/tui/internal/components/dialog/complete.go b/packages/tui/internal/components/dialog/complete.go index d3d2ea9d7..176d6e11c 100644 --- a/packages/tui/internal/components/dialog/complete.go +++ b/packages/tui/internal/components/dialog/complete.go @@ -99,7 +99,10 @@ func (c *completionDialogComponent) getAllCompletions(query string) tea.Cmd { baseStyle := styles.NewStyle().Background(t.BackgroundElement()) // Ensure stable provider order just in case - sort.SliceStable(itemsByProvider, func(i, j int) bool { return itemsByProvider[i].idx < itemsByProvider[j].idx }) + sort.SliceStable( + itemsByProvider, + func(i, j int) bool { return itemsByProvider[i].idx < itemsByProvider[j].idx }, + ) final := make([]completions.CompletionSuggestion, 0) for _, entry := range itemsByProvider { @@ -167,6 +170,16 @@ func (c *completionDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { value := c.pseudoSearchTextArea.Value() width := lipgloss.Width(value) triggerWidth := lipgloss.Width(c.trigger) + + if msg.String() == "space" || msg.String() == " " { + item, i := c.list.GetSelectedItem() + if i > -1 { + return c, c.complete(item) + } + // If no exact match, close the dialog + return c, c.close() + } + // Only close on backspace when there are no characters left, unless we're back to just the trigger if (msg.String() != "backspace" && msg.String() != "ctrl+h") || (width <= triggerWidth && value != c.trigger) { return c, c.close() From ee0519aacc22db40f99365c6759d2f1715931a5c Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Mon, 11 Aug 2025 19:21:59 -0500 Subject: [PATCH 104/178] feat: add clangd for cpp --- packages/opencode/src/lsp/server.ts | 77 +++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index cf78fe7bf..3da5a7e28 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -421,4 +421,81 @@ export namespace LSPServer { } }, } + + export const Clangd: Info = { + id: "clangd", + root: NearestRoot(["compile_commands.json", "compile_flags.txt", ".clangd", "CMakeLists.txt", "Makefile"]), + extensions: [".c", ".cpp", ".cc", ".cxx", ".c++", ".h", ".hpp", ".hh", ".hxx", ".h++"], + async spawn(_, root) { + let bin = Bun.which("clangd", { + PATH: process.env["PATH"] + ":" + Global.Path.bin, + }) + if (!bin) { + log.info("downloading clangd from GitHub releases") + + const releaseResponse = await fetch("https://api.github.com/repos/clangd/clangd/releases/latest") + if (!releaseResponse.ok) { + log.error("Failed to fetch clangd release info") + return + } + + const release = await releaseResponse.json() + + const platform = process.platform + let assetName = "" + + if (platform === "darwin") { + assetName = "clangd-mac-" + } else if (platform === "linux") { + assetName = "clangd-linux-" + } else if (platform === "win32") { + assetName = "clangd-windows-" + } else { + log.error(`Platform ${platform} is not supported by clangd auto-download`) + return + } + + assetName += release.tag_name + ".zip" + + const asset = release.assets.find((a: any) => a.name === assetName) + if (!asset) { + log.error(`Could not find asset ${assetName} in latest clangd release`) + return + } + + const downloadUrl = asset.browser_download_url + const downloadResponse = await fetch(downloadUrl) + if (!downloadResponse.ok) { + log.error("Failed to download clangd") + return + } + + const zipPath = path.join(Global.Path.bin, "clangd.zip") + await Bun.file(zipPath).write(downloadResponse) + + await $`unzip -o -q ${zipPath}`.cwd(Global.Path.bin).nothrow() + await fs.rm(zipPath, { force: true }) + + const extractedDir = path.join(Global.Path.bin, assetName.replace(".zip", "")) + bin = path.join(extractedDir, "bin", "clangd" + (platform === "win32" ? ".exe" : "")) + + if (!(await Bun.file(bin).exists())) { + log.error("Failed to extract clangd binary") + return + } + + if (platform !== "win32") { + await $`chmod +x ${bin}`.nothrow() + } + + log.info(`installed clangd`, { bin }) + } + + return { + process: spawn(bin, ["--background-index", "--clang-tidy"], { + cwd: root, + }), + } + }, + } } From 3946a08f408e235b3b80aad6cff5a6d2a35effa5 Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 00:30:49 +0000 Subject: [PATCH 105/178] release: v0.4.21 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 9f8a44918..e0b836f30 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.20", + "version": "0.4.21", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 70705d875..571e3b263 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.20", + "version": "0.4.21", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 188932416..01993e167 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.20", + "version": "0.4.21", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 2073ed9de..7f77692f6 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.20", + "version": "0.4.21", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 403ece8e5..c0af899ba 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.20", + "version": "0.4.21", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 971896721..436772755 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.20", + "version": "0.4.21", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index f1d1d1228..7b6e92326 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.20", + "version": "0.4.21", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index 046c7084b..25cb8ef57 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.20", + "version": "0.4.21", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 19da5895c..4c18334d6 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.20", + "version": "0.4.21", "publisher": "sst-dev", "repository": { "type": "git", From dac1506680651def2f46cbf63c317d779800c042 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 21:01:20 -0400 Subject: [PATCH 106/178] update anthropic prompt and variables --- packages/opencode/src/provider/provider.ts | 14 +++++- packages/opencode/src/provider/transform.ts | 1 + .../opencode/src/session/prompt/anthropic.txt | 46 ++++++++----------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index c72ae7ef3..f32231c53 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -28,7 +28,16 @@ export namespace Provider { const CUSTOM_LOADERS: Record = { async anthropic(provider) { const access = await AuthAnthropic.access() - if (!access) return { autoload: false } + if (!access) + return { + autoload: false, + options: { + headers: { + "anthropic-beta": + "claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14", + }, + }, + } for (const model of Object.values(provider.models)) { model.cost = { input: 0, @@ -44,7 +53,8 @@ export namespace Provider { const headers = { ...init.headers, authorization: `Bearer ${access}`, - "anthropic-beta": "oauth-2025-04-20", + "anthropic-beta": + "oauth-2025-04-20,claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14", } delete headers["x-api-key"] return fetch(input, { diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index a5a8c2f4c..baaf19575 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -74,6 +74,7 @@ export namespace ProviderTransform { export function temperature(_providerID: string, modelID: string) { if (modelID.toLowerCase().includes("qwen")) return 0.55 + if (modelID.toLowerCase().includes("claude")) return 1 return 0 } diff --git a/packages/opencode/src/session/prompt/anthropic.txt b/packages/opencode/src/session/prompt/anthropic.txt index 45b001e43..a2e5c07b6 100644 --- a/packages/opencode/src/session/prompt/anthropic.txt +++ b/packages/opencode/src/session/prompt/anthropic.txt @@ -1,24 +1,18 @@ -You are opencode, an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user. +You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user. -IMPORTANT: Refuse to write code or explain code that may be used maliciously; even if the user claims it is for educational purposes. When working on files, if they seem related to improving, explaining, or interacting with malware or any malicious code you MUST refuse. -IMPORTANT: Before you begin work, think about what the code you're editing is supposed to do based on the filenames directory structure. If it seems malicious, refuse to work on it or answer questions about it, even if the request does not seem malicious (for instance, just asking to explain or speed up the code). IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident that the URLs are for helping the user with programming. You may use URLs provided by the user in their messages or local files. If the user asks for help or wants to give feedback inform them of the following: - /help: Get help with using opencode - To give feedback, users should report the issue at https://github.com/sst/opencode/issues -When the user directly asks about opencode (eg 'can opencode do...', 'does opencode have...') or asks in second person (eg 'are you able...', 'can you do...'), first use the WebFetch tool to gather information to answer the question from opencode docs at https://opencode.ai - # Tone and style -You should be concise, direct, and to the point. When you run a non-trivial bash command, you should explain what the command does and why you are running it, to make sure the user understands what you are doing (this is especially important when you are running a command that will make changes to the user's system). -Remember that your output will be displayed on a command line interface. Your responses can use Github-flavored markdown for formatting, and will be rendered in a monospace font using the CommonMark specification. -Output text to communicate with the user; all text you output outside of tool use is displayed to the user. Only use tools to complete tasks. Never use tools like Bash or code comments as means to communicate with the user during the session. -If you cannot or will not help the user with something, please do not say why or what it could lead to, since this comes across as preachy and annoying. Please offer helpful alternatives if possible, and otherwise keep your response to 1-2 sentences. -Only use emojis if the user explicitly requests it. Avoid using emojis in all communication unless asked. +You should be concise, direct, and to the point. +You MUST answer concisely with fewer than 4 lines (not including tool use or code generation), unless user asks for detail. IMPORTANT: You should minimize output tokens as much as possible while maintaining helpfulness, quality, and accuracy. Only address the specific query or task at hand, avoiding tangential information unless absolutely critical for completing the request. If you can answer in 1-3 sentences or a short paragraph, please do. IMPORTANT: You should NOT answer with unnecessary preamble or postamble (such as explaining your code or summarizing your action), unless the user asks you to. -IMPORTANT: Keep your responses short, since they will be displayed on a command line interface. You MUST answer concisely with fewer than 4 lines (not including tool use or code generation), unless user asks for detail. Answer the user's question directly, without elaboration, explanation, or details. One word answers are best. Avoid introductions, conclusions, and explanations. You MUST avoid text before/after your response, such as "The answer is .", "Here is the content of the file..." or "Based on the information provided, the answer is..." or "Here is what I will do next...". Here are some examples to demonstrate appropriate verbosity: +Do not add additional code explanation summary unless requested by the user. After working on a file, just stop, rather than providing an explanation of what you did. +Answer the user's question directly, without elaboration, explanation, or details. One word answers are best. Avoid introductions, conclusions, and explanations. You MUST avoid text before/after your response, such as "The answer is .", "Here is the content of the file..." or "Based on the information provided, the answer is..." or "Here is what I will do next...". Here are some examples to demonstrate appropriate verbosity: user: 2 + 2 assistant: 4 @@ -56,18 +50,18 @@ assistant: [runs ls and sees foo.c, bar.c, baz.c] user: which file contains the implementation of foo? assistant: src/foo.c - - -user: write tests for new feature -assistant: [uses grep and glob search tools to find where similar tests are defined, uses concurrent read file tool use blocks in one tool call to read relevant files at the same time, uses edit file tool to write new tests] - +When you run a non-trivial bash command, you should explain what the command does and why you are running it, to make sure the user understands what you are doing (this is especially important when you are running a command that will make changes to the user's system). +Remember that your output will be displayed on a command line interface. Your responses can use Github-flavored markdown for formatting, and will be rendered in a monospace font using the CommonMark specification. +Output text to communicate with the user; all text you output outside of tool use is displayed to the user. Only use tools to complete tasks. Never use tools like Bash or code comments as means to communicate with the user during the session. +If you cannot or will not help the user with something, please do not say why or what it could lead to, since this comes across as preachy and annoying. Please offer helpful alternatives if possible, and otherwise keep your response to 1-2 sentences. +Only use emojis if the user explicitly requests it. Avoid using emojis in all communication unless asked. +IMPORTANT: Keep your responses short, since they will be displayed on a command line interface. # Proactiveness You are allowed to be proactive, but only when the user asks you to do something. You should strive to strike a balance between: -1. Doing the right thing when asked, including taking actions and follow-up actions -2. Not surprising the user with actions you take without asking +- Doing the right thing when asked, including taking actions and follow-up actions +- Not surprising the user with actions you take without asking For example, if the user asks you how to approach something, you should do your best to answer their question first, and not immediately jump into taking actions. -3. Do not add additional code explanation summary unless requested by the user. After working on a file, just stop, rather than providing an explanation of what you did. # Following conventions When making changes to files, first understand the file's code conventions. Mimic code style, use existing libraries and utilities, and follow existing patterns. @@ -81,7 +75,7 @@ When making changes to files, first understand the file's code conventions. Mimi # Task Management -You have access to the TodoWrite and TodoRead tools to help you manage and plan tasks. Use these tools VERY frequently to ensure that you are tracking your tasks and giving the user visibility into your progress. +You have access to the TodoWrite tools to help you manage and plan tasks. Use these tools VERY frequently to ensure that you are tracking your tasks and giving the user visibility into your progress. These tools are also EXTREMELY helpful for planning tasks, and for breaking down larger complex tasks into smaller steps. If you do not use this tool when planning, you may forget to do important tasks - and that is unacceptable. It is critical that you mark todos as completed as soon as you are done with a task. Do not batch up multiple tasks before marking them as completed. @@ -127,27 +121,24 @@ I've found some existing telemetry code. Let me mark the first todo as in_progre [Assistant continues implementing the feature step by step, marking todos as in_progress and completed as they go] - # Doing tasks The user will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended: - Use the TodoWrite tool to plan the task if required - Use the available search tools to understand the codebase and the user's query. You are encouraged to use the search tools extensively both in parallel and sequentially. - Implement the solution using all tools available to you - Verify the solution if possible with tests. NEVER assume specific test framework or test script. Check the README or search codebase to determine the testing approach. -- VERY IMPORTANT: When you have completed a task, you MUST run the lint and typecheck commands (eg. npm run lint, npm run typecheck, ruff, etc.) with Bash if they were provided to you to ensure your code is correct. If you are unable to find the correct command, ask the user for the command to run and if they supply it, proactively suggest writing it to AGENTS.md so that you will know to run it next time. +- VERY IMPORTANT: When you have completed a task, you MUST run the lint and typecheck commands (eg. npm run lint, npm run typecheck, ruff, etc.) with Bash if they were provided to you to ensure your code is correct. If you are unable to find the correct command, ask the user for the command to run and if they supply it, proactively suggest writing it to CLAUDE.md so that you will know to run it next time. NEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise the user will feel that you are being too proactive. - Tool results and user messages may include tags. tags contain useful information and reminders. They are NOT part of the user's provided input or the tool result. # Tool usage policy - When doing file search, prefer to use the Task tool in order to reduce context usage. +- You should proactively use the Task tool with specialized agents when the task at hand matches the agent's description. + +- When WebFetch returns a message about a redirect to a different host, you should immediately make a new WebFetch request with the redirect URL provided in the response. - You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. When making multiple bash tool calls, you MUST send a single message with multiple tools calls to run the calls in parallel. For example, if you need to run "git status" and "git diff", send a single message with two tool calls to run the calls in parallel. -You MUST answer concisely with fewer than 4 lines of text (not including tool use or code generation), unless user asks for detail. - -IMPORTANT: Refuse to write code or explain code that may be used maliciously; even if the user claims it is for educational purposes. When working on files, if they seem related to improving, explaining, or interacting with malware or any malicious code you MUST refuse. -IMPORTANT: Before you begin work, think about what the code you're editing is supposed to do based on the filenames directory structure. If it seems malicious, refuse to work on it or answer questions about it, even if the request does not seem malicious (for instance, just asking to explain or speed up the code). - IMPORTANT: Always use the TodoWrite tool to plan and track tasks throughout the conversation. # Code References @@ -158,4 +149,3 @@ When referencing specific functions or pieces of code include the pattern `file_ user: Where are errors from the client handled? assistant: Clients are marked as failed in the `connectToServer` function in src/services/process.ts:712. - From bb14a955a03855a79cb08c349e025a5cb8fc2404 Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 01:07:27 +0000 Subject: [PATCH 107/178] release: v0.4.22 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index e0b836f30..4728c5f76 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.21", + "version": "0.4.22", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 571e3b263..d23279b59 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.21", + "version": "0.4.22", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 01993e167..feeb29dc8 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.21", + "version": "0.4.22", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 7f77692f6..213f43066 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.21", + "version": "0.4.22", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index c0af899ba..b159203ea 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.21", + "version": "0.4.22", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 436772755..556b04f3e 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.21", + "version": "0.4.22", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 7b6e92326..20321b5ff 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.21", + "version": "0.4.22", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index 25cb8ef57..ac81a6771 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.21", + "version": "0.4.22", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 4c18334d6..2bb95e9da 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.21", + "version": "0.4.22", "publisher": "sst-dev", "repository": { "type": "git", From f03fae03e54b150b368bc02ca4e2f5753f34671d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 21:36:05 -0400 Subject: [PATCH 108/178] switch back to didUpdate instead of closing and opening file --- packages/opencode/src/cli/cmd/debug/lsp.ts | 4 +++ packages/opencode/src/lsp/client.ts | 29 ++++++++++++++-------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/opencode/src/cli/cmd/debug/lsp.ts b/packages/opencode/src/cli/cmd/debug/lsp.ts index ac1bac7c1..91b13df43 100644 --- a/packages/opencode/src/cli/cmd/debug/lsp.ts +++ b/packages/opencode/src/cli/cmd/debug/lsp.ts @@ -2,6 +2,7 @@ import { LSP } from "../../../lsp" import { bootstrap } from "../../bootstrap" import { cmd } from "../cmd" import { Log } from "../../../util/log" +import { appendFile } from "fs/promises" export const LSPCommand = cmd({ command: "lsp", @@ -17,6 +18,9 @@ const DiagnosticsCommand = cmd({ await bootstrap({ cwd: process.cwd() }, async () => { await LSP.touchFile(args.file, true) console.log(await LSP.diagnostics()) + await appendFile(args.file, `\nconst x: number = "foo"`) + await LSP.touchFile(args.file, true) + console.log(await LSP.diagnostics()) }) }, }) diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts index c63e02592..509e982eb 100644 --- a/packages/opencode/src/lsp/client.ts +++ b/packages/opencode/src/lsp/client.ts @@ -126,19 +126,26 @@ export namespace LSPClient { input.path = path.isAbsolute(input.path) ? input.path : path.resolve(app.path.cwd, input.path) const file = Bun.file(input.path) const text = await file.text() - const version = files[input.path] - if (version !== undefined) { - diagnostics.delete(input.path) - await connection.sendNotification("textDocument/didClose", { - textDocument: { - uri: `file://` + input.path, - }, - }) - } - log.info("textDocument/didOpen", input) - diagnostics.delete(input.path) const extension = path.extname(input.path) const languageId = LANGUAGE_EXTENSIONS[extension] ?? "plaintext" + + const version = files[input.path] + if (version !== undefined) { + const next = version + 1 + files[input.path] = next + log.info("textDocument/didChange", { path: input.path, version: next }) + await connection.sendNotification("textDocument/didChange", { + textDocument: { + uri: `file://` + input.path, + version: next, + }, + contentChanges: [{ text }], + }) + return + } + + log.info("textDocument/didOpen", input) + diagnostics.delete(input.path) await connection.sendNotification("textDocument/didOpen", { textDocument: { uri: `file://` + input.path, From 0ca10ec2f5b2fd69eedaf58f7971438f02452142 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 21:52:05 -0400 Subject: [PATCH 109/178] ignore: log --- packages/opencode/src/snapshot/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index 0c8cc5e82..163fec37c 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -43,6 +43,7 @@ export namespace Snapshot { } await $`git --git-dir ${git} add .`.quiet().cwd(app.path.cwd).nothrow() const hash = await $`git --git-dir ${git} write-tree`.quiet().cwd(app.path.cwd).nothrow().text() + log.info("tracking", { hash }) return hash.trim() } From 7d095d19f6c52220987f0c674db6e0d26a77d174 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 21:59:12 -0400 Subject: [PATCH 110/178] fix undo/redo when opencode is run in nested folders --- packages/opencode/src/snapshot/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index 163fec37c..d69fb69d9 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -43,7 +43,7 @@ export namespace Snapshot { } await $`git --git-dir ${git} add .`.quiet().cwd(app.path.cwd).nothrow() const hash = await $`git --git-dir ${git} write-tree`.quiet().cwd(app.path.cwd).nothrow().text() - log.info("tracking", { hash }) + log.info("tracking", { hash, cwd: app.path.cwd, git }) return hash.trim() } @@ -65,7 +65,7 @@ export namespace Snapshot { .split("\n") .map((x) => x.trim()) .filter(Boolean) - .map((x) => path.join(app.path.cwd, x)), + .map((x) => path.join(app.path.root, x)), } } From 9437cf4ff66c540533f200ae6ce35780c1b0fc03 Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 02:04:53 +0000 Subject: [PATCH 111/178] release: v0.4.23 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 4728c5f76..78ac9c38d 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.22", + "version": "0.4.23", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index d23279b59..a5564a41c 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.22", + "version": "0.4.23", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index feeb29dc8..e27092d2b 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.22", + "version": "0.4.23", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 213f43066..4b6aacd52 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.22", + "version": "0.4.23", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index b159203ea..9a809df86 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.22", + "version": "0.4.23", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 556b04f3e..cb1161187 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.22", + "version": "0.4.23", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 20321b5ff..7d8122aac 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.22", + "version": "0.4.23", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index ac81a6771..cfe2e198c 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.22", + "version": "0.4.23", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 2bb95e9da..d5bfb442b 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.22", + "version": "0.4.23", "publisher": "sst-dev", "repository": { "type": "git", From e6f3cf0839e401a8efa12614910852f9581b227b Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 22:26:18 -0400 Subject: [PATCH 112/178] fix pyright --- .gitignore | 1 + packages/opencode/src/cli/cmd/debug/lsp.ts | 4 ---- packages/opencode/src/lsp/server.ts | 19 ++++++++++++++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 2728097b9..33e3deb81 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ node_modules .vscode openapi.json playground +tmp diff --git a/packages/opencode/src/cli/cmd/debug/lsp.ts b/packages/opencode/src/cli/cmd/debug/lsp.ts index 91b13df43..ac1bac7c1 100644 --- a/packages/opencode/src/cli/cmd/debug/lsp.ts +++ b/packages/opencode/src/cli/cmd/debug/lsp.ts @@ -2,7 +2,6 @@ import { LSP } from "../../../lsp" import { bootstrap } from "../../bootstrap" import { cmd } from "../cmd" import { Log } from "../../../util/log" -import { appendFile } from "fs/promises" export const LSPCommand = cmd({ command: "lsp", @@ -18,9 +17,6 @@ const DiagnosticsCommand = cmd({ await bootstrap({ cwd: process.cwd() }, async () => { await LSP.touchFile(args.file, true) console.log(await LSP.diagnostics()) - await appendFile(args.file, `\nconst x: number = "foo"`) - await LSP.touchFile(args.file, true) - console.log(await LSP.diagnostics()) }) }, }) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index 3da5a7e28..6cfba6d9d 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -210,7 +210,24 @@ export namespace LSPServer { extensions: [".py", ".pyi"], root: NearestRoot(["pyproject.toml", "setup.py", "setup.cfg", "requirements.txt", "Pipfile", "pyrightconfig.json"]), async spawn(_, root) { - const proc = spawn(BunProc.which(), ["x", "pyright-langserver", "--stdio"], { + let binary = Bun.which("pyright-langserver") + const args = [] + if (!binary) { + const js = path.join(Global.Path.bin, "node_modules", "pyright", "dist", "pyright-langserver.js") + if (!(await Bun.file(js).exists())) { + await Bun.spawn([BunProc.which(), "install", "pyright"], { + cwd: Global.Path.bin, + env: { + ...process.env, + BUN_BE_BUN: "1", + }, + }).exited + } + binary = BunProc.which() + args.push(...["run", js]) + } + args.push("--stdio") + const proc = spawn(binary, args, { cwd: root, env: { ...process.env, From 3ec670784daaa7ef2c3269790f44c739b09ac3b2 Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 02:33:35 +0000 Subject: [PATCH 113/178] release: v0.4.24 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 78ac9c38d..3ebaeac59 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.23", + "version": "0.4.24", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index a5564a41c..035964d92 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.23", + "version": "0.4.24", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index e27092d2b..b9dbc4f03 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.23", + "version": "0.4.24", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 4b6aacd52..f54015f71 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.23", + "version": "0.4.24", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 9a809df86..b96c4f5a8 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.23", + "version": "0.4.24", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index cb1161187..43d85b481 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.23", + "version": "0.4.24", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 7d8122aac..847794965 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.23", + "version": "0.4.24", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index cfe2e198c..884665917 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.23", + "version": "0.4.24", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index d5bfb442b..133b357b8 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.23", + "version": "0.4.24", "publisher": "sst-dev", "repository": { "type": "git", From fb0a200ecfa421d2bee9d2c053985f32d8b6b03d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 22:42:17 -0400 Subject: [PATCH 114/178] refactor: replace OPENCODE_AGENTS env var with HTTP API call Replace environment variable passing of agent data from Node.js to TUI with proper HTTP API call to /agent endpoint. This improves architecture by eliminating env var dependencies and allows dynamic agent data fetching. --- packages/opencode/src/cli/cmd/tui.ts | 3 +-- packages/tui/cmd/opencode/main.go | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui.ts b/packages/opencode/src/cli/cmd/tui.ts index 3d8028af7..30e861db6 100644 --- a/packages/opencode/src/cli/cmd/tui.ts +++ b/packages/opencode/src/cli/cmd/tui.ts @@ -12,7 +12,7 @@ import { Bus } from "../../bus" import { Log } from "../../util/log" import { FileWatcher } from "../../file/watch" import { Ide } from "../../ide" -import { Agent } from "../../agent/agent" + import { Flag } from "../../flag/flag" import { Session } from "../../session" @@ -141,7 +141,6 @@ export const TuiCommand = cmd({ CGO_ENABLED: "0", OPENCODE_SERVER: server.url.toString(), OPENCODE_APP_INFO: JSON.stringify(app), - OPENCODE_AGENTS: JSON.stringify(await Agent.list()), }, onExit: () => { server.stop() diff --git a/packages/tui/cmd/opencode/main.go b/packages/tui/cmd/opencode/main.go index c242a737a..f1473e8ba 100644 --- a/packages/tui/cmd/opencode/main.go +++ b/packages/tui/cmd/opencode/main.go @@ -45,14 +45,6 @@ func main() { os.Exit(1) } - agentsStr := os.Getenv("OPENCODE_AGENTS") - var agents []opencode.Agent - err = json.Unmarshal([]byte(agentsStr), &agents) - if err != nil { - slog.Error("Failed to unmarshal modes", "error", err) - os.Exit(1) - } - stat, err := os.Stdin.Stat() if err != nil { slog.Error("Failed to stat stdin", "error", err) @@ -81,13 +73,25 @@ func main() { option.WithBaseURL(url), ) + // Fetch agents from the /agent endpoint + agentsPtr, err := httpClient.App.Agents(context.Background()) + if err != nil { + slog.Error("Failed to fetch agents", "error", err) + os.Exit(1) + } + if agentsPtr == nil { + slog.Error("No agents returned from server") + os.Exit(1) + } + agents := *agentsPtr + ctx, cancel := context.WithCancel(context.Background()) defer cancel() apiHandler := util.NewAPILogHandler(ctx, httpClient, "tui", slog.LevelDebug) logger := slog.New(apiHandler) slog.SetDefault(logger) - slog.Debug("TUI launched", "app", appInfoStr, "modes", agentsStr, "url", url) + slog.Debug("TUI launched", "app", appInfoStr, "agents_count", len(agents), "url", url) go func() { err = clipboard.Init() From 603e81ef5a54cc50a58ec6c161d702f52ca240d7 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 22:45:40 -0400 Subject: [PATCH 115/178] Simplify git-committer agent definition for better maintainability --- .opencode/agent/git-committer.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .opencode/agent/git-committer.md diff --git a/.opencode/agent/git-committer.md b/.opencode/agent/git-committer.md new file mode 100644 index 000000000..20f090147 --- /dev/null +++ b/.opencode/agent/git-committer.md @@ -0,0 +1,10 @@ +--- +description: Use this agent when you are saked to commit and push code changes to a git repository. +mode: subagent +--- + +You commit and push to git + +Commit messages should be brief since they are used to generate release notes. + +Messages should say WHY the change was made and not WHAT was changed. From 681abcbf2d838d69f628abe1799c84ee7e6310fc Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 02:51:32 +0000 Subject: [PATCH 116/178] release: v0.4.25 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 3ebaeac59..19007e357 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.24", + "version": "0.4.25", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 035964d92..f4757abf9 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.24", + "version": "0.4.25", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index b9dbc4f03..3ab542925 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.24", + "version": "0.4.25", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index f54015f71..fda56be4c 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.24", + "version": "0.4.25", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index b96c4f5a8..051c21b26 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.24", + "version": "0.4.25", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 43d85b481..71892034e 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.24", + "version": "0.4.25", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 847794965..816c5a14d 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.24", + "version": "0.4.25", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index 884665917..2ff885105 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.24", + "version": "0.4.25", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 133b357b8..75c95f007 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.24", + "version": "0.4.25", "publisher": "sst-dev", "repository": { "type": "git", From 5f34dcc792c96286bc81b5bdb11da1b735dc56d1 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 23:04:19 -0400 Subject: [PATCH 117/178] azure reasoning effort --- packages/opencode/src/provider/transform.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index baaf19575..c0591a69f 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -87,8 +87,7 @@ export namespace ProviderTransform { if (modelID.includes("gpt-5")) { if (providerID === "azure") { return { - reasoning_effort: "minimal", - text_verbosity: "verbose", + reasoningEffort: "minimal", } } return { From 2b6afe90d0068b18662e267ea34983a5e0b7ba27 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 23:20:19 -0400 Subject: [PATCH 118/178] fix azure reasoningEffort --- packages/opencode/src/global/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/global/index.ts b/packages/opencode/src/global/index.ts index 4c3bc05be..c69ea0d78 100644 --- a/packages/opencode/src/global/index.ts +++ b/packages/opencode/src/global/index.ts @@ -27,7 +27,7 @@ await Promise.all([ fs.mkdir(Global.Path.log, { recursive: true }), ]) -const CACHE_VERSION = "6" +const CACHE_VERSION = "7" const version = await Bun.file(path.join(Global.Path.cache, "version")) .text() From 5fbbdcaf643cfa47e297a33d6b7b1ff90c1f6ae0 Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 03:25:36 +0000 Subject: [PATCH 119/178] release: v0.4.26 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 19007e357..f7a2eb6fe 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.25", + "version": "0.4.26", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index f4757abf9..0d68ce32e 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.25", + "version": "0.4.26", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 3ab542925..4f33e676b 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.25", + "version": "0.4.26", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index fda56be4c..6cfae4db5 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.25", + "version": "0.4.26", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 051c21b26..d77a00986 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.25", + "version": "0.4.26", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 71892034e..72977db16 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.25", + "version": "0.4.26", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 816c5a14d..2bc27a0b7 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.25", + "version": "0.4.26", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index 2ff885105..1decf7b96 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.25", + "version": "0.4.26", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 75c95f007..474431b9a 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.25", + "version": "0.4.26", "publisher": "sst-dev", "repository": { "type": "git", From 1ec71e419b4ed24af77d8ffe197390927dc074e9 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 23:36:31 -0400 Subject: [PATCH 120/178] support wildcard matching tool names in config --- opencode.json | 3 +++ packages/opencode/src/session/index.ts | 5 +++-- packages/opencode/src/util/wildcard.ts | 14 ++++++++++++++ packages/web/src/content/docs/docs/agents.mdx | 18 +++++++++++++++++- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/opencode.json b/opencode.json index 59f14ac75..003253ee7 100644 --- a/opencode.json +++ b/opencode.json @@ -1,5 +1,8 @@ { "$schema": "https://opencode.ai/config.json", + "agent": { + "build": {} + }, "mcp": { "context7": { "type": "remote", diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 5f7bf000d..6d2b94863 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -42,6 +42,7 @@ import { ToolRegistry } from "../tool/registry" import { Plugin } from "../plugin" import { Agent } from "../agent/agent" import { Permission } from "../permission" +import { Wildcard } from "../util/wildcard" export namespace Session { const log = Log.create({ service: "session" }) @@ -768,7 +769,7 @@ export namespace Session { mergeDeep(input.tools ?? {}), ) for (const item of await ToolRegistry.tools(input.providerID, input.modelID)) { - if (enabledTools[item.id] === false) continue + if (Wildcard.all(item.id, enabledTools) === false) continue tools[item.id] = tool({ id: item.id as any, description: item.description, @@ -829,7 +830,7 @@ export namespace Session { } for (const [key, item] of Object.entries(await MCP.tools())) { - if (enabledTools[key] === false) continue + if (Wildcard.all(key, enabledTools) === false) continue const execute = item.execute if (!execute) continue item.execute = async (args, opts) => { diff --git a/packages/opencode/src/util/wildcard.ts b/packages/opencode/src/util/wildcard.ts index 43fc417da..d329501f2 100644 --- a/packages/opencode/src/util/wildcard.ts +++ b/packages/opencode/src/util/wildcard.ts @@ -1,3 +1,5 @@ +import { sortBy, pipe } from "remeda" + export namespace Wildcard { export function match(str: string, pattern: string) { const regex = new RegExp( @@ -11,4 +13,16 @@ export namespace Wildcard { ) return regex.test(str) } + + export function all(input: string, patterns: Record) { + const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"])) + let result = undefined + for (const [pattern, value] of sorted) { + if (match(input, pattern)) { + result = value + continue + } + } + return result + } } diff --git a/packages/web/src/content/docs/docs/agents.mdx b/packages/web/src/content/docs/docs/agents.mdx index 80e1867d7..bcb0eca2a 100644 --- a/packages/web/src/content/docs/docs/agents.mdx +++ b/packages/web/src/content/docs/docs/agents.mdx @@ -318,6 +318,22 @@ Control which tools are available in this agent with the `tools` config. You can } ``` +You can also use wildcards to control multiple tools at once. For example, to disable all tools from an MCP server: + +```json title="opencode.json" +{ + "agent": { + "readonly": { + "tools": { + "mymcp_*": false, + "write": false, + "edit": false + } + } + } +} +``` + If no tools are specified, all tools are enabled by default. --- @@ -371,7 +387,7 @@ For example, with OpenAI's reasoning models, you can control the reasoning effor "agent": { "deep-thinker": { "description": "Agent that uses high reasoning effort for complex problems", - "model": "openai/gpt-5-turbo", + "model": "openai/gpt-5-turbo", "reasoningEffort": "high", "textVerbosity": "low" } From 02cfdfbf5b59ed5bc150b5184ed7059f0264470b Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 23:51:08 -0400 Subject: [PATCH 121/178] ci: add duplicate issue detection workflow --- .github/workflows/duplicate-issues.yml | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/duplicate-issues.yml diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml new file mode 100644 index 000000000..8210a4cab --- /dev/null +++ b/.github/workflows/duplicate-issues.yml @@ -0,0 +1,50 @@ +name: Duplicate Issue Detection + +on: + issues: + types: [opened] + +jobs: + check-duplicates: + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Install opencode + run: curl -fsSL https://opencode.ai/install | bash + + - name: Check for duplicate issues + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + opencode run "A new issue has been created: '${{ github.event.issue.title }}' + + Issue body: + ${{ github.event.issue.body }} + + Please search through existing issues in this repository to find any potential duplicates of this new issue. Consider: + 1. Similar titles or descriptions + 2. Same error messages or symptoms + 3. Related functionality or components + 4. Similar feature requests + + If you find any potential duplicates, please comment on the new issue with: + - A brief explanation of why it might be a duplicate + - Links to the potentially duplicate issues + - A suggestion to check those issues first + + Use this format for the comment: + '👋 This issue might be a duplicate of existing issues. Please check: + - #[issue_number]: [brief description of similarity] + + If none of these address your specific case, please let us know how this issue differs.' + + If no clear duplicates are found, do not comment." From 39106718ed699fbe63692b93c4faa17d2d900dfe Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Mon, 11 Aug 2025 23:52:16 -0400 Subject: [PATCH 122/178] ci: tweak --- .github/workflows/duplicate-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml index 8210a4cab..e00f2087a 100644 --- a/.github/workflows/duplicate-issues.yml +++ b/.github/workflows/duplicate-issues.yml @@ -25,7 +25,7 @@ jobs: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - opencode run "A new issue has been created: '${{ github.event.issue.title }}' + opencode run -m anthropic/claude-sonnet-4-20250514 "A new issue has been created: '${{ github.event.issue.title }}' Issue body: ${{ github.event.issue.body }} From 275bc4d2c8a17fc7ead15046b12e45b3999fb417 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 00:01:59 -0400 Subject: [PATCH 123/178] ci: add guidelines check workflow for PRs --- .github/workflows/guidelines-check.yml | 41 ++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/guidelines-check.yml diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml new file mode 100644 index 000000000..3582be7c3 --- /dev/null +++ b/.github/workflows/guidelines-check.yml @@ -0,0 +1,41 @@ +name: Guidelines Check + +on: + pull_request: + types: [opened, synchronize] + +jobs: + check-guidelines: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Install opencode + run: curl -fsSL https://opencode.ai/install | bash + + - name: Check PR guidelines compliance + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + opencode run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' + + PR description: + ${{ github.event.pull_request.body }} + + Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. + + For each violation you find, create a file comment using the gh CLI. Use this exact format for each violation: + + \`\`\`bash + gh pr review ${{ github.event.pull_request.number }} --comment-body 'This violates the AGENTS.md guideline: [specific rule]. Consider: [suggestion]' --file 'path/to/file.ts' --line [line_number] + \`\`\` + + Only create comments for actual violations. If the code follows all guidelines, don't run any gh commands." From a3064e2c32808813023ecbadc8ff8ab642f5a55a Mon Sep 17 00:00:00 2001 From: Dax Date: Tue, 12 Aug 2025 00:34:16 -0400 Subject: [PATCH 124/178] Update duplicate-issues.yml --- .github/workflows/duplicate-issues.yml | 50 -------------------------- 1 file changed, 50 deletions(-) diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml index e00f2087a..e69de29bb 100644 --- a/.github/workflows/duplicate-issues.yml +++ b/.github/workflows/duplicate-issues.yml @@ -1,50 +0,0 @@ -name: Duplicate Issue Detection - -on: - issues: - types: [opened] - -jobs: - check-duplicates: - runs-on: ubuntu-latest - permissions: - contents: read - issues: write - id-token: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Install opencode - run: curl -fsSL https://opencode.ai/install | bash - - - name: Check for duplicate issues - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - opencode run -m anthropic/claude-sonnet-4-20250514 "A new issue has been created: '${{ github.event.issue.title }}' - - Issue body: - ${{ github.event.issue.body }} - - Please search through existing issues in this repository to find any potential duplicates of this new issue. Consider: - 1. Similar titles or descriptions - 2. Same error messages or symptoms - 3. Related functionality or components - 4. Similar feature requests - - If you find any potential duplicates, please comment on the new issue with: - - A brief explanation of why it might be a duplicate - - Links to the potentially duplicate issues - - A suggestion to check those issues first - - Use this format for the comment: - '👋 This issue might be a duplicate of existing issues. Please check: - - #[issue_number]: [brief description of similarity] - - If none of these address your specific case, please let us know how this issue differs.' - - If no clear duplicates are found, do not comment." From 3d9c5b4adf5a7e1a49fb3c0f3a7a30f2d35fa244 Mon Sep 17 00:00:00 2001 From: Dax Date: Tue, 12 Aug 2025 00:34:34 -0400 Subject: [PATCH 125/178] Update guidelines-check.yml --- .github/workflows/guidelines-check.yml | 41 -------------------------- 1 file changed, 41 deletions(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 3582be7c3..e69de29bb 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -1,41 +0,0 @@ -name: Guidelines Check - -on: - pull_request: - types: [opened, synchronize] - -jobs: - check-guidelines: - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - id-token: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Install opencode - run: curl -fsSL https://opencode.ai/install | bash - - - name: Check PR guidelines compliance - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - opencode run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' - - PR description: - ${{ github.event.pull_request.body }} - - Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. - - For each violation you find, create a file comment using the gh CLI. Use this exact format for each violation: - - \`\`\`bash - gh pr review ${{ github.event.pull_request.number }} --comment-body 'This violates the AGENTS.md guideline: [specific rule]. Consider: [suggestion]' --file 'path/to/file.ts' --line [line_number] - \`\`\` - - Only create comments for actual violations. If the code follows all guidelines, don't run any gh commands." From 80b25c79bb931fd8514d4ce0ee0aa487c128adda Mon Sep 17 00:00:00 2001 From: Camden Clark Date: Mon, 11 Aug 2025 23:05:26 -0700 Subject: [PATCH 126/178] fix: preserve process.env when spawning formatter commands (#1850) --- packages/opencode/src/format/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/format/index.ts b/packages/opencode/src/format/index.ts index d4f73c38a..52eefa86d 100644 --- a/packages/opencode/src/format/index.ts +++ b/packages/opencode/src/format/index.ts @@ -71,7 +71,7 @@ export namespace Format { const proc = Bun.spawn({ cmd: item.command.map((x) => x.replace("$FILE", file)), cwd: App.info().path.cwd, - env: item.environment, + env: { ...process.env, ...item.environment }, stdout: "ignore", stderr: "ignore", }) From 631722213b177a0cf4e0ebdbf1adf698730dac6a Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 12 Aug 2025 12:04:42 +0000 Subject: [PATCH 127/178] ignore: update download stats 2025-08-12 --- STATS.md | 91 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/STATS.md b/STATS.md index 057e5f2ed..6b3b2cf06 100644 --- a/STATS.md +++ b/STATS.md @@ -1,47 +1,48 @@ # Download Stats -| Date | GitHub Downloads | npm Downloads | Total | -| ---------- | ---------------- | ---------------- | ---------------- | -| 2025-06-29 | 18,789 (+0) | 39,420 (+0) | 58,209 (+0) | -| 2025-06-30 | 20,127 (+1,338) | 41,059 (+1,639) | 61,186 (+2,977) | -| 2025-07-01 | 22,108 (+1,981) | 43,745 (+2,686) | 65,853 (+4,667) | -| 2025-07-02 | 24,814 (+2,706) | 46,168 (+2,423) | 70,982 (+5,129) | -| 2025-07-03 | 27,834 (+3,020) | 49,955 (+3,787) | 77,789 (+6,807) | -| 2025-07-04 | 30,608 (+2,774) | 54,758 (+4,803) | 85,366 (+7,577) | -| 2025-07-05 | 32,524 (+1,916) | 58,371 (+3,613) | 90,895 (+5,529) | -| 2025-07-06 | 33,766 (+1,242) | 59,694 (+1,323) | 93,460 (+2,565) | -| 2025-07-08 | 38,052 (+4,286) | 64,468 (+4,774) | 102,520 (+9,060) | -| 2025-07-09 | 40,924 (+2,872) | 67,935 (+3,467) | 108,859 (+6,339) | -| 2025-07-10 | 43,796 (+2,872) | 71,402 (+3,467) | 115,198 (+6,339) | -| 2025-07-11 | 46,982 (+3,186) | 77,462 (+6,060) | 124,444 (+9,246) | -| 2025-07-12 | 49,302 (+2,320) | 82,177 (+4,715) | 131,479 (+7,035) | -| 2025-07-13 | 50,803 (+1,501) | 86,394 (+4,217) | 137,197 (+5,718) | -| 2025-07-14 | 53,283 (+2,480) | 87,860 (+1,466) | 141,143 (+3,946) | -| 2025-07-15 | 57,590 (+4,307) | 91,036 (+3,176) | 148,626 (+7,483) | -| 2025-07-16 | 62,313 (+4,723) | 95,258 (+4,222) | 157,571 (+8,945) | -| 2025-07-17 | 66,684 (+4,371) | 100,048 (+4,790) | 166,732 (+9,161) | -| 2025-07-18 | 70,379 (+3,695) | 102,587 (+2,539) | 172,966 (+6,234) | -| 2025-07-19 | 73,497 (+3,117) | 105,904 (+3,317) | 179,401 (+6,434) | -| 2025-07-20 | 76,453 (+2,956) | 109,044 (+3,140) | 185,497 (+6,096) | -| 2025-07-21 | 80,197 (+3,744) | 113,537 (+4,493) | 193,734 (+8,237) | -| 2025-07-22 | 84,251 (+4,054) | 118,073 (+4,536) | 202,324 (+8,590) | -| 2025-07-23 | 88,589 (+4,338) | 121,436 (+3,363) | 210,025 (+7,701) | -| 2025-07-24 | 92,469 (+3,880) | 124,091 (+2,655) | 216,560 (+6,535) | -| 2025-07-25 | 96,417 (+3,948) | 126,985 (+2,894) | 223,402 (+6,842) | -| 2025-07-26 | 100,646 (+4,229) | 131,411 (+4,426) | 232,057 (+8,655) | -| 2025-07-27 | 102,644 (+1,998) | 134,736 (+3,325) | 237,380 (+5,323) | -| 2025-07-28 | 105,446 (+2,802) | 136,016 (+1,280) | 241,462 (+4,082) | -| 2025-07-29 | 108,998 (+3,552) | 137,542 (+1,526) | 246,540 (+5,078) | -| 2025-07-30 | 113,544 (+4,546) | 140,317 (+2,775) | 253,861 (+7,321) | -| 2025-07-31 | 118,339 (+4,795) | 143,344 (+3,027) | 261,683 (+7,822) | -| 2025-08-01 | 123,539 (+5,200) | 146,680 (+3,336) | 270,219 (+8,536) | -| 2025-08-02 | 127,864 (+4,325) | 149,236 (+2,556) | 277,100 (+6,881) | -| 2025-08-03 | 131,397 (+3,533) | 150,451 (+1,215) | 281,848 (+4,748) | -| 2025-08-04 | 136,266 (+4,869) | 153,260 (+2,809) | 289,526 (+7,678) | -| 2025-08-05 | 141,596 (+5,330) | 155,752 (+2,492) | 297,348 (+7,822) | -| 2025-08-06 | 147,067 (+5,471) | 158,309 (+2,557) | 305,376 (+8,028) | -| 2025-08-07 | 152,591 (+5,524) | 160,889 (+2,580) | 313,480 (+8,104) | -| 2025-08-08 | 158,187 (+5,596) | 163,448 (+2,559) | 321,635 (+8,155) | -| 2025-08-09 | 162,770 (+4,583) | 165,721 (+2,273) | 328,491 (+6,856) | -| 2025-08-10 | 165,695 (+2,925) | 167,109 (+1,388) | 332,804 (+4,313) | -| 2025-08-11 | 169,297 (+3,602) | 167,953 (+844) | 337,250 (+4,446) | +| Date | GitHub Downloads | npm Downloads | Total | +| ---------- | ---------------- | ---------------- | ----------------- | +| 2025-06-29 | 18,789 (+0) | 39,420 (+0) | 58,209 (+0) | +| 2025-06-30 | 20,127 (+1,338) | 41,059 (+1,639) | 61,186 (+2,977) | +| 2025-07-01 | 22,108 (+1,981) | 43,745 (+2,686) | 65,853 (+4,667) | +| 2025-07-02 | 24,814 (+2,706) | 46,168 (+2,423) | 70,982 (+5,129) | +| 2025-07-03 | 27,834 (+3,020) | 49,955 (+3,787) | 77,789 (+6,807) | +| 2025-07-04 | 30,608 (+2,774) | 54,758 (+4,803) | 85,366 (+7,577) | +| 2025-07-05 | 32,524 (+1,916) | 58,371 (+3,613) | 90,895 (+5,529) | +| 2025-07-06 | 33,766 (+1,242) | 59,694 (+1,323) | 93,460 (+2,565) | +| 2025-07-08 | 38,052 (+4,286) | 64,468 (+4,774) | 102,520 (+9,060) | +| 2025-07-09 | 40,924 (+2,872) | 67,935 (+3,467) | 108,859 (+6,339) | +| 2025-07-10 | 43,796 (+2,872) | 71,402 (+3,467) | 115,198 (+6,339) | +| 2025-07-11 | 46,982 (+3,186) | 77,462 (+6,060) | 124,444 (+9,246) | +| 2025-07-12 | 49,302 (+2,320) | 82,177 (+4,715) | 131,479 (+7,035) | +| 2025-07-13 | 50,803 (+1,501) | 86,394 (+4,217) | 137,197 (+5,718) | +| 2025-07-14 | 53,283 (+2,480) | 87,860 (+1,466) | 141,143 (+3,946) | +| 2025-07-15 | 57,590 (+4,307) | 91,036 (+3,176) | 148,626 (+7,483) | +| 2025-07-16 | 62,313 (+4,723) | 95,258 (+4,222) | 157,571 (+8,945) | +| 2025-07-17 | 66,684 (+4,371) | 100,048 (+4,790) | 166,732 (+9,161) | +| 2025-07-18 | 70,379 (+3,695) | 102,587 (+2,539) | 172,966 (+6,234) | +| 2025-07-19 | 73,497 (+3,117) | 105,904 (+3,317) | 179,401 (+6,434) | +| 2025-07-20 | 76,453 (+2,956) | 109,044 (+3,140) | 185,497 (+6,096) | +| 2025-07-21 | 80,197 (+3,744) | 113,537 (+4,493) | 193,734 (+8,237) | +| 2025-07-22 | 84,251 (+4,054) | 118,073 (+4,536) | 202,324 (+8,590) | +| 2025-07-23 | 88,589 (+4,338) | 121,436 (+3,363) | 210,025 (+7,701) | +| 2025-07-24 | 92,469 (+3,880) | 124,091 (+2,655) | 216,560 (+6,535) | +| 2025-07-25 | 96,417 (+3,948) | 126,985 (+2,894) | 223,402 (+6,842) | +| 2025-07-26 | 100,646 (+4,229) | 131,411 (+4,426) | 232,057 (+8,655) | +| 2025-07-27 | 102,644 (+1,998) | 134,736 (+3,325) | 237,380 (+5,323) | +| 2025-07-28 | 105,446 (+2,802) | 136,016 (+1,280) | 241,462 (+4,082) | +| 2025-07-29 | 108,998 (+3,552) | 137,542 (+1,526) | 246,540 (+5,078) | +| 2025-07-30 | 113,544 (+4,546) | 140,317 (+2,775) | 253,861 (+7,321) | +| 2025-07-31 | 118,339 (+4,795) | 143,344 (+3,027) | 261,683 (+7,822) | +| 2025-08-01 | 123,539 (+5,200) | 146,680 (+3,336) | 270,219 (+8,536) | +| 2025-08-02 | 127,864 (+4,325) | 149,236 (+2,556) | 277,100 (+6,881) | +| 2025-08-03 | 131,397 (+3,533) | 150,451 (+1,215) | 281,848 (+4,748) | +| 2025-08-04 | 136,266 (+4,869) | 153,260 (+2,809) | 289,526 (+7,678) | +| 2025-08-05 | 141,596 (+5,330) | 155,752 (+2,492) | 297,348 (+7,822) | +| 2025-08-06 | 147,067 (+5,471) | 158,309 (+2,557) | 305,376 (+8,028) | +| 2025-08-07 | 152,591 (+5,524) | 160,889 (+2,580) | 313,480 (+8,104) | +| 2025-08-08 | 158,187 (+5,596) | 163,448 (+2,559) | 321,635 (+8,155) | +| 2025-08-09 | 162,770 (+4,583) | 165,721 (+2,273) | 328,491 (+6,856) | +| 2025-08-10 | 165,695 (+2,925) | 167,109 (+1,388) | 332,804 (+4,313) | +| 2025-08-11 | 169,297 (+3,602) | 167,953 (+844) | 337,250 (+4,446) | +| 2025-08-12 | 176,307 (+7,010) | 171,876 (+3,923) | 348,183 (+10,933) | From b2f2c9ac37e05455c282bf4106a5288d3a1478b3 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Tue, 12 Aug 2025 07:52:09 -0500 Subject: [PATCH 128/178] fix: use real cursor instead of virtual cursor --- .../tui/internal/components/chat/editor.go | 6 +++ packages/tui/internal/tui/tui.go | 48 ++++++++++++++----- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/packages/tui/internal/components/chat/editor.go b/packages/tui/internal/components/chat/editor.go index 509de6244..980d5262c 100644 --- a/packages/tui/internal/components/chat/editor.go +++ b/packages/tui/internal/components/chat/editor.go @@ -31,6 +31,7 @@ type EditorComponent interface { tea.Model tea.ViewModel Content() string + Cursor() *tea.Cursor Lines() int Value() string Length() int @@ -407,6 +408,10 @@ func (m *editorComponent) Content() string { return content } +func (m *editorComponent) Cursor() *tea.Cursor { + return m.textarea.Cursor() +} + func (m *editorComponent) View() string { width := m.width if m.app.Session.ID == "" { @@ -694,6 +699,7 @@ func NewEditorComponent(app *app.App) EditorComponent { ta.Prompt = " " ta.ShowLineNumbers = false ta.CharLimit = -1 + ta.VirtualCursor = false ta = updateTextareaStyles(ta) m := &editorComponent{ diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 6a56e2012..7895adeac 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -59,6 +59,8 @@ const interruptDebounceTimeout = 1 * time.Second const exitDebounceTimeout = 1 * time.Second type Model struct { + tea.Model + tea.CursorModel width, height int app *app.App modal layout.Modal @@ -748,15 +750,17 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return a, tea.Batch(cmds...) } -func (a Model) View() string { +func (a Model) View() (string, *tea.Cursor) { t := theme.CurrentTheme() var mainLayout string + var editorX int + var editorY int if a.app.Session.ID == "" { - mainLayout = a.home() + mainLayout, editorX, editorY = a.home() } else { - mainLayout = a.chat() + mainLayout, editorX, editorY = a.chat() } mainLayout = styles.NewStyle(). Background(t.Background()). @@ -780,7 +784,12 @@ func (a Model) View() string { if theme.CurrentThemeUsesAnsiColors() { mainLayout = util.ConvertRGBToAnsi16Colors(mainLayout) } - return mainLayout + "\n" + a.status.View() + + cursor := a.editor.Cursor() + cursor.Position.X += editorX + cursor.Position.Y += editorY + + return mainLayout + "\n" + a.status.View(), cursor } func (a Model) Cleanup() { @@ -807,7 +816,7 @@ func (a Model) openFile(filepath string) (tea.Model, tea.Cmd) { return a, cmd } -func (a Model) home() string { +func (a Model) home() (string, int, int) { t := theme.CurrentTheme() effectiveWidth := a.width - 4 baseStyle := styles.NewStyle().Background(t.Background()) @@ -899,14 +908,21 @@ func (a Model) home() string { styles.WhitespaceStyle(t.Background()), ) - editorX := (effectiveWidth - editorWidth) / 2 + editorX := max(0, (effectiveWidth-editorWidth)/2) editorY := (a.height / 2) + (mainHeight / 2) - 2 if editorLines > 1 { + content := a.editor.Content() + editorHeight := lipgloss.Height(content) + + if editorY+editorHeight > a.height { + difference := (editorY + editorHeight) - a.height + editorY -= difference + } mainLayout = layout.PlaceOverlay( editorX, editorY, - a.editor.Content(), + content, mainLayout, ) } @@ -924,10 +940,10 @@ func (a Model) home() string { ) } - return mainLayout + return mainLayout, editorX + 5, editorY + 2 } -func (a Model) chat() string { +func (a Model) chat() (string, int, int) { effectiveWidth := a.width - 4 t := theme.CurrentTheme() editorView := a.editor.View() @@ -944,14 +960,20 @@ func (a Model) chat() string { ) mainLayout := messagesView + "\n" + editorView - editorX := (effectiveWidth - editorWidth) / 2 + editorX := max(0, (effectiveWidth-editorWidth)/2) + editorY := a.height - editorHeight if lines > 1 { - editorY := a.height - editorHeight + content := a.editor.Content() + editorHeight := lipgloss.Height(content) + if editorY+editorHeight > a.height { + difference := (editorY + editorHeight) - a.height + editorY -= difference + } mainLayout = layout.PlaceOverlay( editorX, editorY, - a.editor.Content(), + content, mainLayout, ) } @@ -970,7 +992,7 @@ func (a Model) chat() string { ) } - return mainLayout + return mainLayout, editorX + 5, editorY + 2 } func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) { From a071a2b7f4acc6eee0ed0fc43b2022f0373eab6d Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 12:58:53 +0000 Subject: [PATCH 129/178] release: v0.4.27 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index f7a2eb6fe..a146b923a 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.26", + "version": "0.4.27", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 0d68ce32e..f67eb8925 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.26", + "version": "0.4.27", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 4f33e676b..10f6e4d94 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.26", + "version": "0.4.27", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 6cfae4db5..4a1a26328 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.26", + "version": "0.4.27", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index d77a00986..24dcaa019 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.26", + "version": "0.4.27", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 72977db16..50faf81fd 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.26", + "version": "0.4.27", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 2bc27a0b7..0f8598fcc 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.26", + "version": "0.4.27", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index 1decf7b96..113a9a1df 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.26", + "version": "0.4.27", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 474431b9a..6bffa8a68 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.26", + "version": "0.4.27", "publisher": "sst-dev", "repository": { "type": "git", From 3530885f48df779e78d713246a5a003a8d286e3d Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Tue, 12 Aug 2025 08:48:32 -0500 Subject: [PATCH 130/178] fix: vscode extension cursor placement --- packages/tui/internal/components/commands/commands.go | 8 ++++---- packages/tui/internal/util/ide.go | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/tui/internal/components/commands/commands.go b/packages/tui/internal/components/commands/commands.go index b8e7871ce..fd578a41b 100644 --- a/packages/tui/internal/components/commands/commands.go +++ b/packages/tui/internal/components/commands/commands.go @@ -83,10 +83,10 @@ func (c *commandsComponent) View() string { } commandsToShow = append(commandsToShow, // empty line - commands.Command{ - Name: "", - Description: "", - }, + // commands.Command{ + // Name: "", + // Description: "", + // }, commands.Command{ Name: commands.CommandName(util.Ide()), Description: "open opencode", diff --git a/packages/tui/internal/util/ide.go b/packages/tui/internal/util/ide.go index 5d0402b4b..7b3832f9b 100644 --- a/packages/tui/internal/util/ide.go +++ b/packages/tui/internal/util/ide.go @@ -6,11 +6,11 @@ import ( ) var SUPPORTED_IDES = []struct { - Search string + Search string ShortName string }{ {"Windsurf", "Windsurf"}, - {"Visual Studio Code", "VS Code"}, + {"Visual Studio Code", "vscode"}, {"Cursor", "Cursor"}, {"VSCodium", "VSCodium"}, } @@ -27,4 +27,5 @@ func Ide() string { } return "unknown" -} \ No newline at end of file +} + From 0835170224b7f5f78b4ad2229bf38a4598edb954 Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 13:54:32 +0000 Subject: [PATCH 131/178] release: v0.4.28 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index a146b923a..fc8f2c282 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.27", + "version": "0.4.28", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index f67eb8925..391e9c804 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.27", + "version": "0.4.28", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 10f6e4d94..ccf8d6ce6 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.27", + "version": "0.4.28", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 4a1a26328..ceadd3ea7 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.27", + "version": "0.4.28", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 24dcaa019..e420b4a47 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.27", + "version": "0.4.28", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 50faf81fd..52f299df7 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.27", + "version": "0.4.28", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 0f8598fcc..f6295fc24 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.27", + "version": "0.4.28", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index 113a9a1df..b126a43b9 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.27", + "version": "0.4.28", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 6bffa8a68..5810f58c7 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.27", + "version": "0.4.28", "publisher": "sst-dev", "repository": { "type": "git", From f12d470b33a46833dea77b903d3359969c28f811 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Tue, 12 Aug 2025 08:54:20 -0500 Subject: [PATCH 132/178] =?UTF-8?q?fix:=20Missing=20~/.local/share/opencod?= =?UTF-8?q?e/bin=20directory=20causes=20misleading=20=E2=80=A6=20(#1860)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/opencode/src/global/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/global/index.ts b/packages/opencode/src/global/index.ts index c69ea0d78..4cfaf03a5 100644 --- a/packages/opencode/src/global/index.ts +++ b/packages/opencode/src/global/index.ts @@ -25,6 +25,7 @@ await Promise.all([ fs.mkdir(Global.Path.config, { recursive: true }), fs.mkdir(Global.Path.state, { recursive: true }), fs.mkdir(Global.Path.log, { recursive: true }), + fs.mkdir(Global.Path.bin, { recursive: true }), ]) const CACHE_VERSION = "7" From 2bbd7a167a96f0d8e64515064f00e89345cc503f Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 14:00:15 +0000 Subject: [PATCH 133/178] release: v0.4.29 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index fc8f2c282..48d8293e0 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.28", + "version": "0.4.29", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 391e9c804..6ceeb2467 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.28", + "version": "0.4.29", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index ccf8d6ce6..0d3561147 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.28", + "version": "0.4.29", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index ceadd3ea7..5a70453fb 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.28", + "version": "0.4.29", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index e420b4a47..cf4f50329 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.28", + "version": "0.4.29", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 52f299df7..d3c93687f 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.28", + "version": "0.4.29", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index f6295fc24..c63d1d1fa 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.28", + "version": "0.4.29", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index b126a43b9..e974b291d 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.28", + "version": "0.4.29", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 5810f58c7..e34df276a 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.28", + "version": "0.4.29", "publisher": "sst-dev", "repository": { "type": "git", From ccaebdcd16438d0ede6291d3b4f82e19e0143610 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Tue, 12 Aug 2025 10:34:54 -0500 Subject: [PATCH 134/178] fix: long word and attachment wrapping in editor --- .../internal/components/textarea/textarea.go | 195 +++++++++++++++++- 1 file changed, 187 insertions(+), 8 deletions(-) diff --git a/packages/tui/internal/components/textarea/textarea.go b/packages/tui/internal/components/textarea/textarea.go index 60e72e09a..a56f2b788 100644 --- a/packages/tui/internal/components/textarea/textarea.go +++ b/packages/tui/internal/components/textarea/textarea.go @@ -2045,6 +2045,109 @@ func itemWidth(item any) int { return 0 } +// forceWrapAttachment splits an attachment's display text across multiple lines +func forceWrapAttachment(att *attachment.Attachment, width int) [][]any { + if width <= 0 { + return [][]any{{att}} + } + + display := att.Display + displayRunes := []rune(display) + + if len(displayRunes) <= width { + return [][]any{{att}} + } + + var lines [][]any + start := 0 + + for start < len(displayRunes) { + // Calculate how many runes fit in this line + end := start + width + if end > len(displayRunes) { + end = len(displayRunes) + } + + // Create a wrapped attachment for this segment + wrappedAtt := &attachment.Attachment{ + ID: att.ID, + Type: att.Type, + Display: string(displayRunes[start:end]), + URL: att.URL, + Filename: att.Filename, + MediaType: att.MediaType, + Source: att.Source, + } + + lines = append(lines, []any{wrappedAtt}) + start = end + } + + return lines +} + +// forceWrapWord splits a word that's too long to fit within the given width +func forceWrapWord(word []any, width int) [][]any { + if width <= 0 || len(word) == 0 { + return [][]any{word} + } + + var lines [][]any + currentLine := []any{} + currentWidth := 0 + + for _, item := range word { + if att, ok := item.(*attachment.Attachment); ok { + // Handle attachment that might be too wide + attWidth := uniseg.StringWidth(att.Display) + + // If the attachment display is too wide, split it + if attWidth > width { + // Finish current line if it has content + if len(currentLine) > 0 { + lines = append(lines, currentLine) + currentLine = []any{} + currentWidth = 0 + } + + // Split the attachment display across multiple lines + wrappedAttachment := forceWrapAttachment(att, width) + lines = append(lines, wrappedAttachment...) + continue + } + + // If adding this attachment would exceed the width, start a new line + if currentWidth+attWidth > width && len(currentLine) > 0 { + lines = append(lines, currentLine) + currentLine = []any{} + currentWidth = 0 + } + + currentLine = append(currentLine, item) + currentWidth += attWidth + } else if r, ok := item.(rune); ok { + itemWidth := rw.RuneWidth(r) + + // If adding this rune would exceed the width, start a new line + if currentWidth+itemWidth > width && len(currentLine) > 0 { + lines = append(lines, currentLine) + currentLine = []any{} + currentWidth = 0 + } + + currentLine = append(currentLine, item) + currentWidth += itemWidth + } + } + + // Add the last line if it has content + if len(currentLine) > 0 { + lines = append(lines, currentLine) + } + + return lines +} + func wrapInterfaces(content []any, width int) [][]any { if width <= 0 { return [][]any{content} @@ -2076,11 +2179,49 @@ func wrapInterfaces(content []any, width int) [][]any { if !inSpaces { // End of a word if lineW > 0 && lineW+wordW > width { - lines = append(lines, word) - lineW = wordW + // If the word itself is too long to fit on a line, force-wrap it + if wordW > width { + wrappedLines := forceWrapWord(word, width) + lines = append(lines, wrappedLines...) + // Calculate width of the last wrapped line + lastLine := wrappedLines[len(wrappedLines)-1] + lineW = 0 + for _, item := range lastLine { + if r, ok := item.(rune); ok { + lineW += rw.RuneWidth(r) + } else if att, ok := item.(*attachment.Attachment); ok { + lineW += uniseg.StringWidth(att.Display) + } + } + } else { + lines = append(lines, word) + lineW = wordW + } } else { - lines[len(lines)-1] = append(lines[len(lines)-1], word...) - lineW += wordW + // Check if the word needs to be force-wrapped even when it fits on the current line + if wordW > width { + currentLine := lines[len(lines)-1] + wrappedWord := forceWrapWord(word, width-lineW) + if len(wrappedWord) > 0 { + lines[len(lines)-1] = append(currentLine, wrappedWord[0]...) + for i := 1; i < len(wrappedWord); i++ { + lines = append(lines, wrappedWord[i]) + } + // Calculate width of the last wrapped line + lastLine := wrappedWord[len(wrappedWord)-1] + lineW = 0 + for _, item := range lastLine { + if r, ok := item.(rune); ok { + lineW += rw.RuneWidth(r) + } else if att, ok := item.(*attachment.Attachment); ok { + lineW += uniseg.StringWidth(att.Display) + } + } + } + } else { + lines[len(lines)-1] = append(lines[len(lines)-1], word...) + lineW += wordW + } } word = nil wordW = 0 @@ -2110,11 +2251,49 @@ func wrapInterfaces(content []any, width int) [][]any { // Handle any remaining word/spaces at the end of the content. if wordW > 0 { if lineW > 0 && lineW+wordW > width { - lines = append(lines, word) - lineW = wordW + // If the word itself is too long to fit on a line, force-wrap it + if wordW > width { + wrappedLines := forceWrapWord(word, width) + lines = append(lines, wrappedLines...) + // Calculate width of the last wrapped line + lastLine := wrappedLines[len(wrappedLines)-1] + lineW = 0 + for _, item := range lastLine { + if r, ok := item.(rune); ok { + lineW += rw.RuneWidth(r) + } else if att, ok := item.(*attachment.Attachment); ok { + lineW += uniseg.StringWidth(att.Display) + } + } + } else { + lines = append(lines, word) + lineW = wordW + } } else { - lines[len(lines)-1] = append(lines[len(lines)-1], word...) - lineW += wordW + // Check if the word needs to be force-wrapped even when it fits on the current line + if wordW > width { + currentLine := lines[len(lines)-1] + wrappedWord := forceWrapWord(word, width-lineW) + if len(wrappedWord) > 0 { + lines[len(lines)-1] = append(currentLine, wrappedWord[0]...) + for i := 1; i < len(wrappedWord); i++ { + lines = append(lines, wrappedWord[i]) + } + // Calculate width of the last wrapped line + lastLine := wrappedWord[len(wrappedWord)-1] + lineW = 0 + for _, item := range lastLine { + if r, ok := item.(rune); ok { + lineW += rw.RuneWidth(r) + } else if att, ok := item.(*attachment.Attachment); ok { + lineW += uniseg.StringWidth(att.Display) + } + } + } + } else { + lines[len(lines)-1] = append(lines[len(lines)-1], word...) + lineW += wordW + } } } if spaceW > 0 { From 10735f93ca97a1a9eedc3a245ab627bd00e72082 Mon Sep 17 00:00:00 2001 From: Dax Date: Tue, 12 Aug 2025 11:39:39 -0400 Subject: [PATCH 135/178] Add agent-level permissions with whitelist/blacklist support (#1862) --- .github/workflows/duplicate-issues.yml | 50 +++++++ .github/workflows/guidelines-check.yml | 49 ++++++ .opencode/agent/github.md | 13 ++ opencode.json | 3 - packages/opencode/src/agent/agent.ts | 57 +++++-- packages/opencode/src/config/config.ts | 13 +- packages/opencode/src/index.ts | 3 + packages/opencode/src/permission/index.ts | 2 +- packages/opencode/src/session/index.ts | 4 +- packages/opencode/src/tool/bash.ts | 27 +--- packages/opencode/src/tool/edit.ts | 8 +- packages/opencode/src/tool/registry.ts | 15 +- packages/opencode/src/tool/tool.ts | 1 + packages/opencode/src/tool/write.ts | 6 +- packages/opencode/test/tool/bash.test.ts | 3 +- packages/opencode/test/tool/tool.test.ts | 1 + packages/web/src/content/docs/docs/agents.mdx | 141 ++++++++++++++++++ .../web/src/content/docs/docs/permissions.mdx | 2 + 18 files changed, 344 insertions(+), 54 deletions(-) create mode 100644 .opencode/agent/github.md diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml index e69de29bb..ba9577dbb 100644 --- a/.github/workflows/duplicate-issues.yml +++ b/.github/workflows/duplicate-issues.yml @@ -0,0 +1,50 @@ +name: Duplicate Issue Detection + +on: + issues: + types: [opened] + +jobs: + check-duplicates: + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Install opencode + run: curl -fsSL https://opencode.ai/install | bash + + - name: Check for duplicate issues + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + opencode run --agent github -m anthropic/claude-sonnet-4-20250514 "A new issue has been created: '${{ github.event.issue.title }}' + + Issue body: + ${{ github.event.issue.body }} + + Please search through existing issues in this repository to find any potential duplicates of this new issue. Consider: + 1. Similar titles or descriptions + 2. Same error messages or symptoms + 3. Related functionality or components + 4. Similar feature requests + + If you find any potential duplicates, please comment on the new issue with: + - A brief explanation of why it might be a duplicate + - Links to the potentially duplicate issues + - A suggestion to check those issues first + + Use this format for the comment: + '👋 This issue might be a duplicate of existing issues. Please check: + - #[issue_number]: [brief description of similarity] + + If none of these address your specific case, please let us know how this issue differs.' + + If no clear duplicates are found, do not comment." diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index e69de29bb..9f4915f58 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -0,0 +1,49 @@ +name: Guidelines Check + +on: + pull_request: + types: [opened, synchronize] + +jobs: + check-guidelines: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Install opencode + run: curl -fsSL https://opencode.ai/install | bash + + - name: Check PR guidelines compliance + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + opencode run --agent github -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' + + PR description: + ${{ github.event.pull_request.body }} + + Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. + + For each violation you find, create a file comment using the gh CLI. Use this exact format for each violation: + + \`\`\`bash + gh pr review ${{ github.event.pull_request.number }} --comment-body 'This violates the AGENTS.md guideline: [specific rule]. Consider: [suggestion]' --file 'path/to/file.ts' --line [line_number] + \`\`\` + + When possible, also submit code change suggestions using: + + \`\`\`bash + gh pr review ${{ github.event.pull_request.number }} --comment-body 'Suggested fix for AGENTS.md guideline violation:' --file 'path/to/file.ts' --line [line_number] --body '```suggestion + [corrected code here] + ```' + \`\`\` + + Only create comments for actual violations. If the code follows all guidelines, don't run any gh commands." diff --git a/.opencode/agent/github.md b/.opencode/agent/github.md new file mode 100644 index 000000000..da3aa4518 --- /dev/null +++ b/.opencode/agent/github.md @@ -0,0 +1,13 @@ +--- +permission: + bash: + "*": "deny" + "gh*": "allow" +mode: subagent +--- + +You are running in github actions, typically to evaluate a PR. Do not do +anything that is outside the scope of that. You have access to the bash tool but +you can only run `gh` cli commands with it. + +Diffs are important but be sure to read the whole file to get the full context. diff --git a/opencode.json b/opencode.json index 003253ee7..59f14ac75 100644 --- a/opencode.json +++ b/opencode.json @@ -1,8 +1,5 @@ { "$schema": "https://opencode.ai/config.json", - "agent": { - "build": {} - }, "mcp": { "context7": { "type": "remote", diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index b363e0e9b..aa9eeec8e 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -5,6 +5,7 @@ import { Provider } from "../provider/provider" import { generateObject, type ModelMessage } from "ai" import PROMPT_GENERATE from "./generate.txt" import { SystemPrompt } from "../session/system" +import { mergeDeep } from "remeda" export namespace Agent { export const Info = z @@ -14,6 +15,11 @@ export namespace Agent { mode: z.union([z.literal("subagent"), z.literal("primary"), z.literal("all")]), topP: z.number().optional(), temperature: z.number().optional(), + permission: z.object({ + edit: Config.Permission, + bash: z.record(z.string(), Config.Permission), + webfetch: Config.Permission.optional(), + }), model: z .object({ modelID: z.string(), @@ -31,6 +37,13 @@ export namespace Agent { const state = App.state("agent", async () => { const cfg = await Config.get() + const defaultPermission: Info["permission"] = { + edit: "allow", + bash: { + "*": "allow", + }, + webfetch: "allow", + } const result: Record = { general: { name: "general", @@ -41,17 +54,20 @@ export namespace Agent { todowrite: false, }, options: {}, + permission: defaultPermission, mode: "subagent", }, build: { name: "build", tools: {}, options: {}, + permission: defaultPermission, mode: "primary", }, plan: { name: "plan", options: {}, + permission: defaultPermission, tools: { write: false, edit: false, @@ -70,25 +86,48 @@ export namespace Agent { item = result[key] = { name: key, mode: "all", + permission: defaultPermission, options: {}, tools: {}, } - const { model, prompt, tools, description, temperature, top_p, mode, ...extra } = value + const { model, prompt, tools, description, temperature, top_p, mode, permission, ...extra } = value item.options = { ...item.options, ...extra, } - if (value.model) item.model = Provider.parseModel(value.model) - if (value.prompt) item.prompt = value.prompt - if (value.tools) + if (model) item.model = Provider.parseModel(model) + if (prompt) item.prompt = prompt + if (tools) item.tools = { ...item.tools, - ...value.tools, + ...tools, } - if (value.description) item.description = value.description - if (value.temperature != undefined) item.temperature = value.temperature - if (value.top_p != undefined) item.topP = value.top_p - if (value.mode) item.mode = value.mode + if (description) item.description = description + if (temperature != undefined) item.temperature = temperature + if (top_p != undefined) item.topP = top_p + if (mode) item.mode = mode + + if (permission ?? cfg.permission) { + const merged = mergeDeep(cfg.permission ?? {}, permission ?? {}) + if (merged.edit) item.permission.edit = merged.edit + if (merged.webfetch) item.permission.webfetch = merged.webfetch + if (merged.bash) { + if (typeof merged.bash === "string") { + item.permission.bash = { + "*": merged.bash, + } + } + // if granular permissions are provided, default to "ask" + if (typeof merged.bash === "object") { + item.permission.bash = mergeDeep( + { + "*": "ask", + }, + merged.bash, + ) + } + } + } } return result }) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 40ec22b57..a8d84d8bc 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -164,6 +164,9 @@ export namespace Config { export const Mcp = z.discriminatedUnion("type", [McpLocal, McpRemote]) export type Mcp = z.infer + export const Permission = z.union([z.literal("ask"), z.literal("allow"), z.literal("deny")]) + export type Permission = z.infer + export const Agent = z .object({ model: z.string().optional(), @@ -174,6 +177,13 @@ export namespace Config { disable: z.boolean().optional(), description: z.string().optional().describe("Description of when to use the agent"), mode: z.union([z.literal("subagent"), z.literal("primary"), z.literal("all")]).optional(), + permission: z + .object({ + edit: Permission.optional(), + bash: z.union([Permission, z.record(z.string(), Permission)]).optional(), + webfetch: Permission.optional(), + }) + .optional(), }) .catchall(z.any()) .openapi({ @@ -243,9 +253,6 @@ export namespace Config { }) export type Layout = z.infer - export const Permission = z.union([z.literal("ask"), z.literal("allow"), z.literal("deny")]) - export type Permission = z.infer - export const Info = z .object({ $schema: z.string().optional().describe("JSON schema reference for configuration validation"), diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 007de6509..868c96ba0 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -21,6 +21,9 @@ import { GithubCommand } from "./cli/cmd/github" const cancel = new AbortController() +try { +} catch (e) {} + process.on("unhandledRejection", (e) => { Log.Default.error("rejection", { e: e instanceof Error ? e.message : e, diff --git a/packages/opencode/src/permission/index.ts b/packages/opencode/src/permission/index.ts index 53c49696e..d724e9932 100644 --- a/packages/opencode/src/permission/index.ts +++ b/packages/opencode/src/permission/index.ts @@ -155,7 +155,7 @@ export namespace Permission { public readonly permissionID: string, public readonly toolCallID?: string, ) { - super(`The user rejected permission to use this functionality`) + super(`The user rejected permission to use this specific tool call. You may try again with different parameters.`) } } } diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 6d2b94863..b881161a2 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -523,6 +523,7 @@ export namespace Session { t.execute(args, { sessionID: input.sessionID, abort: new AbortController().signal, + agent: agent.name, messageID: userMsg.id, metadata: async () => {}, }), @@ -765,7 +766,7 @@ export namespace Session { const enabledTools = pipe( agent.tools, - mergeDeep(await ToolRegistry.enabled(input.providerID, input.modelID)), + mergeDeep(await ToolRegistry.enabled(input.providerID, input.modelID, agent)), mergeDeep(input.tools ?? {}), ) for (const item of await ToolRegistry.tools(input.providerID, input.modelID)) { @@ -791,6 +792,7 @@ export namespace Session { abort: options.abortSignal!, messageID: assistantMsg.id, callID: options.toolCallId, + agent: agent.name, metadata: async (val) => { const match = processor.partFromToolCall(options.toolCallId) if (match && match.state.status === "running") { diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index f6368ea14..b6266dc22 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -5,12 +5,12 @@ import { Tool } from "./tool" import DESCRIPTION from "./bash.txt" import { App } from "../app/app" import { Permission } from "../permission" -import { Config } from "../config/config" import { Filesystem } from "../util/filesystem" import { lazy } from "../util/lazy" import { Log } from "../util/log" import { Wildcard } from "../util/wildcard" import { $ } from "bun" +import { Agent } from "../agent/agent" const MAX_OUTPUT_LENGTH = 30000 const DEFAULT_TIMEOUT = 1 * 60 * 1000 @@ -40,20 +40,8 @@ export const BashTool = Tool.define("bash", { async execute(params, ctx) { const timeout = Math.min(params.timeout ?? DEFAULT_TIMEOUT, MAX_TIMEOUT) const app = App.info() - const cfg = await Config.get() const tree = await parser().then((p) => p.parse(params.command)) - const permissions = (() => { - const value = cfg.permission?.bash - if (!value) - return { - "*": "allow", - } - if (typeof value === "string") - return { - "*": value, - } - return value - })() + const permissions = await Agent.get(ctx.agent).then((x) => x.permission.bash) let needsAsk = false for (const node of tree.rootNode.descendantsOfType("command")) { @@ -93,17 +81,10 @@ export const BashTool = Tool.define("bash", { // always allow cd if it passes above check if (!needsAsk && command[0] !== "cd") { - const action = (() => { - for (const [pattern, value] of Object.entries(permissions)) { - const match = Wildcard.match(node.text, pattern) - log.info("checking", { text: node.text.trim(), pattern, match }) - if (match) return value - } - return "ask" - })() + const action = Wildcard.all(node.text, permissions) if (action === "deny") { throw new Error( - "The user has specifically restricted access to this command, you are not allowed to execute it.", + `The user has specifically restricted access to this command, you are not allowed to execute it. Here is the configuration: ${JSON.stringify(permissions)}`, ) } if (action === "ask") needsAsk = true diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts index 1b31b0aa4..8c3bdc63d 100644 --- a/packages/opencode/src/tool/edit.ts +++ b/packages/opencode/src/tool/edit.ts @@ -14,8 +14,8 @@ import { App } from "../app/app" import { File } from "../file" import { Bus } from "../bus" import { FileTime } from "../file/time" -import { Config } from "../config/config" import { Filesystem } from "../util/filesystem" +import { Agent } from "../agent/agent" export const EditTool = Tool.define("edit", { description: DESCRIPTION, @@ -40,7 +40,7 @@ export const EditTool = Tool.define("edit", { throw new Error(`File ${filePath} is not in the current working directory`) } - const cfg = await Config.get() + const agent = await Agent.get(ctx.agent) let diff = "" let contentOld = "" let contentNew = "" @@ -48,7 +48,7 @@ export const EditTool = Tool.define("edit", { if (params.oldString === "") { contentNew = params.newString diff = trimDiff(createTwoFilesPatch(filePath, filePath, contentOld, contentNew)) - if (cfg.permission?.edit === "ask") { + if (agent.permission.edit === "ask") { await Permission.ask({ type: "edit", sessionID: ctx.sessionID, @@ -77,7 +77,7 @@ export const EditTool = Tool.define("edit", { contentNew = replace(contentOld, params.oldString, params.newString, params.replaceAll) diff = trimDiff(createTwoFilesPatch(filePath, filePath, contentOld, contentNew)) - if (cfg.permission?.edit === "ask") { + if (agent.permission.edit === "ask") { await Permission.ask({ type: "edit", sessionID: ctx.sessionID, diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index c2fe5943b..e79380579 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -11,7 +11,7 @@ import { TodoWriteTool, TodoReadTool } from "./todo" import { WebFetchTool } from "./webfetch" import { WriteTool } from "./write" import { InvalidTool } from "./invalid" -import { Config } from "../config/config" +import type { Agent } from "../agent/agent" export namespace ToolRegistry { const ALL = [ @@ -66,20 +66,23 @@ export namespace ToolRegistry { return result } - export async function enabled(_providerID: string, _modelID: string): Promise> { - const cfg = await Config.get() + export async function enabled( + _providerID: string, + _modelID: string, + agent: Agent.Info, + ): Promise> { const result: Record = {} result["patch"] = false - if (cfg.permission?.edit === "deny") { + if (agent.permission.edit === "deny") { result["edit"] = false result["patch"] = false result["write"] = false } - if (cfg?.permission?.bash === "deny") { + if (agent.permission.bash["*"] === "deny" && Object.keys(agent.permission.bash).length === 1) { result["bash"] = false } - if (cfg?.permission?.webfetch === "deny") { + if (agent.permission.webfetch === "deny") { result["webfetch"] = false } diff --git a/packages/opencode/src/tool/tool.ts b/packages/opencode/src/tool/tool.ts index 1c71b9a77..8be3d0cde 100644 --- a/packages/opencode/src/tool/tool.ts +++ b/packages/opencode/src/tool/tool.ts @@ -7,6 +7,7 @@ export namespace Tool { export type Context = { sessionID: string messageID: string + agent: string callID?: string abort: AbortSignal metadata(input: { title?: string; metadata?: M }): void diff --git a/packages/opencode/src/tool/write.ts b/packages/opencode/src/tool/write.ts index 5b0028f84..951ad730e 100644 --- a/packages/opencode/src/tool/write.ts +++ b/packages/opencode/src/tool/write.ts @@ -8,8 +8,8 @@ import { App } from "../app/app" import { Bus } from "../bus" import { File } from "../file" import { FileTime } from "../file/time" -import { Config } from "../config/config" import { Filesystem } from "../util/filesystem" +import { Agent } from "../agent/agent" export const WriteTool = Tool.define("write", { description: DESCRIPTION, @@ -28,8 +28,8 @@ export const WriteTool = Tool.define("write", { const exists = await file.exists() if (exists) await FileTime.assert(ctx.sessionID, filepath) - const cfg = await Config.get() - if (cfg.permission?.edit === "ask") + const agent = await Agent.get(ctx.agent) + if (agent.permission.edit === "ask") await Permission.ask({ type: "write", sessionID: ctx.sessionID, diff --git a/packages/opencode/test/tool/bash.test.ts b/packages/opencode/test/tool/bash.test.ts index e19949d6a..43277dfa2 100644 --- a/packages/opencode/test/tool/bash.test.ts +++ b/packages/opencode/test/tool/bash.test.ts @@ -8,6 +8,7 @@ const ctx = { sessionID: "test", messageID: "", toolCallID: "", + agent: "build", abort: AbortSignal.any([]), metadata: () => {}, } @@ -33,7 +34,7 @@ describe("tool.bash", () => { test("cd ../ should fail outside of project root", async () => { await App.provide({ cwd: projectRoot }, async () => { - await expect( + expect( bash.execute( { command: "cd ../", diff --git a/packages/opencode/test/tool/tool.test.ts b/packages/opencode/test/tool/tool.test.ts index a0f7ce909..313aa4242 100644 --- a/packages/opencode/test/tool/tool.test.ts +++ b/packages/opencode/test/tool/tool.test.ts @@ -8,6 +8,7 @@ const ctx = { sessionID: "test", messageID: "", toolCallID: "", + agent: "build", abort: AbortSignal.any([]), metadata: () => {}, } diff --git a/packages/web/src/content/docs/docs/agents.mdx b/packages/web/src/content/docs/docs/agents.mdx index bcb0eca2a..beb1b29ae 100644 --- a/packages/web/src/content/docs/docs/agents.mdx +++ b/packages/web/src/content/docs/docs/agents.mdx @@ -358,6 +358,147 @@ Here are all the tools can be controlled through the agent config. --- +### Permissions + +Permissions control what actions an agent can take. + +- edit, bash, webfetch + +Each permission can be set to allow, ask, or deny. + +- allow, ask, deny + +Configure permissions globally in opencode.json. + +```json title="opencode.json" +{ + "$schema": "https://opencode.ai/config.json", + "permission": { + "edit": "ask", + "bash": "allow", + "webfetch": "deny" + } +} +``` + +You can override permissions per agent in JSON. + +```json title="opencode.json" {7-18} +{ + "$schema": "https://opencode.ai/config.json", + "agent": { + "build": { + "permission": { + "edit": "allow", + "bash": { + "*": "allow", + "git push": "ask", + "terraform *": "deny" + }, + "webfetch": "ask" + } + } + } +} +``` + +You can also set permissions in Markdown agents. + +```markdown title="~/.config/opencode/agent/review.md" +--- +description: Code review without edits +mode: subagent +permission: + edit: deny + bash: ask + webfetch: deny +--- + +Only analyze code and suggest changes. +``` + +Bash permissions support granular patterns for fine-grained control. + +```json title="Allow most, ask for risky, deny terraform" +{ + "$schema": "https://opencode.ai/config.json", + "permission": { + "bash": { + "*": "allow", + "git push": "ask", + "terraform *": "deny" + } + } +} +``` + +If you provide a granular bash map, the default becomes ask unless you set \* explicitly. + +```json title="Granular defaults to ask" +{ + "$schema": "https://opencode.ai/config.json", + "permission": { + "bash": { + "git status": "allow" + } + } +} +``` + +Agent-level permissions merge over global settings. + +- Global sets defaults; agent overrides when specified + +Specific bash rules can override a global default. + +```json title="Global ask, agent allows safe commands" +{ + "$schema": "https://opencode.ai/config.json", + "permission": { "bash": "ask" }, + "agent": { + "build": { + "permission": { + "bash": { "git status": "allow", "*": "ask" } + } + } + } +} +``` + +Permissions affect tool availability and prompts differently. + +- deny hides tools (edit also hides write/patch); ask prompts; allow runs + +For quick reference, here are common setups. + +```json title="Read-only reviewer" +{ + "$schema": "https://opencode.ai/config.json", + "agent": { + "review": { + "permission": { "edit": "deny", "bash": "deny", "webfetch": "allow" } + } + } +} +``` + +```json title="Planning agent that can browse but cannot change code" +{ + "$schema": "https://opencode.ai/config.json", + "agent": { + "plan": { + "permission": { "edit": "deny", "bash": "deny", "webfetch": "ask" } + } + } +} +``` + +See the full permissions guide for more patterns. + +- /docs/permissions + +--- + ### Mode Control the agent's mode with the `mode` config. The `mode` option is used to determine how the agent can be used. diff --git a/packages/web/src/content/docs/docs/permissions.mdx b/packages/web/src/content/docs/docs/permissions.mdx index 2ac7b58a1..44dbc92ef 100644 --- a/packages/web/src/content/docs/docs/permissions.mdx +++ b/packages/web/src/content/docs/docs/permissions.mdx @@ -21,6 +21,8 @@ Permissions are configured in your `opencode.json` file under the `permission` k | `bash` | Control bash command execution | | `webfetch` | Control web content fetching | +They can also be configured per agent, see [Agent Configuration](/docs/agents#agent-configuration) for more details. + --- ### edit From 354e55ecefa28433b77e36907ff9c13db93fcc57 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 12:12:54 -0400 Subject: [PATCH 136/178] ci: ignore --- .github/workflows/duplicate-issues.yml | 1 - .github/workflows/guidelines-check.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml index ba9577dbb..865cd3123 100644 --- a/.github/workflows/duplicate-issues.yml +++ b/.github/workflows/duplicate-issues.yml @@ -10,7 +10,6 @@ jobs: permissions: contents: read issues: write - id-token: write steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 9f4915f58..454f4816c 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -10,7 +10,6 @@ jobs: permissions: contents: read pull-requests: write - id-token: write steps: - name: Checkout repository uses: actions/checkout@v4 From 5a17f44da4847f92fd153d825d586a5ab0887048 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 12:28:08 -0400 Subject: [PATCH 137/178] support OPENCODE_PERMISSION json env variable --- packages/opencode/src/config/config.ts | 4 ++++ packages/opencode/src/flag/flag.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index a8d84d8bc..8a373d6f7 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -110,6 +110,10 @@ export namespace Config { ].map((x) => "file://" + x), ) + if (Flag.OPENCODE_PERMISSION) { + result.permission = mergeDeep(result.permission ?? {}, JSON.parse(Flag.OPENCODE_PERMISSION)) + } + // Handle migration from autoshare to share field if (result.autoshare === true && !result.share) { result.share = "auto" diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts index b39ce6a9e..38253d729 100644 --- a/packages/opencode/src/flag/flag.ts +++ b/packages/opencode/src/flag/flag.ts @@ -3,6 +3,7 @@ export namespace Flag { export const OPENCODE_DISABLE_WATCHER = truthy("OPENCODE_DISABLE_WATCHER") export const OPENCODE_CONFIG = process.env["OPENCODE_CONFIG"] export const OPENCODE_DISABLE_AUTOUPDATE = truthy("OPENCODE_DISABLE_AUTOUPDATE") + export const OPENCODE_PERMISSION = process.env["OPENCODE_PERMISSION"] function truthy(key: string) { const value = process.env[key]?.toLowerCase() From 2c2752ee02e1c227b7827fcea2797a8cf31daa60 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 12:33:10 -0400 Subject: [PATCH 138/178] ci: ignore --- .github/workflows/duplicate-issues.yml | 3 ++- .github/workflows/guidelines-check.yml | 3 ++- .opencode/agent/github.md | 13 ------------- 3 files changed, 4 insertions(+), 15 deletions(-) delete mode 100644 .opencode/agent/github.md diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml index 865cd3123..30a15a626 100644 --- a/.github/workflows/duplicate-issues.yml +++ b/.github/workflows/duplicate-issues.yml @@ -23,8 +23,9 @@ jobs: env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OPENCODE_PERMISSION: { "bash": { "gh*": "allow", "*": "deny" } } run: | - opencode run --agent github -m anthropic/claude-sonnet-4-20250514 "A new issue has been created: '${{ github.event.issue.title }}' + opencode run -m anthropic/claude-sonnet-4-20250514 "A new issue has been created: '${{ github.event.issue.title }}' Issue body: ${{ github.event.issue.body }} diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 454f4816c..18a36830b 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -23,8 +23,9 @@ jobs: env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OPENCODE_PERMISSION: { "bash": { "gh*": "allow", "*": "deny" } } run: | - opencode run --agent github -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' + opencode run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' PR description: ${{ github.event.pull_request.body }} diff --git a/.opencode/agent/github.md b/.opencode/agent/github.md deleted file mode 100644 index da3aa4518..000000000 --- a/.opencode/agent/github.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -permission: - bash: - "*": "deny" - "gh*": "allow" -mode: subagent ---- - -You are running in github actions, typically to evaluate a PR. Do not do -anything that is outside the scope of that. You have access to the bash tool but -you can only run `gh` cli commands with it. - -Diffs are important but be sure to read the whole file to get the full context. From 4178b3c6ae9541fbca5b83288bc5b479fbb64fa3 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 12:53:55 -0400 Subject: [PATCH 139/178] ci: sync --- .github/workflows/duplicate-issues.yml | 2 +- .github/workflows/guidelines-check.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml index 30a15a626..863635ed8 100644 --- a/.github/workflows/duplicate-issues.yml +++ b/.github/workflows/duplicate-issues.yml @@ -23,7 +23,7 @@ jobs: env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: { "bash": { "gh*": "allow", "*": "deny" } } + OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "*": "deny" } }' run: | opencode run -m anthropic/claude-sonnet-4-20250514 "A new issue has been created: '${{ github.event.issue.title }}' diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 18a36830b..36fe3b8db 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -23,7 +23,7 @@ jobs: env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: { "bash": { "gh*": "allow", "*": "deny" } } + OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "*": "deny" } }' run: | opencode run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' From ee2b57958d6edaf36f680176e8ecb65623955923 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 12:58:18 -0400 Subject: [PATCH 140/178] ci: disable aur --- packages/opencode/script/publish.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts index b0520fb5a..3ce750d3c 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -97,6 +97,7 @@ if (!snapshot) { const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) + /* // AUR package const pkgbuild = [ "# Maintainer: dax", @@ -136,6 +137,7 @@ if (!snapshot) { await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${version}"` if (!dry) await $`cd ./dist/aur-${pkg} && git push` } + */ // Homebrew formula const homebrewFormula = [ From dd0c0491192e0ed35b3854a96e08487b9014331a Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 13:01:45 -0400 Subject: [PATCH 141/178] ci: tweak --- .github/workflows/publish.yml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 97b943c95..403f78e97 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -48,19 +48,20 @@ jobs: restore-keys: | ${{ runner.os }}-bun- - - name: Install makepkg - run: | - sudo apt-get update - sudo apt-get install -y pacman-package-manager + # Temporarily disabled AUR steps + # - name: Install makepkg + # run: | + # sudo apt-get update + # sudo apt-get install -y pacman-package-manager - - name: Setup SSH for AUR - run: | - mkdir -p ~/.ssh - echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts - git config --global user.email "opencode@sst.dev" - git config --global user.name "opencode" + # - name: Setup SSH for AUR + # run: | + # mkdir -p ~/.ssh + # echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa + # chmod 600 ~/.ssh/id_rsa + # ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts + # git config --global user.email "opencode@sst.dev" + # git config --global user.name "opencode" - name: Install dependencies run: bun install From be13e71fb93d9102650b77f8ea8a3f23fb7fad8d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 13:09:24 -0400 Subject: [PATCH 142/178] ci: sync --- .github/workflows/publish.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 403f78e97..f8f2146a2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -54,15 +54,14 @@ jobs: # sudo apt-get update # sudo apt-get install -y pacman-package-manager - # - name: Setup SSH for AUR - # run: | - # mkdir -p ~/.ssh - # echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa - # chmod 600 ~/.ssh/id_rsa + - name: Setup SSH for AUR + run: | + mkdir -p ~/.ssh + echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + git config --global user.email "opencode@sst.dev" + git config --global user.name "opencode" # ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts - # git config --global user.email "opencode@sst.dev" - # git config --global user.name "opencode" - - name: Install dependencies run: bun install From 3a2b2f13f252e17237dcd4ccce24b1694f6a46f6 Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 17:14:23 +0000 Subject: [PATCH 143/178] release: v0.4.34 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/sdk/js/src/gen/types.gen.ts | 25 +++++++++++++++++++++++++ packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 10 files changed, 34 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 48d8293e0..6f000bdca 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.29", + "version": "0.4.34", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 6ceeb2467..531a2d254 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.29", + "version": "0.4.34", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 0d3561147..d32ea217f 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.29", + "version": "0.4.34", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 5a70453fb..cd046e38d 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.29", + "version": "0.4.34", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index cf4f50329..3f17edbc2 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.29", + "version": "0.4.34", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index d3c93687f..e89753e9c 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.29", + "version": "0.4.34", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index c63d1d1fa..7ddc86d5a 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.29", + "version": "0.4.34", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 5bc67b1d8..ddf7ac08b 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -912,6 +912,15 @@ export type AgentConfig = { */ description?: string mode?: string + permission?: { + edit?: string + bash?: + | string + | { + [key: string]: string + } + webfetch?: string + } [key: string]: | unknown | string @@ -921,6 +930,15 @@ export type AgentConfig = { } | boolean | string + | { + edit?: string + bash?: + | string + | { + [key: string]: string + } + webfetch?: string + } | undefined } @@ -1061,6 +1079,13 @@ export type Agent = { mode: string topP?: number temperature?: number + permission: { + edit: string + bash: { + [key: string]: string + } + webfetch?: string + } model?: { modelID: string providerID: string diff --git a/packages/web/package.json b/packages/web/package.json index e974b291d..af85420c6 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.29", + "version": "0.4.34", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index e34df276a..b1f3c1190 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.29", + "version": "0.4.34", "publisher": "sst-dev", "repository": { "type": "git", From 6f584ec6410f539d602620c0961741dd06db70f2 Mon Sep 17 00:00:00 2001 From: Dax Date: Tue, 12 Aug 2025 13:23:19 -0400 Subject: [PATCH 144/178] ci: improve guideline check (#1867) --- .github/workflows/guidelines-check.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 36fe3b8db..c9a776715 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -27,10 +27,15 @@ jobs: run: | opencode run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' - PR description: - ${{ github.event.pull_request.body }} + + ${{ github.event.pull_request.number }} + - Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. + + ${{ github.event.pull_request.body }} + + + Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. Diffs are important but make sure you read the entire file to get proper context. For each violation you find, create a file comment using the gh CLI. Use this exact format for each violation: From 61105b487f395bf23f01413d64f3b1773b50bb5f Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 13:25:05 -0400 Subject: [PATCH 145/178] ci: ignore --- .github/workflows/guidelines-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index c9a776715..039c3c69a 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -25,7 +25,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "*": "deny" } }' run: | - opencode run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' + opencode --print-logs run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' ${{ github.event.pull_request.number }} From fab0e5de04f3a7a46ccd95effce6d203ed48843f Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 13:30:52 -0400 Subject: [PATCH 146/178] fix issue when @ tagging fiels throwing error --- packages/opencode/src/session/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index b881161a2..182e16170 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -523,7 +523,7 @@ export namespace Session { t.execute(args, { sessionID: input.sessionID, abort: new AbortController().signal, - agent: agent.name, + agent: input.agent!, messageID: userMsg.id, metadata: async () => {}, }), From e32b6e2cc2d832281bc059c974da917c9cac0249 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 13:31:23 -0400 Subject: [PATCH 147/178] ci: ignore --- .github/workflows/guidelines-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 039c3c69a..8fe1d7595 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -1,7 +1,7 @@ name: Guidelines Check on: - pull_request: + pull_request_target: types: [opened, synchronize] jobs: From 3a9dd306dbdb4633dd31e046e56ad2cf26497b0f Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 17:36:48 +0000 Subject: [PATCH 148/178] release: v0.4.35 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 6f000bdca..da2582ada 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.34", + "version": "0.4.35", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 531a2d254..2c417c04e 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.34", + "version": "0.4.35", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index d32ea217f..079d45fe1 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.34", + "version": "0.4.35", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index cd046e38d..7d500b77c 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.34", + "version": "0.4.35", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 3f17edbc2..de20e2793 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.34", + "version": "0.4.35", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index e89753e9c..61c285cfb 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.34", + "version": "0.4.35", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 7ddc86d5a..4c393e834 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.34", + "version": "0.4.35", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index af85420c6..704be3458 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.34", + "version": "0.4.35", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index b1f3c1190..12295fb48 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.34", + "version": "0.4.35", "publisher": "sst-dev", "repository": { "type": "git", From 5141fe8d7d7c6bd9630b9c2468aceaf807765fb6 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 13:33:55 -0400 Subject: [PATCH 149/178] ci: sync --- .github/workflows/guidelines-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 8fe1d7595..c041f4c17 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -25,7 +25,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "*": "deny" } }' run: | - opencode --print-logs run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' + opencode run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' ${{ github.event.pull_request.number }} From 342f4239e425f45dea8fa139d91d3263c9af3e88 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 13:36:56 -0400 Subject: [PATCH 150/178] ci: softer language --- .github/workflows/guidelines-check.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index c041f4c17..cb3c9517e 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -40,13 +40,13 @@ jobs: For each violation you find, create a file comment using the gh CLI. Use this exact format for each violation: \`\`\`bash - gh pr review ${{ github.event.pull_request.number }} --comment-body 'This violates the AGENTS.md guideline: [specific rule]. Consider: [suggestion]' --file 'path/to/file.ts' --line [line_number] + gh pr review ${{ github.event.pull_request.number }} --comment-body 'This may be a conflict with AGENTS.md guidelines: [specific rule]. Consider: [suggestion]' --file 'path/to/file.ts' --line [line_number] \`\`\` When possible, also submit code change suggestions using: \`\`\`bash - gh pr review ${{ github.event.pull_request.number }} --comment-body 'Suggested fix for AGENTS.md guideline violation:' --file 'path/to/file.ts' --line [line_number] --body '```suggestion + gh pr review ${{ github.event.pull_request.number }} --comment-body 'Suggested fix for AGENTS.md guideline:' --file 'path/to/file.ts' --line [line_number] --body '```suggestion [corrected code here] ```' \`\`\` From f62d826037990df7702dfe9ed4aed634bd393f40 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 13:42:46 -0400 Subject: [PATCH 151/178] ci: sync --- .github/workflows/guidelines-check.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index cb3c9517e..b5e0fa821 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -37,18 +37,14 @@ jobs: Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. Diffs are important but make sure you read the entire file to get proper context. - For each violation you find, create a file comment using the gh CLI. Use this exact format for each violation: + For each violation you find, create a file comment using the gh CLI with the suggested fix. \`\`\`bash - gh pr review ${{ github.event.pull_request.number }} --comment-body 'This may be a conflict with AGENTS.md guidelines: [specific rule]. Consider: [suggestion]' --file 'path/to/file.ts' --line [line_number] - \`\`\` - - When possible, also submit code change suggestions using: - - \`\`\`bash - gh pr review ${{ github.event.pull_request.number }} --comment-body 'Suggested fix for AGENTS.md guideline:' --file 'path/to/file.ts' --line [line_number] --body '```suggestion + gh pr review ${{ github.event.pull_request.number }} --comment-body '[description-of-problem]' --file 'path/to/file.ts' --line [line_number] --body '```suggestion [corrected code here] ```' \`\`\` + If you do not have a fix you can just lave a comment without the suggestion. + Only create comments for actual violations. If the code follows all guidelines, don't run any gh commands." From e78fe8dcba454510557fa97c273472ab18d2addd Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 13:53:32 -0400 Subject: [PATCH 152/178] ci: tweak --- .github/workflows/guidelines-check.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index b5e0fa821..5a098c4a1 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -37,14 +37,17 @@ jobs: Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. Diffs are important but make sure you read the entire file to get proper context. - For each violation you find, create a file comment using the gh CLI with the suggested fix. + For each violation you find, create a file comment using the gh CLI - \`\`\`bash - gh pr review ${{ github.event.pull_request.number }} --comment-body '[description-of-problem]' --file 'path/to/file.ts' --line [line_number] --body '```suggestion - [corrected code here] - ```' + ``` + gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments -f body='[description of problem]' -f path='[path-to-file]' -F line=[line-number] -f side='RIGHT' + ``` + + If you have a suggestion for how to fix the problem the body can include a suggestion: + ``` + \`\`\`suggestion + [suggested code here] \`\`\` - - If you do not have a fix you can just lave a comment without the suggestion. + ``` Only create comments for actual violations. If the code follows all guidelines, don't run any gh commands." From 6d1b6a6fb172842a0d1e835a4f1345a4947ba653 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 13:54:53 -0400 Subject: [PATCH 153/178] ci: ignore --- .github/workflows/guidelines-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 5a098c4a1..4c5543719 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -37,7 +37,7 @@ jobs: Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. Diffs are important but make sure you read the entire file to get proper context. - For each violation you find, create a file comment using the gh CLI + For each violation you find, create a file comment using the gh CLI - make it clear these are merely suggestions and the human can make a call on what to do. ``` gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments -f body='[description of problem]' -f path='[path-to-file]' -F line=[line-number] -f side='RIGHT' From 834aa036a4c4b743f4e979118c0e83904faee958 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 13:58:51 -0400 Subject: [PATCH 154/178] ci: sync --- .github/workflows/guidelines-check.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 4c5543719..39929896a 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -35,12 +35,12 @@ jobs: ${{ github.event.pull_request.body }} - Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. Diffs are important but make sure you read the entire file to get proper context. + Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. Diffs are important but make sure you read the entire file to get proper context. Make it clear the suggestions are merely suggestions and the human can decide what to do - For each violation you find, create a file comment using the gh CLI - make it clear these are merely suggestions and the human can make a call on what to do. + For each violation you find, create a file comment using the following command: ``` - gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments -f body='[description of problem]' -f path='[path-to-file]' -F line=[line-number] -f side='RIGHT' + gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments -f body='[description of problem]' -f path='[path-to-file]' -f line=[line-number] -f side='RIGHT' ``` If you have a suggestion for how to fix the problem the body can include a suggestion: From 4cddda3e16ec71031f4c656d2e3dd820d0c6284a Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 14:00:30 -0400 Subject: [PATCH 155/178] ci: sync --- .github/workflows/guidelines-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 39929896a..7cec751fe 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -37,7 +37,7 @@ jobs: Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. Diffs are important but make sure you read the entire file to get proper context. Make it clear the suggestions are merely suggestions and the human can decide what to do - For each violation you find, create a file comment using the following command: + For each violation you find, create a file comment using the following command EXACTLY as shown: ``` gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments -f body='[description of problem]' -f path='[path-to-file]' -f line=[line-number] -f side='RIGHT' From aae354c9513c8f36e770ff805e75585fb152593c Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Tue, 12 Aug 2025 13:03:26 -0500 Subject: [PATCH 156/178] fix: word wrapping with hyphens --- packages/tui/internal/components/chat/message.go | 4 +++- packages/tui/internal/util/file.go | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go index 1bbf3c5ca..2d762dfa9 100644 --- a/packages/tui/internal/components/chat/message.go +++ b/packages/tui/internal/components/chat/message.go @@ -325,7 +325,9 @@ func renderText( // wrap styled text styledText := result.String() - wrappedText := ansi.WordwrapWc(styledText, width-6, " -") + styledText = strings.ReplaceAll(styledText, "-", "\u2011") + wrappedText := ansi.WordwrapWc(styledText, width-6, " ") + wrappedText = strings.ReplaceAll(wrappedText, "\u2011", "-") content = base.Width(width - 6).Render(wrappedText) } diff --git a/packages/tui/internal/util/file.go b/packages/tui/internal/util/file.go index b079f24cd..943f784fc 100644 --- a/packages/tui/internal/util/file.go +++ b/packages/tui/internal/util/file.go @@ -85,6 +85,7 @@ func Extension(path string) string { func ToMarkdown(content string, width int, backgroundColor compat.AdaptiveColor) string { r := styles.GetMarkdownRenderer(width-6, backgroundColor) content = strings.ReplaceAll(content, RootPath+"/", "") + content = strings.ReplaceAll(content, "-", "\u2011") rendered, _ := r.Render(content) lines := strings.Split(rendered, "\n") @@ -105,5 +106,6 @@ func ToMarkdown(content string, width int, backgroundColor compat.AdaptiveColor) } } content = strings.Join(lines, "\n") + content = strings.ReplaceAll(content, "\u2011", "-") return strings.TrimSuffix(content, "\n") } From 45451095f70196f532763c5684d6bc79cc24d288 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 14:10:44 -0400 Subject: [PATCH 157/178] ci: sync --- .github/workflows/guidelines-check.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 7cec751fe..0c424ad22 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -37,17 +37,6 @@ jobs: Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. Diffs are important but make sure you read the entire file to get proper context. Make it clear the suggestions are merely suggestions and the human can decide what to do - For each violation you find, create a file comment using the following command EXACTLY as shown: - - ``` - gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments -f body='[description of problem]' -f path='[path-to-file]' -f line=[line-number] -f side='RIGHT' - ``` - - If you have a suggestion for how to fix the problem the body can include a suggestion: - ``` - \`\`\`suggestion - [suggested code here] - \`\`\` - ``` + Use the gh cli to create comments on the files for the violations. Try to leave the comment on the exact line number. If you have a suggested fix include it in a suggestion code block. Only create comments for actual violations. If the code follows all guidelines, don't run any gh commands." From 596d4e449003f06b665607110b6bf0d965777746 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 14:17:18 -0400 Subject: [PATCH 158/178] ci: sync --- .github/workflows/guidelines-check.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index 0c424ad22..a21f0be19 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -39,4 +39,14 @@ jobs: Use the gh cli to create comments on the files for the violations. Try to leave the comment on the exact line number. If you have a suggested fix include it in a suggestion code block. + Command should be like this + ``` + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments \ + -f 'body=[summary of issue]' -f 'commit_id=${{ github.event.pull_request.head.sha }}' -f 'path=[path-to-file]' -F "line=[line]" -f 'side=RIGHT' + ``` + Only create comments for actual violations. If the code follows all guidelines, don't run any gh commands." From e9cb360cb7e6a72be187a78358428182c34d37ab Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 14:21:34 -0400 Subject: [PATCH 159/178] ci: sync --- .github/workflows/guidelines-check.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index a21f0be19..cce45623f 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -23,7 +23,7 @@ jobs: env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "*": "deny" } }' + OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "gh review*": "deny", "*": "deny" } }' run: | opencode run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' @@ -39,7 +39,7 @@ jobs: Use the gh cli to create comments on the files for the violations. Try to leave the comment on the exact line number. If you have a suggested fix include it in a suggestion code block. - Command should be like this + Command MUST be like this. ``` gh api \ --method POST \ From c4ae3e429cd69388d9cde11fa79a8b5d99f04f78 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Tue, 12 Aug 2025 13:22:19 -0500 Subject: [PATCH 160/178] fix: markdown lists --- packages/tui/internal/util/file.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/tui/internal/util/file.go b/packages/tui/internal/util/file.go index 943f784fc..879e8a6ab 100644 --- a/packages/tui/internal/util/file.go +++ b/packages/tui/internal/util/file.go @@ -3,6 +3,7 @@ package util import ( "fmt" "path/filepath" + "regexp" "strings" "unicode" @@ -85,7 +86,8 @@ func Extension(path string) string { func ToMarkdown(content string, width int, backgroundColor compat.AdaptiveColor) string { r := styles.GetMarkdownRenderer(width-6, backgroundColor) content = strings.ReplaceAll(content, RootPath+"/", "") - content = strings.ReplaceAll(content, "-", "\u2011") + hyphenRegex := regexp.MustCompile(`-([^ ]|$)`) + content = hyphenRegex.ReplaceAllString(content, "\u2011$1") rendered, _ := r.Render(content) lines := strings.Split(rendered, "\n") From 13d4a802ac2c4d7932c51fb8a8096d802b2980a3 Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 18:31:12 +0000 Subject: [PATCH 161/178] release: v0.4.36 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index da2582ada..6fd361d10 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.35", + "version": "0.4.36", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 2c417c04e..394c3804b 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.35", + "version": "0.4.36", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 079d45fe1..8a00eda85 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.35", + "version": "0.4.36", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 7d500b77c..1b2e996bd 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.35", + "version": "0.4.36", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index de20e2793..85d729cd7 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.35", + "version": "0.4.36", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 61c285cfb..6309565f5 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.35", + "version": "0.4.36", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 4c393e834..bb7430ef0 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.35", + "version": "0.4.36", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index 704be3458..22c19e802 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.35", + "version": "0.4.36", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 12295fb48..fbbd9bc64 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.35", + "version": "0.4.36", "publisher": "sst-dev", "repository": { "type": "git", From b289fd9dc730055e27d5698b887bc49f9cc7bcc2 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 14:25:57 -0400 Subject: [PATCH 162/178] ci: sync --- .github/workflows/guidelines-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index cce45623f..c740bd000 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -23,7 +23,7 @@ jobs: env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "gh review*": "deny", "*": "deny" } }' + OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "gh pr review*": "deny", "*": "deny" } }' run: | opencode run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}' From 9fff9a37d09796c0a438d30b21205e2e147986b2 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 14:38:58 -0400 Subject: [PATCH 163/178] ci: sync --- .github/workflows/guidelines-check.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/guidelines-check.yml b/.github/workflows/guidelines-check.yml index c740bd000..b4da51c93 100644 --- a/.github/workflows/guidelines-check.yml +++ b/.github/workflows/guidelines-check.yml @@ -1,8 +1,9 @@ name: Guidelines Check on: - pull_request_target: - types: [opened, synchronize] + # Disabled - uncomment to re-enable + # pull_request_target: + # types: [opened, synchronize] jobs: check-guidelines: From 6aa157cfe6195effa6f9fea3dc18380a48544489 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 14:51:13 -0400 Subject: [PATCH 164/178] limit bash tool to 1000 lines of output --- packages/opencode/src/tool/bash.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index b6266dc22..4e54d798a 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -12,7 +12,6 @@ import { Wildcard } from "../util/wildcard" import { $ } from "bun" import { Agent } from "../agent/agent" -const MAX_OUTPUT_LENGTH = 30000 const DEFAULT_TIMEOUT = 1 * 60 * 1000 const MAX_TIMEOUT = 10 * 60 * 1000 @@ -107,7 +106,6 @@ export const BashTool = Tool.define("bash", { const process = exec(params.command, { cwd: app.path.cwd, signal: ctx.abort, - maxBuffer: MAX_OUTPUT_LENGTH, timeout, }) @@ -155,6 +153,12 @@ export const BashTool = Tool.define("bash", { }, }) + const lines = output.split("\n") + if (lines.length > 1000) { + output = lines.slice(0, 1000).join("\n") + output += "\n\n(Ouput was truncated)" + } + return { title: params.command, metadata: { From cadc5982f1f72ad2d5be26f67edf1b5be7cddf12 Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 18:56:25 +0000 Subject: [PATCH 165/178] release: v0.4.37 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 6fd361d10..820fc9638 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.36", + "version": "0.4.37", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 394c3804b..0840edc51 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.36", + "version": "0.4.37", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 8a00eda85..29984fded 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.36", + "version": "0.4.37", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index 1b2e996bd..d04f68b74 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.36", + "version": "0.4.37", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 85d729cd7..8ca1ba14f 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.36", + "version": "0.4.37", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 6309565f5..f302861b5 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.36", + "version": "0.4.37", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index bb7430ef0..741434ffe 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.36", + "version": "0.4.37", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/web/package.json b/packages/web/package.json index 22c19e802..e5704e9a9 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.36", + "version": "0.4.37", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index fbbd9bc64..181761621 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.36", + "version": "0.4.37", "publisher": "sst-dev", "repository": { "type": "git", From 5cc44c872e829a6dd2725a42f47d2fb370bb9cbd Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 14:54:25 -0400 Subject: [PATCH 166/178] disable todo tools for qwen models to improve compatibility --- packages/opencode/src/tool/registry.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index e79380579..9e8f9638c 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -68,7 +68,7 @@ export namespace ToolRegistry { export async function enabled( _providerID: string, - _modelID: string, + modelID: string, agent: Agent.Info, ): Promise> { const result: Record = {} @@ -86,6 +86,11 @@ export namespace ToolRegistry { result["webfetch"] = false } + if (modelID.includes("qwen")) { + result["todowrite"] = false + result["todoread"] = false + } + return result } From 14e00a06b6885dd10ebd7dbb9ecd6a7f4a655a52 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 15:09:10 -0400 Subject: [PATCH 167/178] ci: sync --- .github/workflows/duplicate-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml index 863635ed8..1af84ef26 100644 --- a/.github/workflows/duplicate-issues.yml +++ b/.github/workflows/duplicate-issues.yml @@ -23,7 +23,7 @@ jobs: env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "*": "deny" } }' + OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "*": "deny" }, "webfetch": "deny" }' run: | opencode run -m anthropic/claude-sonnet-4-20250514 "A new issue has been created: '${{ github.event.issue.title }}' From d16ae1fc4e6732dfb0d497f389f10c5ab04c253a Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 16:14:40 -0400 Subject: [PATCH 168/178] bash truncate character max instead of line max --- packages/opencode/src/tool/bash.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index 4e54d798a..44106b2e9 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -12,6 +12,7 @@ import { Wildcard } from "../util/wildcard" import { $ } from "bun" import { Agent } from "../agent/agent" +const MAX_OUTPUT_LENGTH = 30_000 const DEFAULT_TIMEOUT = 1 * 60 * 1000 const MAX_TIMEOUT = 10 * 60 * 1000 @@ -153,10 +154,9 @@ export const BashTool = Tool.define("bash", { }, }) - const lines = output.split("\n") - if (lines.length > 1000) { - output = lines.slice(0, 1000).join("\n") - output += "\n\n(Ouput was truncated)" + if (output.length > MAX_OUTPUT_LENGTH) { + output = output.slice(0, MAX_OUTPUT_LENGTH) + output += "\n\n(Output was truncated due to length limit)" } return { From 81583cddbdd588fa3eb9e3e15ea70909ce1b4b93 Mon Sep 17 00:00:00 2001 From: spoons-and-mirrors <212802214+spoons-and-mirrors@users.noreply.github.com> Date: Tue, 12 Aug 2025 22:21:57 +0200 Subject: [PATCH 169/178] refactor(agent-modal): revamped UI/UX for the agent modal (#1838) Co-authored-by: Dax Raad Co-authored-by: Dax --- packages/tui/internal/app/app.go | 44 ++- packages/tui/internal/app/state.go | 43 +++ .../tui/internal/components/dialog/agents.go | 311 ++++++++++++++---- packages/tui/internal/tui/tui.go | 29 +- 4 files changed, 330 insertions(+), 97 deletions(-) diff --git a/packages/tui/internal/app/app.go b/packages/tui/internal/app/app.go index 47520257d..345726483 100644 --- a/packages/tui/internal/app/app.go +++ b/packages/tui/internal/app/app.go @@ -71,9 +71,11 @@ type ModelSelectedMsg struct { Provider opencode.Provider Model opencode.Model } + type AgentSelectedMsg struct { - Agent opencode.Agent + AgentName string } + type SessionClearedMsg struct{} type CompactSessionMsg struct{} type SendPrompt = Prompt @@ -272,6 +274,7 @@ func (a *App) cycleMode(forward bool) (*App, tea.Cmd) { } a.State.Agent = a.Agent().Name + a.State.UpdateAgentUsage(a.Agent().Name) return a, a.SaveState() } @@ -316,6 +319,45 @@ func (a *App) CycleRecentModel() (*App, tea.Cmd) { return a, toast.NewErrorToast("Recent model not found") } +func (a *App) SwitchToAgent(agentName string) (*App, tea.Cmd) { + // Find the agent index by name + for i, agent := range a.Agents { + if agent.Name == agentName { + a.AgentIndex = i + break + } + } + + // Set up model for the new agent + modelID := a.Agent().Model.ModelID + providerID := a.Agent().Model.ProviderID + if modelID == "" { + if model, ok := a.State.AgentModel[a.Agent().Name]; ok { + modelID = model.ModelID + providerID = model.ProviderID + } + } + + if modelID != "" { + for _, provider := range a.Providers { + if provider.ID == providerID { + a.Provider = &provider + for _, model := range provider.Models { + if model.ID == modelID { + a.Model = &model + break + } + } + break + } + } + } + + a.State.Agent = a.Agent().Name + a.State.UpdateAgentUsage(agentName) + return a, a.SaveState() +} + // findModelByFullID finds a model by its full ID in the format "provider/model" func findModelByFullID( providers []opencode.Provider, diff --git a/packages/tui/internal/app/state.go b/packages/tui/internal/app/state.go index 283cbd15e..0e4010bec 100644 --- a/packages/tui/internal/app/state.go +++ b/packages/tui/internal/app/state.go @@ -16,6 +16,11 @@ type ModelUsage struct { LastUsed time.Time `toml:"last_used"` } +type AgentUsage struct { + AgentName string `toml:"agent_name"` + LastUsed time.Time `toml:"last_used"` +} + type AgentModel struct { ProviderID string `toml:"provider_id"` ModelID string `toml:"model_id"` @@ -29,6 +34,7 @@ type State struct { Model string `toml:"model"` Agent string `toml:"agent"` RecentlyUsedModels []ModelUsage `toml:"recently_used_models"` + RecentlyUsedAgents []AgentUsage `toml:"recently_used_agents"` MessagesRight bool `toml:"messages_right"` SplitDiff bool `toml:"split_diff"` MessageHistory []Prompt `toml:"message_history"` @@ -42,6 +48,7 @@ func NewState() *State { Agent: "build", AgentModel: make(map[string]AgentModel), RecentlyUsedModels: make([]ModelUsage, 0), + RecentlyUsedAgents: make([]AgentUsage, 0), MessageHistory: make([]Prompt, 0), } } @@ -83,6 +90,42 @@ func (s *State) RemoveModelFromRecentlyUsed(providerID, modelID string) { } } +// UpdateAgentUsage updates the recently used agents list with the specified agent +func (s *State) UpdateAgentUsage(agentName string) { + now := time.Now() + + // Check if this agent is already in the list + for i, usage := range s.RecentlyUsedAgents { + if usage.AgentName == agentName { + s.RecentlyUsedAgents[i].LastUsed = now + usage := s.RecentlyUsedAgents[i] + copy(s.RecentlyUsedAgents[1:i+1], s.RecentlyUsedAgents[0:i]) + s.RecentlyUsedAgents[0] = usage + return + } + } + + newUsage := AgentUsage{ + AgentName: agentName, + LastUsed: now, + } + + // Prepend to slice and limit to last 20 entries + s.RecentlyUsedAgents = append([]AgentUsage{newUsage}, s.RecentlyUsedAgents...) + if len(s.RecentlyUsedAgents) > 20 { + s.RecentlyUsedAgents = s.RecentlyUsedAgents[:20] + } +} + +func (s *State) RemoveAgentFromRecentlyUsed(agentName string) { + for i, usage := range s.RecentlyUsedAgents { + if usage.AgentName == agentName { + s.RecentlyUsedAgents = append(s.RecentlyUsedAgents[:i], s.RecentlyUsedAgents[i+1:]...) + return + } + } +} + func (s *State) AddPromptToHistory(prompt Prompt) { s.MessageHistory = append([]Prompt{prompt}, s.MessageHistory...) if len(s.MessageHistory) > 50 { diff --git a/packages/tui/internal/components/dialog/agents.go b/packages/tui/internal/components/dialog/agents.go index 49e3e025b..615372427 100644 --- a/packages/tui/internal/components/dialog/agents.go +++ b/packages/tui/internal/components/dialog/agents.go @@ -1,8 +1,8 @@ package dialog import ( - "fmt" "sort" + "strings" "github.com/charmbracelet/bubbles/v2/key" tea "github.com/charmbracelet/bubbletea/v2" @@ -19,9 +19,10 @@ import ( const ( numVisibleAgents = 10 - minAgentDialogWidth = 54 - maxAgentDialogWidth = 108 - maxDescriptionLength = 80 + minAgentDialogWidth = 40 + maxAgentDialogWidth = 60 + maxDescriptionLength = 60 + maxRecentAgents = 5 ) // AgentDialog interface for the agent selection dialog @@ -31,7 +32,7 @@ type AgentDialog interface { type agentDialog struct { app *app.App - allAgents []opencode.Agent + allAgents []agentSelectItem width int height int modal *modal.Modal @@ -39,24 +40,31 @@ type agentDialog struct { dialogWidth int } -// agentItem is a custom list item for agent selections -type agentItem struct { - agent opencode.Agent +// agentSelectItem combines the visual improvements with code patterns +type agentSelectItem struct { + name string + displayName string + description string + mode string // "primary", "subagent", "all" + isCurrent bool + agentIndex int + agent opencode.Agent // Keep original agent for compatibility } -func (a agentItem) Render( +func (a agentSelectItem) Render( selected bool, width int, baseStyle styles.Style, ) string { t := theme.CurrentTheme() - itemStyle := baseStyle. Background(t.BackgroundPanel()). Foreground(t.Text()) if selected { - itemStyle = itemStyle.Foreground(t.Primary()) + // Use agent color for highlighting when selected (visual improvement) + agentColor := util.GetAgentColor(a.agentIndex) + itemStyle = itemStyle.Foreground(agentColor) } descStyle := baseStyle. @@ -66,25 +74,43 @@ func (a agentItem) Render( // Calculate available width (accounting for padding and margins) availableWidth := width - 2 // Account for left padding - agentName := a.agent.Name - description := a.agent.Description - if description == "" { - description = fmt.Sprintf("(%s)", a.agent.Mode) + agentName := a.displayName + + // For user agents and subagents, show description; for built-in, show mode + var displayText string + if a.description != "" && (a.mode == "all" || a.mode == "subagent") { + // User agent or subagent with description + displayText = a.description + } else { + // Built-in without description - show mode + switch a.mode { + case "primary": + displayText = "(built-in)" + case "all": + displayText = "(user)" + default: + displayText = "" + } } separator := " - " - // Calculate how much space we have for the description + // Calculate how much space we have for the description (visual improvement) nameAndSeparatorLength := len(agentName) + len(separator) descriptionMaxLength := availableWidth - nameAndSeparatorLength - // Truncate description if it's too long - if len(description) > descriptionMaxLength && descriptionMaxLength > 3 { - description = description[:descriptionMaxLength-3] + "..." + // Cap description length to the maximum allowed + if descriptionMaxLength > maxDescriptionLength { + descriptionMaxLength = maxDescriptionLength + } + + // Truncate description if it's too long (visual improvement) + if len(displayText) > descriptionMaxLength && descriptionMaxLength > 3 { + displayText = displayText[:descriptionMaxLength-3] + "..." } namePart := itemStyle.Render(agentName) - descPart := descStyle.Render(separator + description) + descPart := descStyle.Render(separator + displayText) combinedText := namePart + descPart return baseStyle. @@ -94,8 +120,7 @@ func (a agentItem) Render( Render(combinedText) } -func (a agentItem) Selectable() bool { - // All agents in the dialog are selectable (subagents are filtered out) +func (a agentSelectItem) Selectable() bool { return true } @@ -122,32 +147,43 @@ func (a *agentDialog) Init() tea.Cmd { func (a *agentDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { + case tea.WindowSizeMsg: + a.width = msg.Width + a.height = msg.Height + a.searchDialog.SetWidth(a.dialogWidth) + a.searchDialog.SetHeight(msg.Height) + case SearchSelectionMsg: // Handle selection from search dialog - if item, ok := msg.Item.(agentItem); ok { - return a, tea.Sequence( - util.CmdHandler(modal.CloseModalMsg{}), - util.CmdHandler( - app.AgentSelectedMsg{ - Agent: item.agent, - }), - ) + if item, ok := msg.Item.(agentSelectItem); ok { + if !item.isCurrent { + // Switch to selected agent (using their better pattern) + return a, tea.Sequence( + util.CmdHandler(modal.CloseModalMsg{}), + util.CmdHandler(app.AgentSelectedMsg{AgentName: item.name}), + ) + } } return a, util.CmdHandler(modal.CloseModalMsg{}) case SearchCancelledMsg: return a, util.CmdHandler(modal.CloseModalMsg{}) + case SearchRemoveItemMsg: + if item, ok := msg.Item.(agentSelectItem); ok { + if a.isAgentInRecentSection(item, msg.Index) { + a.app.State.RemoveAgentFromRecentlyUsed(item.name) + items := a.buildDisplayList(a.searchDialog.GetQuery()) + a.searchDialog.SetItems(items) + return a, a.app.SaveState() + } + } + return a, nil + case SearchQueryChangedMsg: // Update the list based on search query items := a.buildDisplayList(msg.Query) a.searchDialog.SetItems(items) return a, nil - - case tea.WindowSizeMsg: - a.width = msg.Width - a.height = msg.Height - a.searchDialog.SetWidth(a.dialogWidth) - a.searchDialog.SetHeight(msg.Height) } updatedDialog, cmd := a.searchDialog.Update(msg) @@ -155,20 +191,38 @@ func (a *agentDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return a, cmd } +func (a *agentDialog) SetSize(width, height int) { + a.width = width + a.height = height +} + func (a *agentDialog) View() string { return a.searchDialog.View() } -func (a *agentDialog) calculateOptimalWidth(agents []opencode.Agent) int { +func (a *agentDialog) calculateOptimalWidth(agents []agentSelectItem) int { maxWidth := minAgentDialogWidth for _, agent := range agents { - // Calculate the width needed for this item: "AgentName - Description" - itemWidth := len(agent.Name) - if agent.Description != "" { - itemWidth += len(agent.Description) + 3 // " - " + // Calculate the width needed for this item: "AgentName - Description" (visual improvement) + itemWidth := len(agent.displayName) + if agent.description != "" && (agent.mode == "all" || agent.mode == "subagent") { + // User agent or subagent - use description (capped to maxDescriptionLength) + descLength := len(agent.description) + if descLength > maxDescriptionLength { + descLength = maxDescriptionLength + } + itemWidth += descLength + 3 // " - " } else { - itemWidth += len(string(agent.Mode)) + 3 // " (mode)" + // Built-in without description - use mode + var modeText string + switch agent.mode { + case "primary": + modeText = "(built-in)" + case "all": + modeText = "(user)" + } + itemWidth += len(modeText) + 3 // " - " } if itemWidth > maxWidth { @@ -177,22 +231,34 @@ func (a *agentDialog) calculateOptimalWidth(agents []opencode.Agent) int { } maxWidth = min(maxWidth, maxAgentDialogWidth) - return maxWidth } func (a *agentDialog) setupAllAgents() { - // Get agents from the app, filtering out subagents - a.allAgents = []opencode.Agent{} - for _, agent := range a.app.Agents { - if agent.Mode != "subagent" { - a.allAgents = append(a.allAgents, agent) - } + currentAgentName := a.app.Agent().Name + + // Build agent items from app.Agents (no API call needed) - their pattern + a.allAgents = make([]agentSelectItem, 0, len(a.app.Agents)) + for i, agent := range a.app.Agents { + isCurrent := agent.Name == currentAgentName + + // Create display name (capitalize first letter) + displayName := strings.Title(agent.Name) + + a.allAgents = append(a.allAgents, agentSelectItem{ + name: agent.Name, + displayName: displayName, + description: agent.Description, // Keep for search but don't use in display + mode: string(agent.Mode), + isCurrent: isCurrent, + agentIndex: i, + agent: agent, // Keep original for compatibility + }) } a.sortAgents() - // Calculate optimal width based on all agents + // Calculate optimal width based on all agents (visual improvement) a.dialogWidth = a.calculateOptimalWidth(a.allAgents) // Ensure minimum width to prevent textinput issues @@ -201,6 +267,7 @@ func (a *agentDialog) setupAllAgents() { a.searchDialog = NewSearchDialog("Search agents...", numVisibleAgents) a.searchDialog.SetWidth(a.dialogWidth) + // Build initial display list (empty query shows grouped view) items := a.buildDisplayList("") a.searchDialog.SetItems(items) } @@ -210,42 +277,40 @@ func (a *agentDialog) sortAgents() { agentA := a.allAgents[i] agentB := a.allAgents[j] - // Current agent goes first - if agentA.Name == a.app.Agent().Name { + // Current agent goes first (your preference) + if agentA.name == a.app.Agent().Name { return true } - if agentB.Name == a.app.Agent().Name { + if agentB.name == a.app.Agent().Name { return false } // Alphabetical order for all other agents - return agentA.Name < agentB.Name + return agentA.name < agentB.name }) } +// buildDisplayList creates the list items based on search query func (a *agentDialog) buildDisplayList(query string) []list.Item { if query != "" { + // Search mode: use fuzzy matching return a.buildSearchResults(query) + } else { + // Grouped mode: show Recent agents section and alphabetical list (their pattern) + return a.buildGroupedResults() } - return a.buildGroupedResults() } +// buildSearchResults creates a flat list of search results using fuzzy matching func (a *agentDialog) buildSearchResults(query string) []list.Item { agentNames := []string{} - agentMap := make(map[string]opencode.Agent) + agentMap := make(map[string]agentSelectItem) for _, agent := range a.allAgents { - // Search by name - searchStr := agent.Name + // Search by name only + searchStr := agent.name agentNames = append(agentNames, searchStr) agentMap[searchStr] = agent - - // Search by description if available - if agent.Description != "" { - searchStr = fmt.Sprintf("%s %s", agent.Name, agent.Description) - agentNames = append(agentNames, searchStr) - agentMap[searchStr] = agent - } } matches := fuzzy.RankFindFold(query, agentNames) @@ -257,25 +322,74 @@ func (a *agentDialog) buildSearchResults(query string) []list.Item { for _, match := range matches { agent := agentMap[match.Target] // Create a unique key to avoid duplicates - key := agent.Name + key := agent.name if seenAgents[key] { continue } seenAgents[key] = true - items = append(items, agentItem{agent: agent}) + items = append(items, agent) } return items } +// buildGroupedResults creates a grouped list with Recent agents section and categorized agents func (a *agentDialog) buildGroupedResults() []list.Item { var items []list.Item - items = append(items, list.HeaderItem("Agents")) + // Add Recent section (their pattern) + recentAgents := a.getRecentAgents(maxRecentAgents) + if len(recentAgents) > 0 { + items = append(items, list.HeaderItem("Recent")) + for _, agent := range recentAgents { + items = append(items, agent) + } + } + + // Create map of recent agent names for filtering + recentAgentNames := make(map[string]bool) + for _, recent := range recentAgents { + recentAgentNames[recent.name] = true + } + + // Separate agents by type (excluding recent ones) + primaryAndUserAgents := make([]agentSelectItem, 0) + subAgents := make([]agentSelectItem, 0) - // Add all agents (subagents are already filtered out) for _, agent := range a.allAgents { - items = append(items, agentItem{agent: agent}) + if !recentAgentNames[agent.name] { + switch agent.mode { + case "subagent": + subAgents = append(subAgents, agent) + default: + // primary, all, and any other types go in main "Agents" section + primaryAndUserAgents = append(primaryAndUserAgents, agent) + } + } + } + + // Sort each category alphabetically + sort.Slice(primaryAndUserAgents, func(i, j int) bool { + return primaryAndUserAgents[i].name < primaryAndUserAgents[j].name + }) + sort.Slice(subAgents, func(i, j int) bool { + return subAgents[i].name < subAgents[j].name + }) + + // Add main agents section + if len(primaryAndUserAgents) > 0 { + items = append(items, list.HeaderItem("Agents")) + for _, agent := range primaryAndUserAgents { + items = append(items, agent) + } + } + + // Add subagents section + if len(subAgents) > 0 { + items = append(items, list.HeaderItem("Subagents")) + for _, agent := range subAgents { + items = append(items, agent) + } } return items @@ -285,10 +399,65 @@ func (a *agentDialog) Render(background string) string { return a.modal.Render(a.View(), background) } -func (s *agentDialog) Close() tea.Cmd { +func (a *agentDialog) Close() tea.Cmd { return nil } +// getRecentAgents returns the most recently used agents (their pattern) +func (a *agentDialog) getRecentAgents(limit int) []agentSelectItem { + var recentAgents []agentSelectItem + + // Get recent agents from app state + for _, usage := range a.app.State.RecentlyUsedAgents { + if len(recentAgents) >= limit { + break + } + + // Find the corresponding agent + for _, agent := range a.allAgents { + if agent.name == usage.AgentName { + recentAgents = append(recentAgents, agent) + break + } + } + } + + // If no recent agents, use the current agent + if len(recentAgents) == 0 { + currentAgentName := a.app.Agent().Name + for _, agent := range a.allAgents { + if agent.name == currentAgentName { + recentAgents = append(recentAgents, agent) + break + } + } + } + + return recentAgents +} + +func (a *agentDialog) isAgentInRecentSection(agent agentSelectItem, index int) bool { + // Only check if we're in grouped mode (no search query) + if a.searchDialog.GetQuery() != "" { + return false + } + + recentAgents := a.getRecentAgents(maxRecentAgents) + if len(recentAgents) == 0 { + return false + } + + // Index 0 is the "Recent" header, so recent agents are at indices 1 to len(recentAgents) + if index >= 1 && index <= len(recentAgents) { + if index-1 < len(recentAgents) { + recentAgent := recentAgents[index-1] + return recentAgent.name == agent.name + } + } + + return false +} + func NewAgentDialog(app *app.App) AgentDialog { dialog := &agentDialog{ app: app, diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 7895adeac..6b91cbfeb 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -599,31 +599,9 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { a.app.State.UpdateModelUsage(msg.Provider.ID, msg.Model.ID) cmds = append(cmds, a.app.SaveState()) case app.AgentSelectedMsg: - // Find the agent index - for i, agent := range a.app.Agents { - if agent.Name == msg.Agent.Name { - a.app.AgentIndex = i - break - } - } - a.app.State.Agent = msg.Agent.Name - - // Switch to the agent's preferred model if available - if model, ok := a.app.State.AgentModel[msg.Agent.Name]; ok { - for _, provider := range a.app.Providers { - if provider.ID == model.ProviderID { - a.app.Provider = &provider - for _, m := range provider.Models { - if m.ID == model.ModelID { - a.app.Model = &m - break - } - } - break - } - } - } - cmds = append(cmds, a.app.SaveState()) + updated, cmd := a.app.SwitchToAgent(msg.AgentName) + a.app = updated + cmds = append(cmds, cmd) case dialog.ThemeSelectedMsg: a.app.State.Theme = msg.ThemeName cmds = append(cmds, a.app.SaveState()) @@ -1171,6 +1149,7 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) { case commands.ModelListCommand: modelDialog := dialog.NewModelDialog(a.app) a.modal = modelDialog + case commands.AgentListCommand: agentDialog := dialog.NewAgentDialog(a.app) a.modal = agentDialog From 47c327641b2beec66fc3960136634d5490ba6bb3 Mon Sep 17 00:00:00 2001 From: spoons-and-mirrors <212802214+spoons-and-mirrors@users.noreply.github.com> Date: Tue, 12 Aug 2025 22:22:03 +0200 Subject: [PATCH 170/178] feat: add session rename functionality to TUI modal (#1821) Co-authored-by: opencode Co-authored-by: Dax Raad Co-authored-by: Dax --- packages/opencode/src/server/server.ts | 41 ++++ packages/sdk/go/session.go | 20 ++ packages/tui/internal/app/app.go | 11 + .../tui/internal/components/dialog/session.go | 195 ++++++++++++++---- packages/tui/internal/tui/tui.go | 5 + 5 files changed, 231 insertions(+), 41 deletions(-) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 60433248c..79be29d28 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -296,6 +296,47 @@ export namespace Server { return c.json(true) }, ) + .patch( + "/session/:id", + describeRoute({ + description: "Update session properties", + operationId: "session.update", + responses: { + 200: { + description: "Successfully updated session", + content: { + "application/json": { + schema: resolver(Session.Info), + }, + }, + }, + }, + }), + zValidator( + "param", + z.object({ + id: z.string(), + }), + ), + zValidator( + "json", + z.object({ + title: z.string().optional(), + }), + ), + async (c) => { + const sessionID = c.req.valid("param").id + const updates = c.req.valid("json") + + const updatedSession = await Session.update(sessionID, (session) => { + if (updates.title !== undefined) { + session.title = updates.title + } + }) + + return c.json(updatedSession) + }, + ) .post( "/session/:id/init", describeRoute({ diff --git a/packages/sdk/go/session.go b/packages/sdk/go/session.go index bb5cecf57..f377b2e4e 100644 --- a/packages/sdk/go/session.go +++ b/packages/sdk/go/session.go @@ -66,6 +66,18 @@ func (r *SessionService) Delete(ctx context.Context, id string, opts ...option.R return } +// Update session properties +func (r *SessionService) Update(ctx context.Context, id string, body SessionUpdateParams, opts ...option.RequestOption) (res *Session, err error) { + opts = append(r.Options[:], opts...) + if id == "" { + err = errors.New("missing required id parameter") + return + } + path := fmt.Sprintf("session/%s", id) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPatch, path, body, &res, opts...) + return +} + // Abort a session func (r *SessionService) Abort(ctx context.Context, id string, opts ...option.RequestOption) (res *bool, err error) { opts = append(r.Options[:], opts...) @@ -2356,3 +2368,11 @@ type SessionSummarizeParams struct { func (r SessionSummarizeParams) MarshalJSON() (data []byte, err error) { return apijson.MarshalRoot(r) } + +type SessionUpdateParams struct { + Title param.Field[string] `json:"title"` +} + +func (r SessionUpdateParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} diff --git a/packages/tui/internal/app/app.go b/packages/tui/internal/app/app.go index 345726483..48b7d67f9 100644 --- a/packages/tui/internal/app/app.go +++ b/packages/tui/internal/app/app.go @@ -760,6 +760,17 @@ func (a *App) DeleteSession(ctx context.Context, sessionID string) error { return nil } +func (a *App) UpdateSession(ctx context.Context, sessionID string, title string) error { + _, err := a.Client.Session.Update(ctx, sessionID, opencode.SessionUpdateParams{ + Title: opencode.F(title), + }) + if err != nil { + slog.Error("Failed to update session", "error", err) + return err + } + return nil +} + func (a *App) ListMessages(ctx context.Context, sessionId string) ([]Message, error) { response, err := a.Client.Session.Messages(ctx, sessionId) if err != nil { diff --git a/packages/tui/internal/components/dialog/session.go b/packages/tui/internal/components/dialog/session.go index daf7a142b..08ff22a22 100644 --- a/packages/tui/internal/components/dialog/session.go +++ b/packages/tui/internal/components/dialog/session.go @@ -6,6 +6,7 @@ import ( "slices" + "github.com/charmbracelet/bubbles/v2/textinput" tea "github.com/charmbracelet/bubbletea/v2" "github.com/muesli/reflow/truncate" "github.com/sst/opencode-sdk-go" @@ -110,6 +111,9 @@ type sessionDialog struct { list list.List[sessionItem] app *app.App deleteConfirmation int // -1 means no confirmation, >= 0 means confirming deletion of session at this index + renameMode bool + renameInput textinput.Model + renameIndex int // index of session being renamed } func (s *sessionDialog) Init() tea.Cmd { @@ -123,69 +127,128 @@ func (s *sessionDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) { s.height = msg.Height s.list.SetMaxWidth(layout.Current.Container.Width - 12) case tea.KeyPressMsg: - switch msg.String() { - case "enter": - if s.deleteConfirmation >= 0 { - s.deleteConfirmation = -1 + if s.renameMode { + switch msg.String() { + case "enter": + if _, idx := s.list.GetSelectedItem(); idx >= 0 && idx < len(s.sessions) && idx == s.renameIndex { + newTitle := s.renameInput.Value() + if strings.TrimSpace(newTitle) != "" { + sessionToUpdate := s.sessions[idx] + return s, tea.Sequence( + func() tea.Msg { + ctx := context.Background() + err := s.app.UpdateSession(ctx, sessionToUpdate.ID, newTitle) + if err != nil { + return toast.NewErrorToast("Failed to rename session: " + err.Error())() + } + s.sessions[idx].Title = newTitle + s.renameMode = false + s.modal.SetTitle("Switch Session") + s.updateListItems() + return toast.NewSuccessToast("Session renamed successfully")() + }, + ) + } + } + s.renameMode = false + s.modal.SetTitle("Switch Session") s.updateListItems() return s, nil + default: + var cmd tea.Cmd + s.renameInput, cmd = s.renameInput.Update(msg) + return s, cmd } - if _, idx := s.list.GetSelectedItem(); idx >= 0 && idx < len(s.sessions) { - selectedSession := s.sessions[idx] + } else { + switch msg.String() { + case "enter": + if s.deleteConfirmation >= 0 { + s.deleteConfirmation = -1 + s.updateListItems() + return s, nil + } + if _, idx := s.list.GetSelectedItem(); idx >= 0 && idx < len(s.sessions) { + selectedSession := s.sessions[idx] + return s, tea.Sequence( + util.CmdHandler(modal.CloseModalMsg{}), + util.CmdHandler(app.SessionSelectedMsg(&selectedSession)), + ) + } + case "n": return s, tea.Sequence( util.CmdHandler(modal.CloseModalMsg{}), - util.CmdHandler(app.SessionSelectedMsg(&selectedSession)), + util.CmdHandler(app.SessionClearedMsg{}), ) - } - case "n": - return s, tea.Sequence( - util.CmdHandler(modal.CloseModalMsg{}), - util.CmdHandler(app.SessionClearedMsg{}), - ) - case "x", "delete", "backspace": - if _, idx := s.list.GetSelectedItem(); idx >= 0 && idx < len(s.sessions) { - if s.deleteConfirmation == idx { - // Second press - actually delete the session - sessionToDelete := s.sessions[idx] - return s, tea.Sequence( - func() tea.Msg { - s.sessions = slices.Delete(s.sessions, idx, idx+1) - s.deleteConfirmation = -1 - s.updateListItems() - return nil - }, - s.deleteSession(sessionToDelete.ID), - ) - } else { - // First press - enter delete confirmation mode - s.deleteConfirmation = idx + case "r": + if _, idx := s.list.GetSelectedItem(); idx >= 0 && idx < len(s.sessions) { + s.renameMode = true + s.renameIndex = idx + s.setupRenameInput(s.sessions[idx].Title) + s.modal.SetTitle("Rename Session") + s.updateListItems() + return s, textinput.Blink + } + case "x", "delete", "backspace": + if _, idx := s.list.GetSelectedItem(); idx >= 0 && idx < len(s.sessions) { + if s.deleteConfirmation == idx { + // Second press - actually delete the session + sessionToDelete := s.sessions[idx] + return s, tea.Sequence( + func() tea.Msg { + s.sessions = slices.Delete(s.sessions, idx, idx+1) + s.deleteConfirmation = -1 + s.updateListItems() + return nil + }, + s.deleteSession(sessionToDelete.ID), + ) + } else { + // First press - enter delete confirmation mode + s.deleteConfirmation = idx + s.updateListItems() + return s, nil + } + } + case "esc": + if s.deleteConfirmation >= 0 { + s.deleteConfirmation = -1 s.updateListItems() return s, nil } } - case "esc": - if s.deleteConfirmation >= 0 { - s.deleteConfirmation = -1 - s.updateListItems() - return s, nil - } } } - var cmd tea.Cmd - listModel, cmd := s.list.Update(msg) - s.list = listModel.(list.List[sessionItem]) - return s, cmd + if !s.renameMode { + var cmd tea.Cmd + listModel, cmd := s.list.Update(msg) + s.list = listModel.(list.List[sessionItem]) + return s, cmd + } + return s, nil } func (s *sessionDialog) Render(background string) string { + if s.renameMode { + // Show rename input instead of list + t := theme.CurrentTheme() + renameView := s.renameInput.View() + + mutedStyle := styles.NewStyle().Foreground(t.TextMuted()).Background(t.BackgroundPanel()).Render + helpText := mutedStyle("Enter to confirm, Esc to cancel") + helpText = styles.NewStyle().PaddingLeft(1).PaddingTop(1).Render(helpText) + + content := strings.Join([]string{renameView, helpText}, "\n") + return s.modal.Render(content, background) + } + listView := s.list.View() t := theme.CurrentTheme() keyStyle := styles.NewStyle().Foreground(t.Text()).Background(t.BackgroundPanel()).Render mutedStyle := styles.NewStyle().Foreground(t.TextMuted()).Background(t.BackgroundPanel()).Render - leftHelp := keyStyle("n") + mutedStyle(" new session") + leftHelp := keyStyle("n") + mutedStyle(" new session") + " " + keyStyle("r") + mutedStyle(" rename") rightHelp := keyStyle("x/del") + mutedStyle(" delete session") bgColor := t.BackgroundPanel() @@ -203,6 +266,39 @@ func (s *sessionDialog) Render(background string) string { return s.modal.Render(content, background) } +func (s *sessionDialog) setupRenameInput(currentTitle string) { + t := theme.CurrentTheme() + bgColor := t.BackgroundPanel() + textColor := t.Text() + textMutedColor := t.TextMuted() + + s.renameInput = textinput.New() + s.renameInput.SetValue(currentTitle) + s.renameInput.Focus() + s.renameInput.CharLimit = 100 + s.renameInput.SetWidth(layout.Current.Container.Width - 20) + + s.renameInput.Styles.Blurred.Placeholder = styles.NewStyle(). + Foreground(textMutedColor). + Background(bgColor). + Lipgloss() + s.renameInput.Styles.Blurred.Text = styles.NewStyle(). + Foreground(textColor). + Background(bgColor). + Lipgloss() + s.renameInput.Styles.Focused.Placeholder = styles.NewStyle(). + Foreground(textMutedColor). + Background(bgColor). + Lipgloss() + s.renameInput.Styles.Focused.Text = styles.NewStyle(). + Foreground(textColor). + Background(bgColor). + Lipgloss() + s.renameInput.Styles.Focused.Prompt = styles.NewStyle(). + Background(bgColor). + Lipgloss() +} + func (s *sessionDialog) updateListItems() { _, currentIdx := s.list.GetSelectedItem() @@ -229,7 +325,22 @@ func (s *sessionDialog) deleteSession(sessionID string) tea.Cmd { } } +// ReopenSessionModalMsg is emitted when the session modal should be reopened +type ReopenSessionModalMsg struct{} + func (s *sessionDialog) Close() tea.Cmd { + if s.renameMode { + // If in rename mode, exit rename mode and return a command to reopen the modal + s.renameMode = false + s.modal.SetTitle("Switch Session") + s.updateListItems() + + // Return a command that will reopen the session modal + return func() tea.Msg { + return ReopenSessionModalMsg{} + } + } + // Normal close behavior return nil } @@ -272,6 +383,8 @@ func NewSessionDialog(app *app.App) SessionDialog { list: listComponent, app: app, deleteConfirmation: -1, + renameMode: false, + renameIndex: -1, modal: modal.New( modal.WithTitle("Switch Session"), modal.WithMaxWidth(layout.Current.Container.Width-8), diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 6b91cbfeb..0262f5077 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -357,6 +357,11 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } a.modal = nil return a, cmd + case dialog.ReopenSessionModalMsg: + // Reopen the session modal (used when exiting rename mode) + sessionDialog := dialog.NewSessionDialog(a.app) + a.modal = sessionDialog + return a, nil case commands.ExecuteCommandMsg: updated, cmd := a.executeCommand(commands.Command(msg)) return updated, cmd From 9fa3e0a0ec20ca2a2fa4eaff84c7533003e609e9 Mon Sep 17 00:00:00 2001 From: Tom Hackshaw <42124348+et0and@users.noreply.github.com> Date: Wed, 13 Aug 2025 08:29:41 +1200 Subject: [PATCH 171/178] chore: spelling in git committer file (#1869) --- .opencode/agent/git-committer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.opencode/agent/git-committer.md b/.opencode/agent/git-committer.md index 20f090147..49c3e3de1 100644 --- a/.opencode/agent/git-committer.md +++ b/.opencode/agent/git-committer.md @@ -1,5 +1,5 @@ --- -description: Use this agent when you are saked to commit and push code changes to a git repository. +description: Use this agent when you are asked to commit and push code changes to a git repository. mode: subagent --- From d1876e30311a563d09b13c8236cbb335abc65d8d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 17:29:10 -0400 Subject: [PATCH 172/178] ci: enable aur --- .github/workflows/publish.yml | 11 +++++------ packages/opencode/script/publish.ts | 2 -- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f8f2146a2..12b1d4afd 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -48,11 +48,10 @@ jobs: restore-keys: | ${{ runner.os }}-bun- - # Temporarily disabled AUR steps - # - name: Install makepkg - # run: | - # sudo apt-get update - # sudo apt-get install -y pacman-package-manager + - name: Install makepkg + run: | + sudo apt-get update + sudo apt-get install -y pacman-package-manager - name: Setup SSH for AUR run: | @@ -61,7 +60,7 @@ jobs: chmod 600 ~/.ssh/id_rsa git config --global user.email "opencode@sst.dev" git config --global user.name "opencode" - # ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts + ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts - name: Install dependencies run: bun install diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts index 3ce750d3c..b0520fb5a 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -97,7 +97,6 @@ if (!snapshot) { const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) - /* // AUR package const pkgbuild = [ "# Maintainer: dax", @@ -137,7 +136,6 @@ if (!snapshot) { await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${version}"` if (!dry) await $`cd ./dist/aur-${pkg} && git push` } - */ // Homebrew formula const homebrewFormula = [ From 5785ded6e2a8607d8eff9f94b2f70caebb2cfec6 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 17:37:15 -0400 Subject: [PATCH 173/178] add openai prompt cache key --- packages/opencode/src/global/index.ts | 2 +- packages/opencode/src/provider/transform.ts | 35 +++++++-------------- packages/opencode/src/session/index.ts | 4 +-- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/packages/opencode/src/global/index.ts b/packages/opencode/src/global/index.ts index 4cfaf03a5..b5379347b 100644 --- a/packages/opencode/src/global/index.ts +++ b/packages/opencode/src/global/index.ts @@ -28,7 +28,7 @@ await Promise.all([ fs.mkdir(Global.Path.bin, { recursive: true }), ]) -const CACHE_VERSION = "7" +const CACHE_VERSION = "8" const version = await Bun.file(path.join(Global.Path.cache, "version")) .text() diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index c0591a69f..8428c2477 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -83,32 +83,19 @@ export namespace ProviderTransform { return undefined } - export function options(providerID: string, modelID: string): Record | undefined { + export function options(providerID: string, modelID: string, sessionID: string): Record | undefined { + const result: Record = {} + + if (providerID === "openai") { + result["promptCacheKey"] = sessionID + } + if (modelID.includes("gpt-5")) { - if (providerID === "azure") { - return { - reasoningEffort: "minimal", - } - } - return { - reasoningEffort: "minimal", - textVerbosity: "low", - // reasoningSummary: "auto", - // include: ["reasoning.encrypted_content"], + result["reasoningEffort"] = "minimal" + if (providerID !== "azure") { + result["textVerbosity"] = "low" } } - // if (modelID.includes("claude")) { - // return { - // thinking: { - // type: "enabled", - // budgetTokens: 32000, - // }, - // } - // } - // if (_providerID === "bedrock") { - // return { - // reasoningConfig: { type: "enabled", budgetTokens: 32000 }, - // } - // } + return result } } diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 182e16170..85b067efc 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -674,7 +674,7 @@ export namespace Session { providerOptions: { [input.providerID]: { ...small.info.options, - ...ProviderTransform.options(input.providerID, small.info.id), + ...ProviderTransform.options(input.providerID, small.info.id, input.sessionID), }, }, messages: [ @@ -868,7 +868,7 @@ export namespace Session { : undefined, topP: agent.topP ?? ProviderTransform.topP(input.providerID, input.modelID), options: { - ...ProviderTransform.options(input.providerID, input.modelID), + ...ProviderTransform.options(input.providerID, input.modelID, input.sessionID), ...model.info.options, ...agent.options, }, From dedaa34dc1c36a2638eb8d25436f9507a3e19c77 Mon Sep 17 00:00:00 2001 From: spoons-and-mirrors <212802214+spoons-and-mirrors@users.noreply.github.com> Date: Tue, 12 Aug 2025 23:38:35 +0200 Subject: [PATCH 174/178] fix(TUI): unsurfacing subagent from agents modal (#1873) --- .../tui/internal/components/dialog/agents.go | 43 +++++++------------ 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/packages/tui/internal/components/dialog/agents.go b/packages/tui/internal/components/dialog/agents.go index 615372427..38a3d3599 100644 --- a/packages/tui/internal/components/dialog/agents.go +++ b/packages/tui/internal/components/dialog/agents.go @@ -240,6 +240,9 @@ func (a *agentDialog) setupAllAgents() { // Build agent items from app.Agents (no API call needed) - their pattern a.allAgents = make([]agentSelectItem, 0, len(a.app.Agents)) for i, agent := range a.app.Agents { + if agent.Mode == "subagent" { + continue // Skip subagents entirely + } isCurrent := agent.Name == currentAgentName // Create display name (capitalize first letter) @@ -307,7 +310,10 @@ func (a *agentDialog) buildSearchResults(query string) []list.Item { agentMap := make(map[string]agentSelectItem) for _, agent := range a.allAgents { - // Search by name only + // Only include non-subagents in search + if agent.mode == "subagent" { + continue + } searchStr := agent.name agentNames = append(agentNames, searchStr) agentMap[searchStr] = agent @@ -352,42 +358,23 @@ func (a *agentDialog) buildGroupedResults() []list.Item { recentAgentNames[recent.name] = true } - // Separate agents by type (excluding recent ones) - primaryAndUserAgents := make([]agentSelectItem, 0) - subAgents := make([]agentSelectItem, 0) - + // Only show non-subagents (primary/user) in the main section + mainAgents := make([]agentSelectItem, 0) for _, agent := range a.allAgents { if !recentAgentNames[agent.name] { - switch agent.mode { - case "subagent": - subAgents = append(subAgents, agent) - default: - // primary, all, and any other types go in main "Agents" section - primaryAndUserAgents = append(primaryAndUserAgents, agent) - } + mainAgents = append(mainAgents, agent) } } - // Sort each category alphabetically - sort.Slice(primaryAndUserAgents, func(i, j int) bool { - return primaryAndUserAgents[i].name < primaryAndUserAgents[j].name - }) - sort.Slice(subAgents, func(i, j int) bool { - return subAgents[i].name < subAgents[j].name + // Sort main agents alphabetically + sort.Slice(mainAgents, func(i, j int) bool { + return mainAgents[i].name < mainAgents[j].name }) // Add main agents section - if len(primaryAndUserAgents) > 0 { + if len(mainAgents) > 0 { items = append(items, list.HeaderItem("Agents")) - for _, agent := range primaryAndUserAgents { - items = append(items, agent) - } - } - - // Add subagents section - if len(subAgents) > 0 { - items = append(items, list.HeaderItem("Subagents")) - for _, agent := range subAgents { + for _, agent := range mainAgents { items = append(items, agent) } } From 17b07877e596a0ec79436f2595d60722804f5c54 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 17:44:27 -0400 Subject: [PATCH 175/178] ci: disable AUR packaging in publish workflow --- .github/workflows/publish.yml | 10 ++-- packages/opencode/script/publish.ts | 76 ++++++++++++++--------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 12b1d4afd..762be8de4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -48,10 +48,10 @@ jobs: restore-keys: | ${{ runner.os }}-bun- - - name: Install makepkg - run: | - sudo apt-get update - sudo apt-get install -y pacman-package-manager + # - name: Install makepkg + # run: | + # sudo apt-get update + # sudo apt-get install -y pacman-package-manager - name: Setup SSH for AUR run: | @@ -60,7 +60,7 @@ jobs: chmod 600 ~/.ssh/id_rsa git config --global user.email "opencode@sst.dev" git config --global user.name "opencode" - ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts + # ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts - name: Install dependencies run: bun install diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts index b0520fb5a..9ceeb387a 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -97,45 +97,45 @@ if (!snapshot) { const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) - // AUR package - const pkgbuild = [ - "# Maintainer: dax", - "# Maintainer: adam", - "", - "pkgname='${pkg}'", - `pkgver=${version.split("-")[0]}`, - "options=('!debug' '!strip')", - "pkgrel=1", - "pkgdesc='The AI coding agent built for the terminal.'", - "url='https://github.com/sst/opencode'", - "arch=('aarch64' 'x86_64')", - "license=('MIT')", - "provides=('opencode')", - "conflicts=('opencode')", - "depends=('fzf' 'ripgrep')", - "", - `source_aarch64=("\${pkgname}_\${pkgver}_aarch64.zip::https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-arm64.zip")`, - `sha256sums_aarch64=('${arm64Sha}')`, - "", - `source_x86_64=("\${pkgname}_\${pkgver}_x86_64.zip::https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-x64.zip")`, - `sha256sums_x86_64=('${x64Sha}')`, - "", - "package() {", - ' install -Dm755 ./opencode "${pkgdir}/usr/bin/opencode"', - "}", - "", - ].join("\n") + // // AUR package + // const pkgbuild = [ + // "# Maintainer: dax", + // "# Maintainer: adam", + // "", + // "pkgname='${pkg}'", + // `pkgver=${version.split("-")[0]}`, + // "options=('!debug' '!strip')", + // "pkgrel=1", + // "pkgdesc='The AI coding agent built for the terminal.'", + // "url='https://github.com/sst/opencode'", + // "arch=('aarch64' 'x86_64')", + // "license=('MIT')", + // "provides=('opencode')", + // "conflicts=('opencode')", + // "depends=('fzf' 'ripgrep')", + // "", + // `source_aarch64=("\${pkgname}_\${pkgver}_aarch64.zip::https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-arm64.zip")`, + // `sha256sums_aarch64=('${arm64Sha}')`, + // "", + // `source_x86_64=("\${pkgname}_\${pkgver}_x86_64.zip::https://github.com/sst/opencode/releases/download/v${version}/opencode-linux-x64.zip")`, + // `sha256sums_x86_64=('${x64Sha}')`, + // "", + // "package() {", + // ' install -Dm755 ./opencode "${pkgdir}/usr/bin/opencode"', + // "}", + // "", + // ].join("\n") - for (const pkg of ["opencode", "opencode-bin"]) { - await $`rm -rf ./dist/aur-${pkg}` - await $`git clone ssh://aur@aur.archlinux.org/${pkg}.git ./dist/aur-${pkg}` - await $`cd ./dist/aur-${pkg} && git checkout master` - await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild.replace("${pkg}", pkg)) - await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO` - await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO` - await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${version}"` - if (!dry) await $`cd ./dist/aur-${pkg} && git push` - } + // for (const pkg of ["opencode", "opencode-bin"]) { + // await $`rm -rf ./dist/aur-${pkg}` + // await $`git clone ssh://aur@aur.archlinux.org/${pkg}.git ./dist/aur-${pkg}` + // await $`cd ./dist/aur-${pkg} && git checkout master` + // await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild.replace("${pkg}", pkg)) + // await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO` + // await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO` + // await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${version}"` + // if (!dry) await $`cd ./dist/aur-${pkg} && git push` + // } // Homebrew formula const homebrewFormula = [ From 482239b8488ee138e76dcc71a38e341b733b4adf Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 17:47:04 -0400 Subject: [PATCH 176/178] ci: sync --- .github/workflows/publish.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 762be8de4..422c6c0c0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -52,7 +52,6 @@ jobs: # run: | # sudo apt-get update # sudo apt-get install -y pacman-package-manager - - name: Setup SSH for AUR run: | mkdir -p ~/.ssh @@ -60,7 +59,7 @@ jobs: chmod 600 ~/.ssh/id_rsa git config --global user.email "opencode@sst.dev" git config --global user.name "opencode" - # ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts + # ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts - name: Install dependencies run: bun install From 296cc41a074d660360867ddd66b4b6f26d1e7cc9 Mon Sep 17 00:00:00 2001 From: opencode Date: Tue, 12 Aug 2025 21:51:19 +0000 Subject: [PATCH 177/178] release: v0.4.40 --- cloud/core/package.json | 2 +- cloud/function/package.json | 2 +- cloud/web/package.json | 2 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/sdk/js/src/gen/sdk.gen.ts | 16 ++++++++++++++++ packages/sdk/js/src/gen/types.gen.ts | 20 ++++++++++++++++++++ packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 11 files changed, 45 insertions(+), 9 deletions(-) diff --git a/cloud/core/package.json b/cloud/core/package.json index 820fc9638..662d89d14 100644 --- a/cloud/core/package.json +++ b/cloud/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode/cloud-core", - "version": "0.4.37", + "version": "0.4.40", "private": true, "type": "module", "dependencies": { diff --git a/cloud/function/package.json b/cloud/function/package.json index 0840edc51..2f97811d8 100644 --- a/cloud/function/package.json +++ b/cloud/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-function", - "version": "0.4.37", + "version": "0.4.40", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/cloud/web/package.json b/cloud/web/package.json index 29984fded..6403f94ad 100644 --- a/cloud/web/package.json +++ b/cloud/web/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/cloud-web", - "version": "0.4.37", + "version": "0.4.40", "private": true, "description": "", "type": "module", diff --git a/packages/function/package.json b/packages/function/package.json index d04f68b74..81e3d7317 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode/function", - "version": "0.4.37", + "version": "0.4.40", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 8ca1ba14f..0650db7fd 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.4.37", + "version": "0.4.40", "name": "opencode", "type": "module", "private": true, diff --git a/packages/plugin/package.json b/packages/plugin/package.json index f302861b5..43315516a 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.4.37", + "version": "0.4.40", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 741434ffe..4c9691aef 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.4.37", + "version": "0.4.40", "type": "module", "scripts": { "typecheck": "tsc --noEmit" diff --git a/packages/sdk/js/src/gen/sdk.gen.ts b/packages/sdk/js/src/gen/sdk.gen.ts index 4c42bd547..4ec41808e 100644 --- a/packages/sdk/js/src/gen/sdk.gen.ts +++ b/packages/sdk/js/src/gen/sdk.gen.ts @@ -19,6 +19,8 @@ import type { SessionDeleteResponses, SessionGetData, SessionGetResponses, + SessionUpdateData, + SessionUpdateResponses, SessionInitData, SessionInitResponses, SessionAbortData, @@ -224,6 +226,20 @@ class Session extends _HeyApiClient { }) } + /** + * Update session properties + */ + public update(options: Options) { + return (options.client ?? this._client).patch({ + url: "/session/{id}", + ...options, + headers: { + "Content-Type": "application/json", + ...options.headers, + }, + }) + } + /** * Analyze the app and create an AGENTS.md file */ diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index ddf7ac08b..a1c3372e1 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1240,6 +1240,26 @@ export type SessionGetResponses = { export type SessionGetResponse = SessionGetResponses[keyof SessionGetResponses] +export type SessionUpdateData = { + body?: { + title?: string + } + path: { + id: string + } + query?: never + url: "/session/{id}" +} + +export type SessionUpdateResponses = { + /** + * Successfully updated session + */ + 200: Session +} + +export type SessionUpdateResponse = SessionUpdateResponses[keyof SessionUpdateResponses] + export type SessionInitData = { body?: { messageID: string diff --git a/packages/web/package.json b/packages/web/package.json index e5704e9a9..8b0e44ad2 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode/web", "type": "module", - "version": "0.4.37", + "version": "0.4.40", "scripts": { "dev": "astro dev", "dev:remote": "sst shell --stage=dev --target=Web astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 181761621..a3398a255 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.4.37", + "version": "0.4.40", "publisher": "sst-dev", "repository": { "type": "git", From aa5e39e744ecb736be91dee31b7be010759ac7e6 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Tue, 12 Aug 2025 18:43:24 -0400 Subject: [PATCH 178/178] fix unzip not found printing to tui --- packages/opencode/src/lsp/server.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index 6cfba6d9d..0a3954cf1 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -94,7 +94,7 @@ export namespace LSPServer { const zipPath = path.join(Global.Path.bin, "vscode-eslint.zip") await Bun.file(zipPath).write(response) - await $`unzip -o -q ${zipPath}`.cwd(Global.Path.bin).nothrow() + await $`unzip -o -q ${zipPath}`.quiet().cwd(Global.Path.bin).nothrow() await fs.rm(zipPath, { force: true }) const extractedPath = path.join(Global.Path.bin, "vscode-eslint-main") @@ -269,7 +269,7 @@ export namespace LSPServer { const zipPath = path.join(Global.Path.bin, "elixir-ls.zip") await Bun.file(zipPath).write(response) - await $`unzip -o -q ${zipPath}`.cwd(Global.Path.bin).nothrow() + await $`unzip -o -q ${zipPath}`.quiet().cwd(Global.Path.bin).nothrow() await fs.rm(zipPath, { force: true, @@ -371,7 +371,7 @@ export namespace LSPServer { await Bun.file(tempPath).write(downloadResponse) if (ext === "zip") { - await $`unzip -o -q ${tempPath}`.cwd(Global.Path.bin).nothrow() + await $`unzip -o -q ${tempPath}`.quiet().cwd(Global.Path.bin).nothrow() } else { await $`tar -xf ${tempPath}`.cwd(Global.Path.bin).nothrow() } @@ -490,7 +490,7 @@ export namespace LSPServer { const zipPath = path.join(Global.Path.bin, "clangd.zip") await Bun.file(zipPath).write(downloadResponse) - await $`unzip -o -q ${zipPath}`.cwd(Global.Path.bin).nothrow() + await $`unzip -o -q ${zipPath}`.quiet().cwd(Global.Path.bin).nothrow() await fs.rm(zipPath, { force: true }) const extractedDir = path.join(Global.Path.bin, assetName.replace(".zip", ""))
+ {/* Mobile top bar */} + + + {/* Backdrop for mobile sidebar - closes sidebar when clicked */} + {sidebarOpen() &&
setSidebarOpen(false)}>
} + + + + {/* Main Content */} +
{props.children}
+